Files
dolt/integration-tests/go-sql-server-driver/testdef.go
T
Maximilian Hoffman 08167dfd5d Redo import perf (#4689)
* import perf harness prog

* more test isolation

* rework common dep

* fix server driver

* delete old files, fix readme

* more readme fixes

* batching edits

* edits

* skip import tests by def

* GA starter

* rename

* fix arg name

* change input name again

* remove espaced character from input desc

* fix env definition

* fix env definition

* fix workflow_dispatch name

* CI test files

* remove os spec

* runs on

* different set variable

* defaults

* defaults

* remove local GMS

* edits

* run bench from go/ directory

* dropped def equals

* go run, not go build

* fix output command name

* configure dolt

* fix query file path

* fix query file path

* switch query and inputs

* fix format <<EOF

* reformat yaml

* debug output

* more debug output

* fix echo -> cat

* fix to json flag

* fix yaml spacing

* yaml spacing

* yaml spacing

* revert html spacing

* json format

* env var expansion

* echo not cat

* another json string printing error

* no echo

* log template

* no template string

* wrong parameter, template should be templateName

* remove console.log

* pass file, not string, to ses

* rely on preexisting template? email action interface

* fix yaml

* fix html print

* fix html header

* change ci script

* fix sql-server connection pass

* mysql no password, setup default db

* put password back

* missed port for default database

* expanded CI tests

* shorter test file

* extend ci tests

* change default query

* try to push to dolthub

* modify push to dolthub

* duplicate test names

* typo

* dolt-action version

* invalid param for dolt_action

* specify feature branch

* specify main branch dolt-action

* -b flag first

* dont need -q flag for batch EOF

* combine results and dolthub push

* missing curly brace

* no auth

* new creds

* new cred fun

* use the cred key

* try again

* log table

* move push out of batch

* more logging

* new vs old branch

* fix branch rule

* more test

* all tests

* smaller tests

* smaller tests

* double env: key

* fix yaml error

* more yaml errors

* more yaml errors

* [ga-format-pr] Run go/utils/repofmt/format_repo.sh and go/Godeps/update.sh

* linting fixes, remove other import nightly

* linting

* go vet

* licenses

* fix compile errorrs

* warn don't panic on init()

* [ga-format-pr] Run go/utils/repofmt/format_repo.sh and go/Godeps/update.sh

* no external package

* [ga-format-pr] Run go/utils/repofmt/format_repo.sh and go/Godeps/update.sh

* unused dep

* [ga-format-pr] Run go/utils/repofmt/format_repo.sh and go/Godeps/update.sh

* Revert perf scripts

* refactor to repo dispatch

Co-authored-by: max-hoffman <max-hoffman@users.noreply.github.com>
2022-11-10 12:56:43 -08:00

319 lines
7.4 KiB
Go

// Copyright 2022 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"bytes"
"context"
"os"
"testing"
"time"
"database/sql"
driver "github.com/dolthub/dolt/go/libraries/doltcore/dtestutils/sql_server_driver"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
yaml "gopkg.in/yaml.v3"
)
type TestDef struct {
Tests []Test `yaml:"tests"`
}
// Test is a single test to run. The Repos and MultiRepos will be created, and
// any Servers defined within them will be started. The interactions and
// assertions defined in Conns will be run.
type Test struct {
Name string `yaml:"name"`
Repos []driver.TestRepo `yaml:"repos"`
MultiRepos []driver.MultiRepo `yaml:"multi_repos"`
Conns []driver.Connection `yaml:"connections"`
// Skip the entire test with this reason.
Skip string `yaml:"skip"`
}
func ParseTestsFile(path string) (TestDef, error) {
contents, err := os.ReadFile(path)
if err != nil {
return TestDef{}, err
}
dec := yaml.NewDecoder(bytes.NewReader(contents))
dec.KnownFields(true)
var res TestDef
err = dec.Decode(&res)
return res, err
}
func MakeRepo(t *testing.T, rs driver.RepoStore, r driver.TestRepo) driver.Repo {
repo, err := rs.MakeRepo(r.Name)
require.NoError(t, err)
for _, f := range r.WithFiles {
require.NoError(t, f.WriteAtDir(repo.Dir))
}
for _, remote := range r.WithRemotes {
require.NoError(t, repo.CreateRemote(remote.Name, remote.URL))
}
return repo
}
func MakeServer(t *testing.T, dc driver.DoltCmdable, s *driver.Server) *driver.SqlServer {
if s == nil {
return nil
}
opts := []driver.SqlServerOpt{driver.WithArgs(s.Args...)}
if s.Port != 0 {
opts = append(opts, driver.WithPort(s.Port))
}
server, err := driver.StartSqlServer(dc, opts...)
require.NoError(t, err)
if len(s.ErrorMatches) > 0 {
err := server.ErrorStop()
require.Error(t, err)
output := string(server.Output.Bytes())
for _, a := range s.ErrorMatches {
require.Regexp(t, a, output)
}
return nil
} else {
t.Cleanup(func() {
// We use assert, not require here, since FailNow() in
// a Cleanup does not make sense.
err := server.GracefulStop()
if assert.NoError(t, err) {
output := string(server.Output.Bytes())
for _, a := range s.LogMatches {
assert.Regexp(t, a, output)
}
}
})
return server
}
}
func (test Test) Run(t *testing.T) {
if test.Skip != "" {
t.Skip(test.Skip)
}
u, err := driver.NewDoltUser()
require.NoError(t, err)
rs, err := u.MakeRepoStore()
require.NoError(t, err)
servers := make(map[string]*driver.SqlServer)
for _, r := range test.Repos {
repo := MakeRepo(t, rs, r)
server := MakeServer(t, repo, r.Server)
if server != nil {
server.DBName = r.Name
servers[r.Name] = server
}
}
for _, mr := range test.MultiRepos {
// Each MultiRepo gets its own dolt config --global.
u, err := driver.NewDoltUser()
require.NoError(t, err)
rs, err = u.MakeRepoStore()
require.NoError(t, err)
for _, r := range mr.Repos {
MakeRepo(t, rs, r)
}
for _, f := range mr.WithFiles {
require.NoError(t, f.WriteAtDir(rs.Dir))
}
server := MakeServer(t, rs, mr.Server)
if server != nil {
servers[mr.Name] = server
}
}
for i, c := range test.Conns {
server := servers[c.On]
require.NotNilf(t, server, "error in test spec: could not find server %s for connection %d", c.On, i)
if c.RetryAttempts > 1 {
RetryTestRun(t, c.RetryAttempts, func(t require.TestingT) {
db, err := server.DB(c)
require.NoError(t, err)
defer db.Close()
conn, err := db.Conn(context.Background())
require.NoError(t, err)
defer conn.Close()
for _, q := range c.Queries {
RunQueryAttempt(t, conn, q)
}
})
} else {
func() {
db, err := server.DB(c)
require.NoError(t, err)
defer db.Close()
conn, err := db.Conn(context.Background())
require.NoError(t, err)
defer conn.Close()
for _, q := range c.Queries {
RunQuery(t, conn, q)
}
}()
}
if c.RestartServer != nil {
err := server.Restart(c.RestartServer.Args)
require.NoError(t, err)
}
}
}
func RunTestsFile(t *testing.T, path string) {
def, err := ParseTestsFile(path)
require.NoError(t, err)
for _, test := range def.Tests {
t.Run(test.Name, test.Run)
}
}
type retryTestingT struct {
*testing.T
errorfStrings []string
errorfArgs [][]interface{}
failNow bool
}
func (r *retryTestingT) Errorf(format string, args ...interface{}) {
r.T.Helper()
r.errorfStrings = append(r.errorfStrings, format)
r.errorfArgs = append(r.errorfArgs, args)
}
func (r *retryTestingT) FailNow() {
r.T.Helper()
r.failNow = true
panic(r)
}
func (r *retryTestingT) try(attempts int, test func(require.TestingT)) {
for i := 0; i < attempts; i++ {
r.errorfStrings = nil
r.errorfArgs = nil
r.failNow = false
if i != 0 {
time.Sleep(driver.RetrySleepDuration)
}
func() {
defer func() {
if r := recover(); r != nil {
if _, ok := r.(*retryTestingT); ok {
} else {
panic(r)
}
}
}()
test(r)
}()
if !r.failNow && len(r.errorfStrings) == 0 {
return
}
}
for i := range r.errorfStrings {
r.T.Errorf(r.errorfStrings[i], r.errorfArgs[i]...)
}
if r.failNow {
r.T.FailNow()
}
}
func RetryTestRun(t *testing.T, attempts int, test func(require.TestingT)) {
if attempts == 0 {
attempts = 1
}
rtt := &retryTestingT{T: t}
rtt.try(attempts, test)
}
func RunQuery(t *testing.T, conn *sql.Conn, q driver.Query) {
RetryTestRun(t, q.RetryAttempts, func(t require.TestingT) {
RunQueryAttempt(t, conn, q)
})
}
func RunQueryAttempt(t require.TestingT, conn *sql.Conn, q driver.Query) {
args := make([]any, len(q.Args))
for i := range q.Args {
args[i] = q.Args[i]
}
if q.Query != "" {
rows, err := conn.QueryContext(context.Background(), q.Query, args...)
if err == nil {
defer rows.Close()
}
if q.ErrorMatch != "" {
require.Error(t, err)
require.Regexp(t, q.ErrorMatch, err.Error())
return
}
require.NoError(t, err)
cols, err := rows.Columns()
require.NoError(t, err)
require.Equal(t, q.Result.Columns, cols)
rowstrings, err := RowsToStrings(len(cols), rows)
require.NoError(t, err)
if q.Result.Rows.Or != nil {
require.Contains(t, *q.Result.Rows.Or, rowstrings)
}
} else if q.Exec != "" {
_, err := conn.ExecContext(context.Background(), q.Exec, args...)
if q.ErrorMatch == "" {
require.NoError(t, err)
} else {
require.Error(t, err)
require.Regexp(t, q.ErrorMatch, err.Error())
}
}
}
func RowsToStrings(cols int, rows *sql.Rows) ([][]string, error) {
ret := make([][]string, 0)
for rows.Next() {
scanned := make([]any, cols)
for j := range scanned {
scanned[j] = new(sql.NullString)
}
err := rows.Scan(scanned...)
if err != nil {
return nil, err
}
printed := make([]string, cols)
for j := range scanned {
s := scanned[j].(*sql.NullString)
if !s.Valid {
printed[j] = "NULL"
} else {
printed[j] = s.String
}
}
ret = append(ret, printed)
}
return ret, rows.Err()
}