Skip to content

Commit

Permalink
Test MySQL/MariaDB set session vars
Browse files Browse the repository at this point in the history
  • Loading branch information
yhabteab committed Jul 10, 2024
1 parent b9bf0d2 commit d0abbb0
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 0 deletions.
71 changes: 71 additions & 0 deletions .github/workflows/sql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: SQL

on:
push:
branches:
- main
pull_request: {}

jobs:
mysql:
name: ${{ matrix.database.name }}
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
database:
- {name: MySQL 5.7, image: "mysql:5.7"}
- {name: MySQL 8.0, image: "mysql:8.0"}
- {name: MySQL latest, image: "mysql:latest"}
- {name: MariaDB 10.1, image: "mariadb:10.1"}
- {name: MariaDB 10.2, image: "mariadb:10.2"}
- {name: MariaDB 10.3, image: "mariadb:10.3"}
- {name: MariaDB 10.4, image: "mariadb:10.4"}
- {name: MariaDB 10.5, image: "mariadb:10.5"}
- {name: MariaDB 10.6, image: "mariadb:10.6"}
- {name: MariaDB 10.7, image: "mariadb:10.7"}
- {name: MariaDB 10.11, image: "mariadb:10.11"}
- {name: MariaDB 11.0, image: "mariadb:11.0", compat: 'false'}
- {name: MariaDB latest, image: "mariadb:latest", compat: 'false'}

env:
ICINGAGOLIBRARY_TESTS_DB_TYPE: mysql
ICINGAGOLIBRARY_TESTS_DB: icinga_unittest
ICINGAGOLIBRARY_TESTS_DB_USER: root
ICINGAGOLIBRARY_TESTS_DB_PASSWORD: password
ICINGAGOLIBRARY_TESTS_DB_HOST: 127.0.0.1
ICINGAGOLIBRARY_TESTS_DB_PORT: 3306

services:
mysql:
image: ${{ matrix.database.image }}
env:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: icinga_unittest
MARIADB_ROOT_PASSWORD: password # mariadb >= 11
MARIADB_DATABASE: icinga_unittest # mariadb >= 11
# Wait for the containers to become ready
options: >-
--health-cmd ${{ matrix.database.compat != 'false' && '"mysqladmin ping"' || '"healthcheck.sh --connect --innodb_initialized"' }}
--health-interval 10s
--health-timeout 5s
--health-retries 10
ports:
- 3306:3306

steps:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: stable

- name: Checkout code
uses: actions/checkout@v4

- name: Download dependencies
run: go get -v -t -d ./...

- name: Run tests
timeout-minutes: 10
run: go test -v -timeout 5m ./...
60 changes: 60 additions & 0 deletions database/set_mysql_session_vars_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package database

import (
"context"
"database/sql/driver"
"github.com/go-sql-driver/mysql"
"github.com/stretchr/testify/assert"
"testing"
)

func TestSetMysqlSessionVars(t *testing.T) {
vars := map[string][]struct {
name string
value any
expect error
}{
"UnknownVariables": {
// MySQL single nodes do not recognise the "wsrep_sync_wait" system variable, but MariaDB does!
{name: "wsrep_sync_wait", value: int64(15)}, // MySQL unknown sys var | MariaDB succeeds
{name: "wsrep_sync_wait", value: int64(7)}, // MySQL unknown sys var | MariaDB succeeds
// Just some random unknown system variables :-)
{name: "Icinga", value: "Icinga"}, // unknown sys var
{name: "IcingaDB", value: "IcingaDB"}, // unknown sys var
},
"VariablesWithCorrectValue": { // Setting system variables known by MySQL/MariaDB to a valid value
{name: "autocommit", value: true},
{name: "binlog_format", value: "MIXED"},
{name: "completion_type", value: int64(1) /** CHAIN */},
{name: "default_storage_engine", value: "InnoDB"},
},
"VariablesWithInvalidValues": { // System variables set to an invalid value
{name: "autocommit", value: "NOT-TRUE", expect: &mysql.MySQLError{Number: 1231}},
{name: "binlog_format", value: "IcingaDB", expect: &mysql.MySQLError{Number: 1231}}, // Invalid val!
{name: "completion_type", value: int64(-10), expect: &mysql.MySQLError{Number: 1231}}, // Min valid val 0
{name: "default_storage_engine", value: "IcingaDB", expect: &mysql.MySQLError{Number: 1286}}, // Unknown storage Engine!
},
}

ctx := context.Background()
db := GetTestDB(ctx, t, "ICINGAGOLIBRARY")
if db.DriverName() == PostgreSQL {
t.Skipf("skipping set session vars test for %q driver", PostgreSQL)
}

for name, vs := range vars {
t.Run(name, func(t *testing.T) {
for _, v := range vs {
conn, err := db.DB.Conn(ctx)
assert.NoError(t, err, "connecting to MySQL/MariaDB database should not fail")

err = conn.Raw(func(conn any) error {
return setSessionVariableIfExists(ctx, conn.(driver.Conn), v.name, v.value)
})

assert.ErrorIsf(t, err, v.expect, "setting %q variable to '%v' returns unexpected result", v.name, v.value)
assert.NoError(t, conn.Close(), "closing MySQL/MariaDB connection should not fail")
}
})
}
}
57 changes: 57 additions & 0 deletions database/testutils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package database

import (
"context"
"github.com/creasty/defaults"
"github.com/icinga/icinga-go-library/logging"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
"os"
"strconv"
"strings"
"testing"
"time"
)

// GetTestDB retrieves the database config from env variables, opens a new database and returns it.
// The [envPrefix] argument defines the environment variables prefix to look for e.g. `NOTIFICATIONS`.
//
// The test suite will be skipped if no `envPrefix+"_NOTIFICATIONS_TESTS_DB_TYPE" environment variable is
// set, otherwise fails fatally when invalid configurations are specified.
func GetTestDB(ctx context.Context, t *testing.T, envPrefix string) *DB {
c := &Config{}
require.NoError(t, defaults.Set(c), "applying config default should not fail")

if v, ok := os.LookupEnv(envPrefix + "_TESTS_DB_TYPE"); ok {
c.Type = strings.ToLower(v)
} else {
t.Skipf("Environment %q not set, skipping test!", envPrefix+"_TESTS_DB_TYPE")
}

if v, ok := os.LookupEnv(envPrefix + "_TESTS_DB"); ok {
c.Database = v
}
if v, ok := os.LookupEnv(envPrefix + "_TESTS_DB_USER"); ok {
c.User = v
}
if v, ok := os.LookupEnv(envPrefix + "_TESTS_DB_PASSWORD"); ok {
c.Password = v
}
if v, ok := os.LookupEnv(envPrefix + "_TESTS_DB_HOST"); ok {
c.Host = v
}
if v, ok := os.LookupEnv(envPrefix + "_TESTS_DB_PORT"); ok {
port, err := strconv.Atoi(v)
require.NoError(t, err, "invalid port provided")

c.Port = port
}

require.NoError(t, c.Validate(), "database config validation should not fail")

db, err := NewDbFromConfig(c, logging.NewLogger(zaptest.NewLogger(t).Sugar(), time.Hour), RetryConnectorCallbacks{})
require.NoError(t, err, "connecting to database should not fail")
require.NoError(t, db.PingContext(ctx), "pinging the database should not fail")

return db
}

0 comments on commit d0abbb0

Please sign in to comment.