diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 97a18df1..210c656b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,11 +26,11 @@ jobs: with: go-version: ${{ matrix.go-version }} - - name: Set env vars + - name: Configure environment run: | echo "GOARCH=amd64" >> $GITHUB_ENV echo "DB_HOST=127.0.0.1" >> $GITHUB_ENV echo "UPPER_DB_LOG=ERROR" >> $GITHUB_ENV - - name: Run target + - name: Run tests run: make test diff --git a/.gitignore b/.gitignore index 29460701..3291a6ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ *.sw? *.db *.tmp -generated_*.go +.venv diff --git a/Makefile b/Makefile index eea84293..eb956ab0 100644 --- a/Makefile +++ b/Makefile @@ -1,43 +1,9 @@ -SHELL ?= bash -CPU_CORES ?= $(shell nproc) +UPPER_DB_LOG ?= ERROR -PARALLEL_FLAGS ?= --halt-on-error 2 --jobs=$(CPU_CORES) -v -u - -TEST_FLAGS ?= -v -failfast -race -timeout 20m - -UPPER_DB_LOG ?= WARN - -export TEST_FLAGS -export PARALLEL_FLAGS export UPPER_DB_LOG -test: go-test-internal test-adapters - -benchmark: go-benchmark-internal - -go-benchmark-%: - go test -v -benchtime=500ms -bench=. ./$*/... - -go-test-%: - go test -v ./$*/... - -test-adapters: \ - test-adapter-postgresql \ - test-adapter-cockroachdb \ - test-adapter-mysql \ - test-adapter-mssql \ - test-adapter-sqlite \ - test-adapter-ql \ - test-adapter-mongo - -test-adapter-%: - ($(MAKE) -C adapter/$* test-extended || exit 1) - -test-generic: - export TEST_FLAGS="-run TestGeneric"; \ - $(MAKE) test-adapters +bench: + go test -v -benchtime=500ms -bench=. ./internal/... -goimports: - for FILE in $$(find -name "*.go" | grep -v vendor); do \ - goimports -w $$FILE; \ - done +test: + $(MAKE) -C tests diff --git a/adapter/cockroachdb/Makefile b/adapter/cockroachdb/Makefile deleted file mode 100644 index a6ba1323..00000000 --- a/adapter/cockroachdb/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -SHELL ?= bash - -COCKROACHDB_VERSION ?= v23.2.6 -COCKROACHDB_SUPPORTED ?= $(COCKROACHDB_VERSION) v22.2.19 v21.2.17 -PROJECT ?= $(subst .,_,"upper_cockroachdb_$(COCKROACHDB_VERSION)") - -DB_HOST ?= 127.0.0.1 -DB_PORT ?= 26257 - -DB_NAME ?= upperio -DB_USERNAME ?= upperio_user - -TEST_FLAGS ?= -PARALLEL_FLAGS ?= --halt-on-error 2 --jobs 1 - -export COCKROACHDB_VERSION - -export DB_HOST -export DB_NAME -export DB_PASSWORD -export DB_PORT -export DB_USERNAME - -export TEST_FLAGS - -test: - go test -v -failfast -race -timeout 20m $(TEST_FLAGS) - -test-no-race: - go test -v -failfast $(TEST_FLAGS) - -server-up: server-down - docker compose -p $(PROJECT) up -d && \ - sleep 5 && \ - psql -Uroot -h$(DB_HOST) -p$(DB_PORT) postgres -c "\ - DROP USER IF EXISTS $(DB_USERNAME); \ - DROP DATABASE IF EXISTS $(DB_NAME); \ - CREATE DATABASE $(DB_NAME); \ - CREATE USER $(DB_USERNAME); \ - GRANT ALL ON DATABASE $(DB_NAME) TO $(DB_USERNAME); \ - " - -server-down: - docker compose -p $(PROJECT) down - -test-extended: - parallel $(PARALLEL_FLAGS) \ - "COCKROACHDB_VERSION={} DB_PORT=\$$(($(DB_PORT)+{#})) $(MAKE) server-up test server-down" ::: \ - $(COCKROACHDB_SUPPORTED) diff --git a/adapter/cockroachdb/docker-compose.yml b/adapter/cockroachdb/docker-compose.yml deleted file mode 100644 index d59e86ac..00000000 --- a/adapter/cockroachdb/docker-compose.yml +++ /dev/null @@ -1,9 +0,0 @@ -services: - server: - image: cockroachdb/cockroach:${COCKROACHDB_VERSION:-v2} - environment: - COCKROACHDB_USER: ${DB_USERNAME:-upperio_user} - COCKROACHDB_DB: ${DB_NAME:-upperio} - command: "start-single-node --insecure" - ports: - - '${DB_HOST:-127.0.0.1}:${DB_PORT:-26257}:26257' diff --git a/adapter/cockroachdb/generic_test.go b/adapter/cockroachdb/generic_test.go deleted file mode 100644 index 80296a0d..00000000 --- a/adapter/cockroachdb/generic_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2012-today The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package cockroachdb - -import ( - "testing" - - "github.com/stretchr/testify/suite" - "github.com/upper/db/v4/internal/testsuite" -) - -type GenericTests struct { - testsuite.GenericTestSuite -} - -func (s *GenericTests) SetupSuite() { - s.Helper = &Helper{} -} - -func TestGeneric(t *testing.T) { - suite.Run(t, &GenericTests{}) -} diff --git a/adapter/cockroachdb/record_test.go b/adapter/cockroachdb/record_test.go deleted file mode 100644 index a740b65c..00000000 --- a/adapter/cockroachdb/record_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2012-today The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package cockroachdb - -import ( - "testing" - - "github.com/stretchr/testify/suite" - "github.com/upper/db/v4/internal/testsuite" -) - -type RecordTests struct { - testsuite.RecordTestSuite -} - -func (s *RecordTests) SetupSuite() { - s.Helper = &Helper{} -} - -func TestRecord(t *testing.T) { - suite.Run(t, &RecordTests{}) -} diff --git a/adapter/cockroachdb/sql_test.go b/adapter/cockroachdb/sql_test.go deleted file mode 100644 index 6044821f..00000000 --- a/adapter/cockroachdb/sql_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2012-today The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package cockroachdb - -import ( - "testing" - - "github.com/stretchr/testify/suite" - "github.com/upper/db/v4/internal/testsuite" -) - -type SQLTests struct { - testsuite.SQLTestSuite -} - -func (s *SQLTests) SetupSuite() { - s.Helper = &Helper{} -} - -func TestSQL(t *testing.T) { - suite.Run(t, &SQLTests{}) -} diff --git a/adapter/mongo/Makefile b/adapter/mongo/Makefile deleted file mode 100644 index ef0eb8b2..00000000 --- a/adapter/mongo/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -SHELL ?= bash - -MONGO_VERSION ?= 8 -MONGO_SUPPORTED ?= $(MONGO_VERSION) 7 -PROJECT ?= $(subst .,_,"upper_mongo_$(MONGO_VERSION)") - -DB_HOST ?= 127.0.0.1 -DB_PORT ?= 27017 - -DB_NAME ?= admin -DB_USERNAME ?= upperio_user -DB_PASSWORD ?= upperio//s3cr37 - -TEST_FLAGS ?= -PARALLEL_FLAGS ?= --halt-on-error 2 --jobs 1 - -export MONGO_VERSION - -export DB_HOST -export DB_NAME -export DB_PASSWORD -export DB_PORT -export DB_USERNAME - -export TEST_FLAGS - -test: - go test -v -failfast -race -timeout 20m $(TEST_FLAGS) - -test-no-race: - go test -v -failfast $(TEST_FLAGS) - -server-up: server-down - docker compose -p $(PROJECT) up -d && \ - sleep 10 - -server-down: - docker compose -p $(PROJECT) down - -test-extended: - parallel $(PARALLEL_FLAGS) \ - "MONGO_VERSION={} DB_PORT=\$$((27017+{#})) $(MAKE) server-up test server-down" ::: \ - $(MONGO_SUPPORTED) diff --git a/adapter/mongo/collection.go b/adapter/mongo/collection.go index 19839c61..4a8a47b0 100644 --- a/adapter/mongo/collection.go +++ b/adapter/mongo/collection.go @@ -24,9 +24,7 @@ package mongo import ( "context" "fmt" - "reflect" "strings" - "sync" db "github.com/upper/db/v4" "github.com/upper/db/v4/internal/adapter" @@ -40,13 +38,6 @@ type Collection struct { collection *mongo.Collection } -var ( - // idCache should be a struct if we're going to cache more than just - // _id field here - idCache = make(map[reflect.Type]string) - idCacheMutex sync.RWMutex -) - // Find creates a result set with the given conditions. func (col *Collection) Find(terms ...interface{}) db.Result { fields := []string{"*"} diff --git a/adapter/mongo/connection.go b/adapter/mongo/connection.go index 6517e2aa..3b7848cd 100644 --- a/adapter/mongo/connection.go +++ b/adapter/mongo/connection.go @@ -97,7 +97,7 @@ func ParseURL(s string) (conn ConnectionURL, err error) { } if u, err = url.Parse(s); err != nil { - return conn, fmt.Errorf("invalid URL: %v", err) + return conn, fmt.Errorf("invalid URL: %w", err) } conn.Scheme = u.Scheme @@ -118,7 +118,7 @@ func ParseURL(s string) (conn ConnectionURL, err error) { var vv url.Values if vv, err = url.ParseQuery(u.RawQuery); err != nil { - return conn, fmt.Errorf("invalid query: %v", err) + return conn, fmt.Errorf("invalid query: %w", err) } for k := range vv { diff --git a/adapter/mongo/database.go b/adapter/mongo/database.go index ff5f5b96..0ffaf9ae 100644 --- a/adapter/mongo/database.go +++ b/adapter/mongo/database.go @@ -178,7 +178,7 @@ func (s *Source) open() error { } s.collections = map[string]*Collection{} - s.database = s.session.Database(settings.Database) + s.database = s.session.Database(s.connURL.(ConnectionURL).Database) // ping if err = s.Ping(); err != nil { @@ -191,7 +191,7 @@ func (s *Source) open() error { // Close terminates the current database session. func (s *Source) Close() error { if s.session != nil { - s.session.Disconnect(context.Background()) + return s.session.Disconnect(context.Background()) } return nil diff --git a/adapter/mongo/docker-compose.yml b/adapter/mongo/docker-compose.yml deleted file mode 100644 index 4b34e183..00000000 --- a/adapter/mongo/docker-compose.yml +++ /dev/null @@ -1,11 +0,0 @@ -services: - - server: - image: mongo:${MONGO_VERSION:-3} - environment: - MONGO_INITDB_ROOT_USERNAME: ${DB_USERNAME:-upperio_user} - MONGO_INITDB_ROOT_PASSWORD: ${DB_PASSWORD:-upperio//s3cr37} - MONGO_INITDB_DATABASE: ${DB_NAME:-upperio} - ports: - - '${BIND_HOST:-127.0.0.1}:${DB_PORT:-27017}:27017' - diff --git a/adapter/mongo/generic_test.go b/adapter/mongo/generic_test.go deleted file mode 100644 index 20b68219..00000000 --- a/adapter/mongo/generic_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2012-today The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package mongo - -import ( - "testing" - - "github.com/stretchr/testify/suite" - "github.com/upper/db/v4/internal/testsuite" -) - -type GenericTests struct { - testsuite.GenericTestSuite -} - -func (s *GenericTests) SetupSuite() { - s.Helper = &Helper{} -} - -func TestGeneric(t *testing.T) { - suite.Run(t, &GenericTests{}) -} diff --git a/adapter/mongo/helper_test.go b/adapter/mongo/helper_test.go deleted file mode 100644 index 266893c2..00000000 --- a/adapter/mongo/helper_test.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) 2012-today The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package mongo - -import ( - "context" - "fmt" - "os" - - db "github.com/upper/db/v4" - - "github.com/upper/db/v4/internal/testsuite" - - "go.mongodb.org/mongo-driver/mongo" -) - -var settings = ConnectionURL{ - Database: os.Getenv("DB_NAME"), - User: os.Getenv("DB_USERNAME"), - Password: os.Getenv("DB_PASSWORD"), - Host: os.Getenv("DB_HOST") + ":" + os.Getenv("DB_PORT"), -} - -type Helper struct { - sess db.Session -} - -func (h *Helper) Session() db.Session { - return h.sess -} - -func (h *Helper) Adapter() string { - return "mongo" -} - -func (h *Helper) TearDown() error { - return h.sess.Close() -} - -func (h *Helper) SetUp() error { - ctx := context.Background() - - var err error - - h.sess, err = Open(settings) - if err != nil { - return err - } - - mgdb, ok := h.sess.Driver().(*mongo.Client) - if !ok { - panic("expecting *mongo.Client") - } - - var col *mongo.Collection - col = mgdb.Database(settings.Database).Collection("birthdays") - _ = col.Drop(ctx) - - col = mgdb.Database(settings.Database).Collection("fibonacci") - _ = col.Drop(ctx) - - col = mgdb.Database(settings.Database).Collection("is_even") - _ = col.Drop(ctx) - - col = mgdb.Database(settings.Database).Collection("CaSe_TesT") - _ = col.Drop(ctx) - - // Getting a pointer to the "artist" collection. - artist := h.sess.Collection("artist") - - _ = artist.Truncate() - for i := 0; i < 999; i++ { - _, err = artist.Insert(artistType{ - Name: fmt.Sprintf("artist-%d", i), - }) - if err != nil { - return fmt.Errorf("insert: %w", err) - } - } - - return nil -} - -var _ testsuite.Helper = &Helper{} diff --git a/adapter/mongo/mongo_test.go b/adapter/mongo/mongo_test.go deleted file mode 100644 index e64b9f18..00000000 --- a/adapter/mongo/mongo_test.go +++ /dev/null @@ -1,796 +0,0 @@ -// Copyright (c) 2012-present The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -// Tests for the mongodb adapter. -package mongo - -import ( - "fmt" - "log" - "math/rand" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/suite" - db "github.com/upper/db/v4" - "github.com/upper/db/v4/internal/testsuite" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/bson/primitive" -) - -type artistType struct { - ID primitive.ObjectID `bson:"_id,omitempty"` - Name string `bson:"name"` -} - -// Structure for testing conversions and datatypes. -type testValuesStruct struct { - Uint uint `bson:"_uint"` - Uint8 uint8 `bson:"_uint8"` - Uint16 uint16 `bson:"_uint16"` - Uint32 uint32 `bson:"_uint32"` - Uint64 uint64 `bson:"_uint64"` - - Int int `bson:"_int"` - Int8 int8 `bson:"_int8"` - Int16 int16 `bson:"_int16"` - Int32 int32 `bson:"_int32"` - Int64 int64 `bson:"_int64"` - - Float32 float32 `bson:"_float32"` - Float64 float64 `bson:"_float64"` - - Bool bool `bson:"_bool"` - String string `bson:"_string"` - - Date time.Time `bson:"_date"` - DateN *time.Time `bson:"_nildate"` - DateP *time.Time `bson:"_ptrdate"` - Time time.Duration `bson:"_time"` -} - -var testValues testValuesStruct - -func init() { - t := time.Date(2012, 7, 28, 1, 2, 3, 0, time.Local) - - testValues = testValuesStruct{ - 1, 1, 1, 1, 1, - -1, -1, -1, -1, -1, - 1.337, 1.337, - true, - "Hello world!", - t, - nil, - &t, - time.Second * time.Duration(7331), - } -} - -type AdapterTests struct { - testsuite.Suite -} - -func (s *AdapterTests) SetupSuite() { - s.Helper = &Helper{} -} - -func (s *AdapterTests) TestOpenWithWrongData() { - var err error - var rightSettings, wrongSettings ConnectionURL - - // Attempt to open with safe settings. - rightSettings = ConnectionURL{ - Database: settings.Database, - Host: settings.Host, - User: settings.User, - Password: settings.Password, - } - - // Attempt to open an empty database. - _, err = Open(rightSettings) - s.Require().NoError(err) - - // Attempt to open with wrong password. - wrongSettings = ConnectionURL{ - Database: settings.Database, - Host: settings.Host, - User: settings.User, - Password: "fail", - } - - _, err = Open(wrongSettings) - s.Error(err) - - // Attempt to open with wrong database. - wrongSettings = ConnectionURL{ - Database: "fail", - Host: settings.Host, - User: settings.User, - Password: settings.Password, - } - - _, err = Open(wrongSettings) - s.Error(err) - - // Attempt to open with wrong username. - wrongSettings = ConnectionURL{ - Database: settings.Database, - Host: settings.Host, - User: "fail", - Password: settings.Password, - } - - _, err = Open(wrongSettings) - s.Error(err) -} - -func (s *AdapterTests) TestTruncate() { - // Opening database. - sess, err := Open(settings) - s.Require().NoError(err) - - // We should close the database when it's no longer in use. - defer sess.Close() - - // Getting a list of all collections in this database. - collections, err := sess.Collections() - s.Require().NoError(err) - - for _, col := range collections { - // The collection may ot may not exists. - if ok, _ := col.Exists(); ok { - // Truncating the structure, if exists. - err = col.Truncate() - s.Require().NoError(err) - } - } -} - -func (s *AdapterTests) TestInsert() { - // Opening database. - sess, err := Open(settings) - s.Require().NoError(err) - - // We should close the database when it's no longer in use. - defer sess.Close() - - // Getting a pointer to the "artist" collection. - artist := sess.Collection("artist") - _ = artist.Truncate() - - // Inserting a map. - record, err := artist.Insert(map[string]string{ - "name": "Ozzie", - }) - s.Require().NoError(err) - - id := record.ID() - s.NotZero(record.ID()) - - _, ok := id.(primitive.ObjectID) - s.True(ok) - - _, err = primitive.ObjectIDFromHex(id.(primitive.ObjectID).Hex()) - s.Require().NoError(err) - - // Inserting a struct. - record, err = artist.Insert(struct { - Name string - }{ - "Flea", - }) - s.Require().NoError(err) - - id = record.ID() - s.NotZero(id) - - _, ok = id.(primitive.ObjectID) - s.True(ok) - - _, err = primitive.ObjectIDFromHex(id.(primitive.ObjectID).Hex()) - s.Require().NoError(err) - - // Inserting a struct (using tags to specify the field name). - record, err = artist.Insert(struct { - ArtistName string `bson:"name"` - }{ - "Slash", - }) - s.Require().NoError(err) - - id = record.ID() - s.NotNil(id) - - _, ok = id.(primitive.ObjectID) - - s.True(ok) - - _, err = primitive.ObjectIDFromHex(id.(primitive.ObjectID).Hex()) - s.Require().NoError(err) - - // Inserting a pointer to a struct - record, err = artist.Insert(&struct { - ArtistName string `bson:"name"` - }{ - "Metallica", - }) - s.Require().NoError(err) - - id = record.ID() - s.NotNil(id) - - _, ok = id.(primitive.ObjectID) - s.True(ok) - - _, err = primitive.ObjectIDFromHex(id.(primitive.ObjectID).Hex()) - s.Require().NoError(err) - - // Inserting a pointer to a map - record, err = artist.Insert(&map[string]string{ - "name": "Freddie", - }) - s.Require().NoError(err) - s.NotZero(id) - - _, ok = id.(primitive.ObjectID) - s.True(ok) - - id = record.ID() - s.NotNil(id) - - _, err = primitive.ObjectIDFromHex(id.(primitive.ObjectID).Hex()) - s.Require().NoError(err) - - // Counting elements, must be exactly 6 elements. - total, err := artist.Find().Count() - s.Require().NoError(err) - s.Equal(uint64(5), total) -} - -func (s *AdapterTests) TestGetNonExistentRow_Issue426() { - // Opening database. - sess, err := Open(settings) - s.Require().NoError(err) - - defer sess.Close() - - artist := sess.Collection("artist") - - var one artistType - err = artist.Find(db.Cond{"name": "nothing"}).One(&one) - - s.NotZero(err) - s.Equal(db.ErrNoMoreRows, err) - - var all []artistType - err = artist.Find(db.Cond{"name": "nothing"}).All(&all) - - s.Zero(len(all), "All should return an empty slice") - s.Equal(0, len(all)) -} - -func (s *AdapterTests) TestResultCount() { - var err error - var res db.Result - - // Opening database. - sess, err := Open(settings) - s.Require().NoError(err) - - defer sess.Close() - - // We should close the database when it's no longer in use. - artist := sess.Collection("artist") - - res = artist.Find() - - // Counting all the matching rows. - total, err := res.Count() - s.Require().NoError(err) - s.NotZero(total) -} - -func (s *AdapterTests) TestGroup() { - var stats db.Collection - - sess, err := Open(settings) - s.Require().NoError(err) - - type statsT struct { - Numeric int `db:"numeric" bson:"numeric"` - Value int `db:"value" bson:"value"` - } - - defer sess.Close() - - stats = sess.Collection("statsTest") - - // Truncating table. - _ = stats.Truncate() - - // Adding row append. - for i := 0; i < 1000; i++ { - numeric, value := rand.Intn(10), rand.Intn(100) - _, err = stats.Insert(statsT{numeric, value}) - s.Require().NoError(err) - } - - // db.statsTest.group({key: {numeric: true}, initial: {sum: 0}, reduce: function(doc, prev) { prev.sum += 1}}); - - // Testing GROUP BY - res := stats.Find().GroupBy(bson.M{ - "key": bson.M{"numeric": true}, - "initial": bson.M{"sum": 0}, - "reduce": `function(doc, prev) { prev.sum += 1}`, - }) - - var results []map[string]interface{} - - err = res.All(&results) - s.Equal(db.ErrUnsupported, err) -} - -func (s *AdapterTests) TestResultNonExistentCount() { - sess, err := Open(settings) - s.Require().NoError(err) - - defer sess.Close() - - total, err := sess.Collection("notartist").Find().Count() - s.Require().NoError(err) - s.Zero(total) -} - -func (s *AdapterTests) TestResultFetch() { - - // Opening database. - sess, err := Open(settings) - s.Require().NoError(err) - - // We should close the database when it's no longer in use. - defer sess.Close() - - artist := sess.Collection("artist") - - // Testing map - res := artist.Find() - - rowM := map[string]interface{}{} - - for res.Next(&rowM) { - s.NotZero(rowM["_id"]) - - _, ok := rowM["_id"].(primitive.ObjectID) - s.True(ok) - - _, err := primitive.ObjectIDFromHex(rowM["_id"].(primitive.ObjectID).Hex()) - s.Require().NoError(err) - - name, ok := rowM["name"].(string) - s.True(ok) - s.NotZero(name) - } - - err = res.Close() - s.Require().NoError(err) - - // Testing struct - rowS := struct { - ID primitive.ObjectID `bson:"_id"` - Name string `bson:"name"` - }{} - - res = artist.Find() - - for res.Next(&rowS) { - _, err := primitive.ObjectIDFromHex(rowS.ID.Hex()) - s.Require().NoError(err) - - s.NotZero(rowS.Name) - } - - err = res.Close() - s.Require().NoError(err) - - // Testing tagged struct - rowT := struct { - Value1 primitive.ObjectID `bson:"_id"` - Value2 string `bson:"name"` - }{} - - res = artist.Find() - - for res.Next(&rowT) { - _, err := primitive.ObjectIDFromHex(rowT.Value1.Hex()) - s.Require().NoError(err) - - s.NotZero(rowT.Value2) - } - - err = res.Close() - s.Require().NoError(err) - - // Testing Result.All() with a slice of maps. - res = artist.Find() - - allRowsM := []map[string]interface{}{} - err = res.All(&allRowsM) - s.Require().NoError(err) - - for _, singleRowM := range allRowsM { - s.NotZero(singleRowM["_id"]) - } - - // Testing Result.All() with a slice of structs. - res = artist.Find() - - allRowsS := []struct { - ID primitive.ObjectID `bson:"_id"` - Name string - }{} - err = res.All(&allRowsS) - s.Require().NoError(err) - - for _, singleRowS := range allRowsS { - _, err := primitive.ObjectIDFromHex(singleRowS.ID.Hex()) - s.Require().NoError(err) - } - - // Testing Result.All() with a slice of tagged structs. - res = artist.Find() - - allRowsT := []struct { - Value1 primitive.ObjectID `bson:"_id"` - Value2 string `bson:"name"` - }{} - err = res.All(&allRowsT) - s.Require().NoError(err) - - for _, singleRowT := range allRowsT { - _, err := primitive.ObjectIDFromHex(singleRowT.Value1.Hex()) - s.Require().NoError(err) - } -} - -func (s *AdapterTests) TestUpdate() { - // Opening database. - sess, err := Open(settings) - s.Require().NoError(err) - - // We should close the database when it's no longer in use. - defer sess.Close() - - // Getting a pointer to the "artist" collection. - artist := sess.Collection("artist") - - // Value - value := struct { - ID primitive.ObjectID `bson:"_id"` - Name string - }{} - - // Getting the first artist. - res := artist.Find(db.Cond{"_id": db.NotEq(nil)}).Limit(1) - - err = res.One(&value) - s.Require().NoError(err) - - // Updating with a map - rowM := map[string]interface{}{ - "name": strings.ToUpper(value.Name), - } - - err = res.Update(rowM) - s.Require().NoError(err) - - err = res.One(&value) - s.Require().NoError(err) - - s.Equal(value.Name, rowM["name"]) - - // Updating with a struct - rowS := struct { - Name string - }{strings.ToLower(value.Name)} - - err = res.Update(rowS) - s.Require().NoError(err) - - err = res.One(&value) - s.Require().NoError(err) - - s.Equal(value.Name, rowS.Name) - - // Updating with a tagged struct - rowT := struct { - Value1 string `bson:"name"` - }{strings.Replace(value.Name, "z", "Z", -1)} - - err = res.Update(rowT) - s.Require().NoError(err) - - err = res.One(&value) - s.Require().NoError(err) - - s.Equal(value.Name, rowT.Value1) -} - -func (s *AdapterTests) TestOperators() { - // Opening database. - sess, err := Open(settings) - s.Require().NoError(err) - - // We should close the database when it's no longer in use. - defer sess.Close() - - // Getting a pointer to the "artist" collection. - artist := sess.Collection("artist") - - rowS := struct { - ID uint64 - Name string - }{} - - res := artist.Find(db.Cond{"_id": db.NotIn(0, -1)}) - - err = res.One(&rowS) - s.Require().NoError(err) - - err = res.Close() - s.Require().NoError(err) -} - -func (s *AdapterTests) TestDelete() { - // Opening database. - sess, err := Open(settings) - s.Require().NoError(err) - - // We should close the database when it's no longer in use. - defer sess.Close() - - // Getting a pointer to the "artist" collection. - artist := sess.Collection("artist") - - // Getting the first artist. - res := artist.Find(db.Cond{"_id": db.NotEq(nil)}).Limit(1) - - var first struct { - ID primitive.ObjectID `bson:"_id"` - } - - err = res.One(&first) - s.Require().NoError(err) - - res = artist.Find(db.Cond{"_id": db.Eq(first.ID)}) - - // Trying to remove the row. - err = res.Delete() - s.Require().NoError(err) -} - -func (s *AdapterTests) TestDataTypes() { - // Opening database. - sess, err := Open(settings) - s.Require().NoError(err) - - // We should close the database when it's no longer in use. - defer sess.Close() - - // Getting a pointer to the "data_types" collection. - dataTypes := sess.Collection("data_types") - - // Inserting our test subject. - record, err := dataTypes.Insert(testValues) - s.Require().NoError(err) - - id := record.ID() - s.NotZero(id) - - // Trying to get the same subject we added. - res := dataTypes.Find(db.Cond{"_id": db.Eq(id)}) - - exists, err := res.Count() - s.Require().NoError(err) - s.NotZero(exists) - - // Trying to dump the subject into an empty structure of the same type. - var item testValuesStruct - err = res.One(&item) - s.Require().NoError(err) - - // Convert dates to local time for comparison. - testValues.Date = testValues.Date.Local() - item.Date = item.Date.Local() - *item.DateP = item.DateP.Local() - - // The original value and the test subject must match. - s.Equal(testValues, item) -} - -func (s *AdapterTests) TestPaginator() { - // Opening database. - sess, err := Open(settings) - s.Require().NoError(err) - - // We should close the database when it's no longer in use. - defer sess.Close() - - // Getting a pointer to the "artist" collection. - artist := sess.Collection("artist") - - err = artist.Truncate() - s.Require().NoError(err) - - for i := 0; i < 999; i++ { - _, err = artist.Insert(artistType{ - Name: fmt.Sprintf("artist-%d", i), - }) - s.Require().NoError(err) - } - - q := sess.Collection("artist").Find().Paginate(15) - paginator := q.Paginate(13) - - var zerothPage []artistType - err = paginator.Page(0).All(&zerothPage) - s.Require().NoError(err) - s.Equal(13, len(zerothPage)) - - var secondPage []artistType - err = paginator.Page(2).All(&secondPage) - s.Require().NoError(err) - s.Equal(13, len(secondPage)) - - tp, err := paginator.TotalPages() - s.Require().NoError(err) - s.NotZero(tp) - s.Equal(uint(77), tp) - - ti, err := paginator.TotalEntries() - s.Require().NoError(err) - s.NotZero(ti) - s.Equal(uint64(999), ti) - - var seventySixthPage []artistType - err = paginator.Page(76).All(&seventySixthPage) - s.Require().NoError(err) - s.Equal(11, len(seventySixthPage)) - - var seventySeventhPage []artistType - err = paginator.Page(77).All(&seventySeventhPage) - s.Require().NoError(err) - s.Equal(0, len(seventySeventhPage)) - - var hundredthPage []artistType - err = paginator.Page(100).All(&hundredthPage) - s.Require().NoError(err) - s.Equal(0, len(hundredthPage)) - - for i := uint(0); i < tp; i++ { - current := paginator.Page(i) - - var items []artistType - err := current.All(&items) - s.Require().NoError(err) - if len(items) < 1 { - break - } - for j := 0; j < len(items); j++ { - s.Equal(fmt.Sprintf("artist-%d", int64(13*int(i)+j)), items[j].Name) - } - } - - paginator = paginator.Cursor("_id") - { - current := paginator.Page(0) - for i := 0; ; i++ { - var items []artistType - err := current.All(&items) - s.Require().NoError(err) - - if len(items) < 1 { - break - } - - for j := 0; j < len(items); j++ { - s.Equal(fmt.Sprintf("artist-%d", int64(13*int(i)+j)), items[j].Name) - } - current = current.NextPage(items[len(items)-1].ID) - } - } - - { - log.Printf("Page 76") - current := paginator.Page(76) - for i := 76; ; i-- { - var items []artistType - - err := current.All(&items) - s.Require().NoError(err) - - if len(items) < 1 { - s.Equal(0, len(items)) - break - } - for j := 0; j < len(items); j++ { - s.Equal(fmt.Sprintf("artist-%d", 13*int(i)+j), items[j].Name) - } - - current = current.PrevPage(items[0].ID) - } - } - - { - resultPaginator := sess.Collection("artist").Find().Paginate(15) - - count, err := resultPaginator.TotalPages() - s.Equal(uint(67), count) - s.Require().NoError(err) - - var items []artistType - err = resultPaginator.Page(5).All(&items) - s.Require().NoError(err) - - for j := 0; j < len(items); j++ { - s.Equal(fmt.Sprintf("artist-%d", 15*5+j), items[j].Name) - } - - resultPaginator = resultPaginator.Cursor("_id").Page(0) - for i := 0; ; i++ { - var items []artistType - - err = resultPaginator.All(&items) - s.Require().NoError(err) - - if len(items) < 1 { - break - } - - for j := 0; j < len(items); j++ { - s.Equal(fmt.Sprintf("artist-%d", 15*i+j), items[j].Name) - } - resultPaginator = resultPaginator.NextPage(items[len(items)-1].ID) - } - - resultPaginator = resultPaginator.Cursor("_id").Page(66) - for i := 66; ; i-- { - var items []artistType - - err = resultPaginator.All(&items) - s.Require().NoError(err) - - if len(items) < 1 { - break - } - - for j := 0; j < len(items); j++ { - s.Equal(fmt.Sprintf("artist-%d", 15*i+j), items[j].Name) - } - resultPaginator = resultPaginator.PrevPage(items[0].ID) - } - } -} - -func TestAdapter(t *testing.T) { - suite.Run(t, &AdapterTests{}) -} diff --git a/adapter/mongo/result.go b/adapter/mongo/result.go index c1b8ba4a..2522acb8 100644 --- a/adapter/mongo/result.go +++ b/adapter/mongo/result.go @@ -491,7 +491,7 @@ func (r *resultQuery) query() (*mongo.Cursor, error) { } if r.cursorReverseOrder { - opts.SetSort(bson.D{{"_id", -1}}) + opts.SetSort(bson.D{{Key: "_id", Value: -1}}) } if len(selectedFields) > 0 { diff --git a/adapter/mssql/Makefile b/adapter/mssql/Makefile deleted file mode 100644 index e314d886..00000000 --- a/adapter/mssql/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -SHELL ?= bash - -MSSQL_VERSION ?= 2022-latest -MSSQL_SUPPORTED ?= $(MSSQL_VERSION) 2019-latest -PROJECT ?= $(subst .,_,"upper_mssql_$(MSSQL_VERSION)") - -DB_HOST ?= 127.0.0.1 -DB_PORT ?= 1433 - -DB_NAME ?= master -DB_USERNAME ?= sa -DB_PASSWORD ?= upperio//s3cr37 - -TEST_FLAGS ?= -PARALLEL_FLAGS ?= --halt-on-error 2 --jobs 1 - -GODEBUG ?= x509negativeserial=1 - -export MSSQL_VERSION - -export DB_HOST -export DB_NAME -export DB_PASSWORD -export DB_PORT -export DB_USERNAME - -export GODEBUG - -export TEST_FLAGS - -test: - go test -v -failfast -race -timeout 20m $(TEST_FLAGS) - -test-no-race: - go test -v -failfast $(TEST_FLAGS) - -server-up: server-down - docker compose -p $(PROJECT) up -d && \ - sleep 20 - -server-down: - docker compose -p $(PROJECT) down - -test-extended: - parallel $(PARALLEL_FLAGS) \ - "MSSQL_VERSION={} DB_PORT=\$$((1433+{#})) $(MAKE) server-up test server-down" ::: \ - $(MSSQL_SUPPORTED) diff --git a/adapter/mssql/docker-compose.yml b/adapter/mssql/docker-compose.yml deleted file mode 100644 index 9b18f8ff..00000000 --- a/adapter/mssql/docker-compose.yml +++ /dev/null @@ -1,12 +0,0 @@ -version: '3' - -services: - - server: - image: mcr.microsoft.com/mssql/server:${MSSQL_VERSION:-2017-latest-ubuntu} - environment: - ACCEPT_EULA: "Y" - SA_PASSWORD: ${DB_PASSWORD:-upperio//s3cr37} - ports: - - '${BIND_HOST:-127.0.0.1}:${DB_PORT:-1433}:1433' - diff --git a/adapter/mssql/generic_test.go b/adapter/mssql/generic_test.go deleted file mode 100644 index 65ddc4ab..00000000 --- a/adapter/mssql/generic_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2012-today The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package mssql - -import ( - "testing" - - "github.com/stretchr/testify/suite" - "github.com/upper/db/v4/internal/testsuite" -) - -type GenericTests struct { - testsuite.GenericTestSuite -} - -func (s *GenericTests) SetupSuite() { - s.Helper = &Helper{} -} - -func TestGeneric(t *testing.T) { - suite.Run(t, &GenericTests{}) -} diff --git a/adapter/mssql/sql_test.go b/adapter/mssql/sql_test.go deleted file mode 100644 index 74105088..00000000 --- a/adapter/mssql/sql_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2012-today The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package mssql - -import ( - "testing" - - "github.com/stretchr/testify/suite" - "github.com/upper/db/v4/internal/testsuite" -) - -type SQLTests struct { - testsuite.SQLTestSuite -} - -func (s *SQLTests) SetupSuite() { - s.Helper = &Helper{} -} - -func TestSQL(t *testing.T) { - suite.Run(t, &SQLTests{}) -} diff --git a/adapter/mysql/Makefile b/adapter/mysql/Makefile deleted file mode 100644 index dcad0e6a..00000000 --- a/adapter/mysql/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -SHELL ?= bash - -MYSQL_VERSION ?= 8.4 -MYSQL_SUPPORTED ?= $(MYSQL_VERSION) 5.7 -PROJECT ?= $(subst .,_,"upper_mysql_$(MYSQL_VERSION)") - -DB_HOST ?= 127.0.0.1 -DB_PORT ?= 3306 - -DB_NAME ?= upperio -DB_USERNAME ?= upperio_user -DB_PASSWORD ?= upperio//s3cr37 - -TEST_FLAGS ?= -PARALLEL_FLAGS ?= --halt-on-error 2 --jobs 1 - -export MYSQL_VERSION - -export DB_HOST -export DB_NAME -export DB_PASSWORD -export DB_PORT -export DB_USERNAME - -export TEST_FLAGS - -test: - go test -v -failfast -race -timeout 20m $(TEST_FLAGS) - -test-no-race: - go test -v -failfast $(TEST_FLAGS) - -server-up: server-down - docker compose -p $(PROJECT) up -d && \ - sleep 15 - -server-down: - docker compose -p $(PROJECT) down - -test-extended: - parallel $(PARALLEL_FLAGS) \ - "MYSQL_VERSION={} DB_PORT=\$$((3306+{#})) $(MAKE) server-up test server-down" ::: \ - $(MYSQL_SUPPORTED) diff --git a/adapter/mysql/docker-compose.yml b/adapter/mysql/docker-compose.yml deleted file mode 100644 index 8e2859bd..00000000 --- a/adapter/mysql/docker-compose.yml +++ /dev/null @@ -1,11 +0,0 @@ -services: - server: - image: mysql:${MYSQL_VERSION:-5} - environment: - MYSQL_USER: ${DB_USERNAME:-upperio_user} - MYSQL_PASSWORD: ${DB_PASSWORD:-upperio//s3cr37} - MYSQL_ALLOW_EMPTY_PASSWORD: 1 - MYSQL_DATABASE: ${DB_NAME:-upperio} - ports: - - '${DB_HOST:-127.0.0.1}:${DB_PORT:-3306}:3306' - diff --git a/adapter/mysql/generic_test.go b/adapter/mysql/generic_test.go deleted file mode 100644 index 7e671f79..00000000 --- a/adapter/mysql/generic_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2012-today The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package mysql - -import ( - "testing" - - "github.com/stretchr/testify/suite" - "github.com/upper/db/v4/internal/testsuite" -) - -type GenericTests struct { - testsuite.GenericTestSuite -} - -func (s *GenericTests) SetupSuite() { - s.Helper = &Helper{} -} - -func TestGeneric(t *testing.T) { - suite.Run(t, &GenericTests{}) -} diff --git a/adapter/mysql/record_test.go b/adapter/mysql/record_test.go deleted file mode 100644 index 7878ddd1..00000000 --- a/adapter/mysql/record_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2012-today The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package mysql - -import ( - "testing" - - "github.com/stretchr/testify/suite" - "github.com/upper/db/v4/internal/testsuite" -) - -type RecordTests struct { - testsuite.RecordTestSuite -} - -func (s *RecordTests) SetupSuite() { - s.Helper = &Helper{} -} - -func TestRecord(t *testing.T) { - suite.Run(t, &RecordTests{}) -} diff --git a/adapter/mysql/sql_test.go b/adapter/mysql/sql_test.go deleted file mode 100644 index e0a17572..00000000 --- a/adapter/mysql/sql_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2012-today The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package mysql - -import ( - "testing" - - "github.com/stretchr/testify/suite" - "github.com/upper/db/v4/internal/testsuite" -) - -type SQLTests struct { - testsuite.SQLTestSuite -} - -func (s *SQLTests) SetupSuite() { - s.Helper = &Helper{} -} - -func TestSQL(t *testing.T) { - suite.Run(t, &SQLTests{}) -} diff --git a/adapter/postgresql/Makefile b/adapter/postgresql/Makefile deleted file mode 100644 index a751cd4c..00000000 --- a/adapter/postgresql/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -SHELL ?= bash - -POSTGRES_VERSION ?= 16-alpine -POSTGRES_SUPPORTED ?= $(POSTGRES_VERSION) 15-alpine 14-alpine -PROJECT ?= $(subst .,_,"upper_postgres_$(POSTGRES_VERSION)") - -DB_HOST ?= 127.0.0.1 -DB_PORT ?= 5432 - -DB_NAME ?= upperio -DB_USERNAME ?= upperio_user -DB_PASSWORD ?= upperio//s3cr37 - -TEST_FLAGS ?= -v -failfast -PARALLEL_FLAGS ?= --halt-on-error 2 --jobs 1 - -export POSTGRES_VERSION - -export DB_HOST -export DB_NAME -export DB_PASSWORD -export DB_PORT -export DB_USERNAME - -export TEST_FLAGS - -test: - go test $(TEST_FLAGS) - -server-up: server-down - docker compose -p $(PROJECT) up -d && \ - sleep 10 - -server-down: - docker compose -p $(PROJECT) down - -test-extended: - parallel $(PARALLEL_FLAGS) \ - "POSTGRES_VERSION={} DB_PORT=\$$((5432+{#})) $(MAKE) server-up test server-down" ::: \ - $(POSTGRES_SUPPORTED) diff --git a/adapter/postgresql/docker-compose.yml b/adapter/postgresql/docker-compose.yml deleted file mode 100644 index 7f751f0a..00000000 --- a/adapter/postgresql/docker-compose.yml +++ /dev/null @@ -1,10 +0,0 @@ -services: - server: - image: postgres:${POSTGRES_VERSION:-11} - environment: - POSTGRES_USER: ${DB_USERNAME:-upperio_user} - POSTGRES_PASSWORD: ${DB_PASSWORD:-upperio//s3cr37} - POSTGRES_DB: ${DB_NAME:-upperio} - ports: - - '${DB_HOST:-127.0.0.1}:${DB_PORT:-5432}:5432' - diff --git a/adapter/postgresql/generic_test.go b/adapter/postgresql/generic_test.go deleted file mode 100644 index 8cd42a4f..00000000 --- a/adapter/postgresql/generic_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2012-today The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package postgresql - -import ( - "testing" - - "github.com/stretchr/testify/suite" - "github.com/upper/db/v4/internal/testsuite" -) - -type GenericTests struct { - testsuite.GenericTestSuite -} - -func (s *GenericTests) SetupSuite() { - s.Helper = &Helper{} -} - -func TestGeneric(t *testing.T) { - suite.Run(t, &GenericTests{}) -} diff --git a/adapter/postgresql/record_test.go b/adapter/postgresql/record_test.go deleted file mode 100644 index 8bdb0709..00000000 --- a/adapter/postgresql/record_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2012-today The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package postgresql - -import ( - "testing" - - "github.com/stretchr/testify/suite" - "github.com/upper/db/v4/internal/testsuite" -) - -type RecordTests struct { - testsuite.RecordTestSuite -} - -func (s *RecordTests) SetupSuite() { - s.Helper = &Helper{} -} - -func TestRecord(t *testing.T) { - suite.Run(t, &RecordTests{}) -} diff --git a/adapter/postgresql/sql_test.go b/adapter/postgresql/sql_test.go deleted file mode 100644 index dd0e5917..00000000 --- a/adapter/postgresql/sql_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2012-today The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package postgresql - -import ( - "testing" - - "github.com/stretchr/testify/suite" - "github.com/upper/db/v4/internal/testsuite" -) - -type SQLTests struct { - testsuite.SQLTestSuite -} - -func (s *SQLTests) SetupSuite() { - s.Helper = &Helper{} -} - -func TestSQL(t *testing.T) { - suite.Run(t, &SQLTests{}) -} diff --git a/adapter/ql/Makefile b/adapter/ql/Makefile deleted file mode 100644 index de6cf3fb..00000000 --- a/adapter/ql/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -SHELL ?= bash - -DB_NAME ?= memory://virtual.db -TEST_FLAGS ?= - -export DB_NAME - -build: - go build && go install - -# Temporarily disabled due to https://gitlab.com/cznic/ql/-/issues/225 -#test: -# go test -v -race -timeout 20m $(TEST_FLAGS) - -test: test-no-race - -test-no-race: - go test -v -failfast $(TEST_FLAGS) - -test-extended: test diff --git a/adapter/sqlite/Makefile b/adapter/sqlite/Makefile deleted file mode 100644 index 13bb4323..00000000 --- a/adapter/sqlite/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -SHELL ?= bash -DB_NAME ?= sqlite3-test.db -TEST_FLAGS ?= - -export DB_NAME - -export TEST_FLAGS - -build: - go build && go install - -require-client: - @if [ -z "$$(which sqlite3)" ]; then \ - echo 'Missing "sqlite3" command. Please install SQLite3 and try again.' && \ - exit 1; \ - fi - -reset-db: require-client - rm -f $(DB_NAME) - -test: reset-db - go test -v -failfast -race -timeout 20m $(TEST_FLAGS) - -test-no-race: - go test -v -failfast $(TEST_FLAGS) - -test-extended: test diff --git a/internal/testsuite/suite.go b/internal/testsuite/suite.go deleted file mode 100644 index 5fabac9b..00000000 --- a/internal/testsuite/suite.go +++ /dev/null @@ -1,37 +0,0 @@ -package testsuite - -import ( - "time" - - "github.com/stretchr/testify/suite" - db "github.com/upper/db/v4" -) - -const TimeZone = "Canada/Eastern" - -var defaultTimeLocation, _ = time.LoadLocation(TimeZone) - -type Helper interface { - Session() db.Session - - Adapter() string - - SetUp() error - TearDown() error -} - -type Suite struct { - suite.Suite - - Helper -} - -func (s *Suite) AfterTest(suiteName, testName string) { - err := s.TearDown() - s.NoError(err) -} - -func (s *Suite) BeforeTest(suiteName, testName string) { - err := s.SetUp() - s.NoError(err) -} diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 00000000..53454433 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,21 @@ +TEST_FLAGS ?= + +GODEBUG ?=x509negativeserial=1 + +export GODEBUG + +all: test-all + +venv: + $(MAKE) -C ansible venv + +test: venv + go test \ + -failfast \ + -timeout 30m \ + -race \ + -v \ + $(TEST_FLAGS) + +test-all: + TEST_FLAGS="-run ." $(MAKE) test diff --git a/tests/adapter_config_test.go b/tests/adapter_config_test.go new file mode 100644 index 00000000..b4ab2d84 --- /dev/null +++ b/tests/adapter_config_test.go @@ -0,0 +1,193 @@ +package db_test + +import ( + "fmt" + "log" + "math/rand" + "net" + "os" + "strconv" + "time" + + "github.com/stretchr/testify/suite" + "github.com/upper/db/v4" + + "github.com/upper/db/v4/adapter/cockroachdb" + "github.com/upper/db/v4/adapter/mongo" + "github.com/upper/db/v4/adapter/mssql" + "github.com/upper/db/v4/adapter/mysql" + "github.com/upper/db/v4/adapter/postgresql" + + cockroachdbtest "github.com/upper/db/v4/tests/cockroachdb" + mongotest "github.com/upper/db/v4/tests/mongo" + mssqltest "github.com/upper/db/v4/tests/mssql" + mysqltest "github.com/upper/db/v4/tests/mysql" + postgresqltest "github.com/upper/db/v4/tests/postgresql" +) + +const ( + defaultUsername = "upper_db_user" + defaultPassword = "upp3r//S3cr37" + defaultDatabase = "upper_db" + defaultHost = "127.0.0.1" + defaultTimeZone = "Canada/Eastern" +) + +const ( + minPort = 40000 + maxPort = 60000 +) + +const TimeZone = defaultTimeZone + +var defaultTimeLocation, _ = time.LoadLocation(TimeZone) + +type Helper interface { + Session() db.Session + + Adapter() string + + SetUp() error + TearDown() error +} + +type Suite struct { + suite.Suite + + Helper +} + +func (s *Suite) AfterTest(suiteName, testName string) { + err := s.TearDown() + s.Require().NoError(err) +} + +func (s *Suite) BeforeTest(suiteName, testName string) { + err := s.SetUp() + s.Require().NoError(err) +} + +func init() { + defaultMap := map[string]string{ + "DB_USERNAME": defaultUsername, + "DB_PASSWORD": defaultPassword, + "DB_NAME": defaultDatabase, + "DB_HOST": defaultHost, + "DB_TIMEZONE": defaultTimeZone, + } + + for k, v := range defaultMap { + if os.Getenv(k) == "" { + os.Setenv(k, v) + } + } +} + +func getRandomPort() int { + for i := 0; i < 5; i++ { + port := minPort + rand.Intn(maxPort-minPort) + + li, err := net.Listen("tcp", defaultHost+":"+strconv.Itoa(port)) + if err == nil { + li.Close() + return port + } + } + + log.Fatalf("could not find a free port") + + return 0 +} + +func newPostgreSQLTestHelper() func() (Helper, string, int) { + return func() (Helper, string, int) { + host, port := os.Getenv("DB_HOST"), getRandomPort() + + connURL := postgresql.ConnectionURL{ + Database: os.Getenv("DB_NAME"), + User: os.Getenv("DB_USERNAME"), + Password: os.Getenv("DB_PASSWORD"), + Host: host + ":" + fmt.Sprintf("%d", port), + Options: map[string]string{ + "timezone": os.Getenv("DB_TIMEZONE"), + }, + } + + helper := postgresqltest.NewHelper(connURL) + return helper, host, port + } +} + +func newMySQLTestHelper() func() (Helper, string, int) { + return func() (Helper, string, int) { + host, port := os.Getenv("DB_HOST"), getRandomPort() + + connURL := mysql.ConnectionURL{ + Database: os.Getenv("DB_NAME"), + User: os.Getenv("DB_USERNAME"), + Password: os.Getenv("DB_PASSWORD"), + Host: host + ":" + fmt.Sprintf("%d", port), + Options: map[string]string{ + "parseTime": "true", + "time_zone": fmt.Sprintf(`'%s'`, TimeZone), + "loc": TimeZone, + }, + } + + helper := mysqltest.NewHelper(connURL) + return helper, host, port + } +} + +func newCockroachDBTestHelper() func() (Helper, string, int) { + return func() (Helper, string, int) { + host, port := os.Getenv("DB_HOST"), getRandomPort() + + connURL := cockroachdb.ConnectionURL{ + Database: os.Getenv("DB_NAME"), + User: os.Getenv("DB_USERNAME"), + Password: os.Getenv("DB_PASSWORD"), + Host: host + ":" + fmt.Sprintf("%d", port), + Options: map[string]string{ + "timezone": os.Getenv("DB_TIMEZONE"), + }, + } + + helper := cockroachdbtest.NewHelper(connURL) + return helper, host, port + } +} + +func newMSSQLTestHelper() func() (Helper, string, int) { + return func() (Helper, string, int) { + host, port := os.Getenv("DB_HOST"), getRandomPort() + + connURL := mssql.ConnectionURL{ + Database: "master", + User: "sa", + Password: os.Getenv("DB_PASSWORD"), + Host: host + ":" + fmt.Sprintf("%d", port), + Options: map[string]string{}, + } + + helper := mssqltest.NewHelper(connURL) + return helper, host, port + } +} + +func newMongoDBTestHelper() func() (Helper, string, int) { + return func() (Helper, string, int) { + host, port := os.Getenv("DB_HOST"), getRandomPort() + + connURL := mongo.ConnectionURL{ + Database: "admin", + User: os.Getenv("DB_USERNAME"), + Password: os.Getenv("DB_PASSWORD"), + Host: host + ":" + fmt.Sprintf("%d", port), + Options: map[string]string{}, + } + + helper := mongotest.NewHelper(connURL) + return helper, host, port + } +} diff --git a/tests/ansible/Makefile b/tests/ansible/Makefile new file mode 100644 index 00000000..4073e270 --- /dev/null +++ b/tests/ansible/Makefile @@ -0,0 +1,31 @@ +VIRTUAL_ENV ?= $(shell pwd)/.venv +PATH := $(VIRTUAL_ENV)/bin:$(PATH) +PYTHON_BIN ?= $(VIRTUAL_ENV)/bin/python +TARGET ?= none +REQUIREMENTS_FILE ?= requirements.txt +ANSIBLE_CONFIG ?= $(shell pwd)/ansible.cfg + +export PATH + +.venv: + python3 -m venv $(VIRTUAL_ENV) + +venv: .venv pip-install + +pip-freeze: + $(PYTHON_BIN) -m pip freeze > $(REQUIREMENTS_FILE) + +pip-install: + $(PYTHON_BIN) -m pip install -r $(REQUIREMENTS_FILE) + +server-up: + ansible-playbook \ + -l "$(TARGET)" \ + --tags up \ + -v playbook.yml + +server-down: + ansible-playbook \ + -l "$(TARGET)" \ + --tags down \ + -v playbook.yml diff --git a/tests/ansible/ansible.cfg b/tests/ansible/ansible.cfg new file mode 100644 index 00000000..24403cf0 --- /dev/null +++ b/tests/ansible/ansible.cfg @@ -0,0 +1,4 @@ +[defaults] +inventory = ./inventory.yml +strategy = free +gather_facts = False diff --git a/tests/ansible/inventory.yml b/tests/ansible/inventory.yml new file mode 100644 index 00000000..b86f42b9 --- /dev/null +++ b/tests/ansible/inventory.yml @@ -0,0 +1,38 @@ +all: + vars: + ansible_connection: local + ansible_python_interpreter: ./.venv/bin/python + docker_public_registry: mirror.gcr.io/ + db_username: upper_db_user + db_password: upp3r//S3cr37 + db_name: upper_db + container_bind_ip: "127.0.0.1" + container_bind_port: "{{ lookup('env', 'CONTAINER_BIND_PORT') }}" + container_name_prefix: "upper_db_" + health_check_retries: 30 + health_check_delay: 5 + hosts: + postgresql-17: + postgresql_version: "17" + postgresql-16: + postgresql_version: "16" + postgresql-15: + postgresql_version: "15" + mysql-latest: + mysql_version: "latest" + mysql-lts: + mysql_version: "lts" + mysql-5: + mysql_version: "5" + cockroachdb-v23: + cockroachdb_version: "v23.2.6" + cockroachdb-v22: + cockroachdb_version: "v22.2.19" + mssql-2022: + mssql_version: "2022-latest" + mssql-2019: + mssql_version: "2019-latest" + mongodb-8: + mongodb_version: "8" + mongodb-7: + mongodb_version: "7" diff --git a/tests/ansible/playbook.yml b/tests/ansible/playbook.yml new file mode 100644 index 00000000..db60de2c --- /dev/null +++ b/tests/ansible/playbook.yml @@ -0,0 +1,15 @@ +- hosts: "postgresql-*" + roles: + - role: postgresql +- hosts: "mysql-*" + roles: + - role: mysql +- hosts: "cockroachdb-*" + roles: + - role: cockroachdb +- hosts: "mssql-*" + roles: + - role: mssql +- hosts: "mongodb-*" + roles: + - role: mongodb diff --git a/tests/ansible/requirements.txt b/tests/ansible/requirements.txt new file mode 100644 index 00000000..69adc92d --- /dev/null +++ b/tests/ansible/requirements.txt @@ -0,0 +1,20 @@ +ansible==11.3.0 +ansible-core==2.18.3 +certifi==2025.1.31 +cffi==1.17.1 +charset-normalizer==3.4.1 +cryptography==44.0.2 +dnspython==2.7.0 +idna==3.10 +Jinja2==3.1.6 +MarkupSafe==3.0.2 +packaging==24.2 +psycopg2-binary==2.9.10 +pycparser==2.22 +pymongo==4.11.3 +pymssql==2.3.2 +PyMySQL==1.1.1 +PyYAML==6.0.2 +requests==2.32.3 +resolvelib==1.0.1 +urllib3==2.3.0 diff --git a/tests/ansible/roles/cockroachdb/tasks/main.yml b/tests/ansible/roles/cockroachdb/tasks/main.yml new file mode 100644 index 00000000..ef7a41f3 --- /dev/null +++ b/tests/ansible/roles/cockroachdb/tasks/main.yml @@ -0,0 +1,47 @@ +- name: "Set task facts" + set_fact: + container_name: "{{ container_name_prefix }}cockroachdb_{{ cockroachdb_version }}" + container_image: "{{ docker_public_registry }}cockroachdb/cockroach:{{ cockroachdb_version }}" + container_bind_port: "{{ 26257 if container_bind_port == '' else container_bind_port }}" + tags: + - up + - down +- name: Escape container name + set_fact: + container_name: "{{ container_name | regex_replace('[^a-zA-Z0-9]', '_') }}" + tags: + - up + - down +- name: "Force-stop CockroachDB {{ cockroachdb_version }}" + docker_container: + name: "{{ container_name }}" + state: absent + tags: + - up + - down +- name: "Run CockroachDB {{ cockroachdb_version }}" + docker_container: + name: "{{ container_name }}" + image: "{{ container_image }}" + state: started + ports: + - "{{ container_bind_ip }}:{{ container_bind_port }}:26257" + env: + COCKROACH_USER: "{{ db_username }}" + COCKROACH_DATABASE: "{{ db_name }}" + command: "start-single-node --insecure" + tags: + - up +- name: "CockroachDB health check" + postgresql_query: + db: "{{ db_name }}" + login_host: "{{ container_bind_ip }}" + login_port: "{{ container_bind_port }}" + login_user: "{{ db_username }}" + query: "SELECT 1" + register: cockroachdb_health_check + retries: "{{ health_check_retries }}" + delay: "{{ health_check_delay }}" + until: cockroachdb_health_check is succeeded + tags: + - up diff --git a/tests/ansible/roles/mongodb/tasks/main.yml b/tests/ansible/roles/mongodb/tasks/main.yml new file mode 100644 index 00000000..1f3d066f --- /dev/null +++ b/tests/ansible/roles/mongodb/tasks/main.yml @@ -0,0 +1,49 @@ +- name: "Set task facts" + set_fact: + container_name: "{{ container_name_prefix }}mongo_{{ mongodb_version }}" + container_image: "{{ docker_public_registry }}mongo:{{ mongodb_version }}" + container_bind_port: "{{ 27017 if container_bind_port == '' else container_bind_port }}" + db_name: "admin" + tags: + - up + - down +- name: Escape container name + set_fact: + container_name: "{{ container_name | regex_replace('[^a-zA-Z0-9]', '_') }}" + tags: + - up + - down +- name: "Force-stop MongoDB {{ mongodb_version }}" + docker_container: + name: "{{ container_name }}" + state: absent + tags: + - up + - down +- name: "Run MongoDB {{ mongodb_version }}" + docker_container: + name: "{{ container_name }}" + image: "{{ container_image }}" + state: started + ports: + - "{{ container_bind_ip }}:{{ container_bind_port }}:27017" + env: + MONGO_INITDB_ROOT_USERNAME: "{{ db_username }}" + MONGO_INITDB_ROOT_PASSWORD: "{{ db_password }}" + MONGO_INITDB_DATABASE: "{{ db_name }}" + tags: + - up +- name: "MongoDB health check" + community.mongodb.mongodb_info: + login_user: "{{ db_username }}" + login_password: "{{ db_password }}" + login_host: "{{ container_bind_ip }}" + login_port: "{{ container_bind_port }}" + login_database: "{{ db_name }}" + filter: "databases" + register: mongodb_health_check + until: mongodb_health_check is not failed + retries: "{{ health_check_retries }}" + delay: "{{ health_check_delay }}" + tags: + - up diff --git a/tests/ansible/roles/mssql/tasks/main.yml b/tests/ansible/roles/mssql/tasks/main.yml new file mode 100644 index 00000000..d0705f85 --- /dev/null +++ b/tests/ansible/roles/mssql/tasks/main.yml @@ -0,0 +1,48 @@ +- name: "Set task facts" + set_fact: + container_name: "{{ container_name_prefix }}mssql_{{ mssql_version }}" + container_image: "mcr.microsoft.com/mssql/server:{{ mssql_version }}" + container_bind_port: "{{ 1433 if container_bind_port == '' else container_bind_port }}" + db_username: "sa" + db_name: "master" + tags: + - up + - down +- name: Escape container name + set_fact: + container_name: "{{ container_name | regex_replace('[^a-zA-Z0-9]', '_') }}" + tags: + - up + - down +- name: "Force-stop MSSQL {{ mssql_version }}" + docker_container: + name: "{{ container_name }}" + state: absent + tags: + - down +- name: "Run MSSQL {{ mssql_version }}" + docker_container: + name: "{{ container_name }}" + image: "{{ container_image }}" + state: started + ports: + - "{{ container_bind_ip }}:{{ container_bind_port }}:1433" + env: + ACCEPT_EULA: "Y" + SA_PASSWORD: "{{ db_password }}" + tags: + - up +- name: "MSSQL health check" + community.general.mssql_script: + login_host: "{{ container_bind_ip }}" + login_port: "{{ container_bind_port }}" + login_user: "{{ db_username }}" + login_password: "{{ db_password }}" + name: "{{ db_name }}" + script: "SELECT 1" + register: mssql_health_check + retries: "{{ health_check_retries }}" + delay: "{{ health_check_delay }}" + until: mssql_health_check is succeeded + tags: + - up diff --git a/tests/ansible/roles/mysql/tasks/main.yml b/tests/ansible/roles/mysql/tasks/main.yml new file mode 100644 index 00000000..4c41357a --- /dev/null +++ b/tests/ansible/roles/mysql/tasks/main.yml @@ -0,0 +1,51 @@ +- name: "Set task facts" + set_fact: + container_name: "{{ container_name_prefix }}mysql_{{ mysql_version }}" + container_image: "{{ docker_public_registry }}mysql:{{ mysql_version }}" + container_bind_port: "{{ 3306 if container_bind_port == '' else container_bind_port }}" + tags: + - up + - down +- name: Escape container name + set_fact: + container_name: "{{ container_name | regex_replace('[^a-zA-Z0-9]', '_') }}" + tags: + - up + - down +- name: "Force-stop MySQL {{ mysql_version }}" + docker_container: + name: "{{ container_name }}" + state: absent + tags: + - up + - down +- name: "Run MySQL {{ mysql_version }}" + docker_container: + name: "{{ container_name }}" + image: "{{ container_image }}" + state: started + ports: + - "{{ container_bind_ip }}:{{ container_bind_port }}:3306" + ulimits: + - nofile:262144:262144 + env: + MYSQL_RANDOM_ROOT_PASSWORD: "yes" + MYSQL_USER: "{{ db_username }}" + MYSQL_PASSWORD: "{{ db_password }}" + MYSQL_DATABASE: "{{ db_name }}" + tags: + - up +- name: "MySQL health check" + mysql_query: + login_host: "{{ container_bind_ip }}" + login_port: "{{ container_bind_port }}" + login_user: "{{ db_username }}" + login_password: "{{ db_password }}" + login_db: "{{ db_name }}" + query: "SELECT 1" + register: mysql_health_check + retries: "{{ health_check_retries }}" + delay: "{{ health_check_delay }}" + until: mysql_health_check is succeeded + tags: + - up diff --git a/tests/ansible/roles/postgresql/tasks/main.yml b/tests/ansible/roles/postgresql/tasks/main.yml new file mode 100644 index 00000000..92b1c419 --- /dev/null +++ b/tests/ansible/roles/postgresql/tasks/main.yml @@ -0,0 +1,47 @@ +- name: "Set task facts" + set_fact: + container_name: "{{ container_name_prefix }}postgres_{{ postgresql_version }}" + container_image: "{{ docker_public_registry }}postgres:{{ postgresql_version }}" + container_bind_port: "{{ 5432 if container_bind_port == '' else container_bind_port }}" + tags: + - up + - down +- name: Escape container name + set_fact: + container_name: "{{ container_name | regex_replace('[^a-zA-Z0-9]', '_') }}" + tags: + - up + - down +- name: "Force-stop PostgreSQL {{ postgresql_version }}" + docker_container: + name: "{{ container_name }}" + state: absent + tags: + - down +- name: "Run PostgreSQL {{ postgresql_version }}" + docker_container: + name: "{{ container_name }}" + image: "{{ container_image }}" + state: started + ports: + - "{{ container_bind_ip }}:{{ container_bind_port }}:5432" + env: + POSTGRES_USER: "{{ db_username }}" + POSTGRES_PASSWORD: "{{ db_password }}" + POSTGRES_DB: "{{ db_name }}" + tags: + - up +- name: "PostgreSQL health check" + postgresql_query: + db: "{{ db_name }}" + login_host: "{{ container_bind_ip }}" + login_port: "{{ container_bind_port }}" + login_user: "{{ db_username }}" + login_password: "{{ db_password }}" + query: "SELECT 1" + register: postgresql_health_check + retries: "{{ health_check_retries }}" + delay: "{{ health_check_delay }}" + until: postgresql_health_check is succeeded + tags: + - up diff --git a/adapter/cockroachdb/cockroachdb_test.go b/tests/cockroachdb/cockroachdb.go similarity index 87% rename from adapter/cockroachdb/cockroachdb_test.go rename to tests/cockroachdb/cockroachdb.go index af33c17d..63dba03a 100644 --- a/adapter/cockroachdb/cockroachdb_test.go +++ b/tests/cockroachdb/cockroachdb.go @@ -1,25 +1,4 @@ -// Copyright (c) 2012-today The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package cockroachdb +package cockroachdb_test import ( "context" @@ -27,6 +6,7 @@ import ( "database/sql/driver" "fmt" "math/rand" + "os" "strconv" "strings" "sync" @@ -37,7 +17,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" db "github.com/upper/db/v4" - "github.com/upper/db/v4/internal/testsuite" + "github.com/upper/db/v4/adapter/cockroachdb" ) type customJSONBObjectArray []customJSONB @@ -46,24 +26,24 @@ func (customJSONBObjectArray) ConvertValue(in interface{}) interface { sql.Scanner driver.Valuer } { - return &JSONB{in} + return &cockroachdb.JSONB{in} } type customJSONBObjectMap map[string]customJSONB func (c customJSONBObjectMap) Value() (driver.Value, error) { - return JSONBValue(c) + return cockroachdb.JSONBValue(c) } func (c *customJSONBObjectMap) Scan(src interface{}) error { - return ScanJSONB(c, src) + return cockroachdb.ScanJSONB(c, src) } type customJSONB struct { N string `json:"name"` V float64 `json:"value"` - *JSONBConverter + *cockroachdb.JSONBConverter } type int64Compat int64 @@ -85,7 +65,7 @@ func (ua uint8CompatArray) Value() (driver.Value, error) { } func (ua *uint8CompatArray) Scan(src interface{}) error { - decoded := Bytea{} + decoded := cockroachdb.Bytea{} if err := decoded.Scan(src); err != nil { return nil } @@ -121,7 +101,7 @@ func (u *int64Compat) Scan(src interface{}) error { type int64CompatArray []int64Compat func (i64a int64CompatArray) Value() (driver.Value, error) { - v := make(Int64Array, len(i64a)) + v := make(cockroachdb.Int64Array, len(i64a)) for i := range i64a { v[i] = int64(i64a[i]) } @@ -129,7 +109,7 @@ func (i64a int64CompatArray) Value() (driver.Value, error) { } func (i64a *int64CompatArray) Scan(src interface{}) error { - s := Int64Array{} + s := cockroachdb.Int64Array{} if err := s.Scan(src); err != nil { return err } @@ -145,7 +125,9 @@ func (i64a *int64CompatArray) Scan(src interface{}) error { } type AdapterTests struct { - testsuite.Suite + *Helper + + suite.Suite } func (s *AdapterTests) SetupSuite() { @@ -222,9 +204,9 @@ func (s *AdapterTests) SkipTest_Issue469_BadConnection() { func testPostgreSQLTypes(t *testing.T, sess db.Session) { type PGTypeInline struct { - IntegerArrayPtr *Int64Array `db:"integer_array_ptr,omitempty"` - StringArrayPtr *StringArray `db:"string_array_ptr,omitempty"` - JSONBMapPtr *JSONBMap `db:"jsonb_map_ptr,omitempty"` + IntegerArrayPtr *cockroachdb.Int64Array `db:"integer_array_ptr,omitempty"` + StringArrayPtr *cockroachdb.StringArray `db:"string_array_ptr,omitempty"` + JSONBMapPtr *cockroachdb.JSONBMap `db:"jsonb_map_ptr,omitempty"` } type PGTypeAutoInline struct { @@ -244,16 +226,16 @@ func testPostgreSQLTypes(t *testing.T, sess db.Session) { Int64Value int64Compat `db:"int64_value"` Int64ValueArray *int64CompatArray `db:"int64_value_array"` - IntegerArray Int64Array `db:"integer_array"` - StringArray StringArray `db:"string_array,stringarray"` - JSONBMap JSONBMap `db:"jsonb_map"` + IntegerArray cockroachdb.Int64Array `db:"integer_array"` + StringArray cockroachdb.StringArray `db:"string_array,stringarray"` + JSONBMap cockroachdb.JSONBMap `db:"jsonb_map"` PGTypeInline `db:",inline"` PGTypeAutoInline `db:",inline"` - JSONBObject JSONB `db:"jsonb_object"` - JSONBArray JSONBArray `db:"jsonb_array"` + JSONBObject cockroachdb.JSONB `db:"jsonb_object"` + JSONBArray cockroachdb.JSONBArray `db:"jsonb_array"` CustomJSONBObject customJSONB `db:"custom_jsonb_object"` AutoCustomJSONBObject customJSONB `db:"auto_custom_jsonb_object"` @@ -273,9 +255,9 @@ func testPostgreSQLTypes(t *testing.T, sess db.Session) { UIntCompatValue uintCompat `db:"uinteger_compat_value"` StringCompatValue stringCompat `db:"string_compat_value"` - Int64CompatValueJSONBArray JSONBArray `db:"integer_compat_value_jsonb_array"` - UIntCompatValueJSONBArray JSONBArray `db:"uinteger_compat_value_jsonb_array"` - StringCompatValueJSONBArray JSONBArray `db:"string_compat_value_jsonb_array"` + Int64CompatValueJSONBArray cockroachdb.JSONBArray `db:"integer_compat_value_jsonb_array"` + UIntCompatValueJSONBArray cockroachdb.JSONBArray `db:"uinteger_compat_value_jsonb_array"` + StringCompatValueJSONBArray cockroachdb.JSONBArray `db:"string_compat_value_jsonb_array"` StringValuePtr *string `db:"string_value_ptr,omitempty"` IntegerValuePtr *int64 `db:"integer_value_ptr,omitempty"` @@ -291,9 +273,9 @@ func testPostgreSQLTypes(t *testing.T, sess db.Session) { uuidStringValue := "52356d08-6a16-4839-9224-75f0a547e13c" - integerArrayValue := Int64Array{1, 2, 3, 4} - stringArrayValue := StringArray{"a", "b", "c"} - jsonbMapValue := JSONBMap{"Hello": "World"} + integerArrayValue := cockroachdb.Int64Array{1, 2, 3, 4} + stringArrayValue := cockroachdb.StringArray{"a", "b", "c"} + jsonbMapValue := cockroachdb.JSONBMap{"Hello": "World"} testValue := "Hello world!" @@ -330,14 +312,14 @@ func testPostgreSQLTypes(t *testing.T, sess db.Session) { StringCompatValue: "abc", }, PGType{ - Int64CompatValueJSONBArray: JSONBArray{1.0, -2.0, 3.0, -4.0}, - UIntCompatValueJSONBArray: JSONBArray{1.0, 2.0, 3.0, 4.0}, - StringCompatValueJSONBArray: JSONBArray{"a", "b", "", "c"}, + Int64CompatValueJSONBArray: cockroachdb.JSONBArray{1.0, -2.0, 3.0, -4.0}, + UIntCompatValueJSONBArray: cockroachdb.JSONBArray{1.0, 2.0, 3.0, 4.0}, + StringCompatValueJSONBArray: cockroachdb.JSONBArray{"a", "b", "", "c"}, }, PGType{ - Int64CompatValueJSONBArray: JSONBArray(nil), - UIntCompatValueJSONBArray: JSONBArray(nil), - StringCompatValueJSONBArray: JSONBArray(nil), + Int64CompatValueJSONBArray: cockroachdb.JSONBArray(nil), + UIntCompatValueJSONBArray: cockroachdb.JSONBArray(nil), + StringCompatValueJSONBArray: cockroachdb.JSONBArray(nil), }, PGType{ IntegerValuePtr: &integerValue, @@ -358,7 +340,7 @@ func testPostgreSQLTypes(t *testing.T, sess db.Session) { }, PGType{ PGTypeAutoInline: PGTypeAutoInline{ - AutoIntegerArray: Int64Array{1, 2, 3, 4}, + AutoIntegerArray: cockroachdb.Int64Array{1, 2, 3, 4}, AutoStringArray: nil, }, }, @@ -385,7 +367,7 @@ func testPostgreSQLTypes(t *testing.T, sess db.Session) { "Roses": "red", }, }, - JSONBArray: JSONBArray{float64(1), float64(2), float64(3), float64(4)}, + JSONBArray: cockroachdb.JSONBArray{float64(1), float64(2), float64(3), float64(4)}, }, PGType{ PGTypeAutoInline: PGTypeAutoInline{ @@ -396,13 +378,13 @@ func testPostgreSQLTypes(t *testing.T, sess db.Session) { PGTypeAutoInline: PGTypeAutoInline{ AutoJSONBMap: map[string]interface{}{}, }, - JSONBArray: JSONBArray{}, + JSONBArray: cockroachdb.JSONBArray{}, }, PGType{ PGTypeAutoInline: PGTypeAutoInline{ AutoJSONBMap: map[string]interface{}(nil), }, - JSONBArray: JSONBArray(nil), + JSONBArray: cockroachdb.JSONBArray(nil), }, PGType{ PGTypeAutoInline: PGTypeAutoInline{ @@ -676,10 +658,10 @@ func (s *AdapterTests) TestOptionTypes() { // An option type to pointer jsonb field type optionType2 struct { - ID int64 `db:"id,omitempty"` - Name string `db:"name"` - Tags StringArray `db:"tags"` - Settings *JSONBMap `db:"settings"` + ID int64 `db:"id,omitempty"` + Name string `db:"name"` + Tags cockroachdb.StringArray `db:"tags"` + Settings *cockroachdb.JSONBMap `db:"settings"` } item2 := optionType2{ @@ -706,7 +688,7 @@ func (s *AdapterTests) TestOptionTypes() { s.Equal(len(item2Chk.Tags), len(item2.Tags)) // Update the value - m := JSONBMap{} + m := cockroachdb.JSONBMap{} m["lang"] = "javascript" m["num"] = 31337 item2.Settings = &m @@ -722,16 +704,16 @@ func (s *AdapterTests) TestOptionTypes() { // An option type to pointer string array field type optionType3 struct { - ID int64 `db:"id,omitempty"` - Name string `db:"name"` - Tags *StringArray `db:"tags"` - Settings JSONBMap `db:"settings"` + ID int64 `db:"id,omitempty"` + Name string `db:"name"` + Tags *cockroachdb.StringArray `db:"tags"` + Settings cockroachdb.JSONBMap `db:"settings"` } item3 := optionType3{ Name: "Julia", Tags: nil, - Settings: JSONBMap{"girl": true, "lang": true}, + Settings: cockroachdb.JSONBMap{"girl": true, "lang": true}, } record, err = optionTypes.Insert(item3) @@ -752,10 +734,10 @@ type Settings struct { } func (s *Settings) Scan(src interface{}) error { - return ScanJSONB(s, src) + return cockroachdb.ScanJSONB(s, src) } func (s Settings) Value() (driver.Value, error) { - return JSONBValue(s) + return cockroachdb.JSONBValue(s) } func (s *AdapterTests) TestOptionTypeJsonbStruct() { @@ -767,10 +749,10 @@ func (s *AdapterTests) TestOptionTypeJsonbStruct() { s.NoError(err) type OptionType struct { - ID int64 `db:"id,omitempty"` - Name string `db:"name"` - Tags StringArray `db:"tags"` - Settings Settings `db:"settings"` + ID int64 `db:"id,omitempty"` + Name string `db:"name"` + Tags cockroachdb.StringArray `db:"tags"` + Settings Settings `db:"settings"` } item1 := &OptionType{ @@ -900,12 +882,12 @@ func (s *AdapterTests) Test_Issue370_InsertUUID() { /* { type itemT struct { - ID Int64Array `db:"id"` + ID cockroachdb.Int64Array `db:"id"` Name string `db:"name"` } item1 := itemT{ - ID: Int64Array{1, 2, 3}, + ID: cockroachdb.Int64Array{1, 2, 3}, Name: "Vojtech", } @@ -1026,19 +1008,19 @@ func (s *AdapterTests) Test_Issue391_TextMode() { } func (s *AdapterTests) Test_Issue391_BinaryMode() { - settingsWithBinaryMode := ConnectionURL{ - Database: settings.Database, - User: settings.User, - Password: settings.Password, - Host: settings.Host, + settingsWithBinaryMode := cockroachdb.ConnectionURL{ + Database: s.Helper.connURL.Database, + User: s.Helper.connURL.User, + Password: s.Helper.connURL.Password, + Host: s.Helper.connURL.Host, Options: map[string]string{ "sslmode": "disable", - "timezone": testsuite.TimeZone, + "timezone": os.Getenv("DB_TIMEZONE"), "binary_parameters": "yes", }, } - sess, err := Open(settingsWithBinaryMode) + sess, err := cockroachdb.Open(settingsWithBinaryMode) if err != nil { s.T().Errorf("%v", err) } @@ -1069,9 +1051,9 @@ func (s *AdapterTests) TestStringAndInt64Array() { s.NoError(err) type arrayType struct { - ID int64 `db:"id,pk"` - Integers Int64Array `db:"integers"` - Strings StringArray `db:"strings"` + ID int64 `db:"id,pk"` + Integers cockroachdb.Int64Array `db:"integers"` + Strings cockroachdb.StringArray `db:"strings"` } tt := []arrayType{ diff --git a/adapter/cockroachdb/helper_test.go b/tests/cockroachdb/helper.go similarity index 80% rename from adapter/cockroachdb/helper_test.go rename to tests/cockroachdb/helper.go index 13dedad5..bf3fba11 100644 --- a/adapter/cockroachdb/helper_test.go +++ b/tests/cockroachdb/helper.go @@ -1,51 +1,20 @@ -// Copyright (c) 2012-today The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package cockroachdb +package cockroachdb_test import ( "database/sql" "fmt" - "os" db "github.com/upper/db/v4" + "github.com/upper/db/v4/adapter/cockroachdb" "github.com/upper/db/v4/internal/sqladapter" - "github.com/upper/db/v4/internal/testsuite" ) -var settings = ConnectionURL{ - Database: os.Getenv("DB_NAME"), - User: os.Getenv("DB_USERNAME"), - Password: os.Getenv("DB_PASSWORD"), - Host: os.Getenv("DB_HOST") + ":" + os.Getenv("DB_PORT"), - Options: map[string]string{ - "sslmode": "disable", - "timezone": testsuite.TimeZone, - }, -} - const preparedStatementsKey = "pg_prepared_statements_count" type Helper struct { sess db.Session + + connURL cockroachdb.ConnectionURL } func cleanUp(sess db.Session) error { @@ -88,7 +57,7 @@ func (h *Helper) Session() db.Session { } func (h *Helper) Adapter() string { - return Adapter + return cockroachdb.Adapter } func (h *Helper) TearDown() error { @@ -102,9 +71,9 @@ func (h *Helper) TearDown() error { func (h *Helper) SetUp() error { var err error - h.sess, err = Open(settings) + h.sess, err = cockroachdb.Open(h.connURL) if err != nil { - return err + return fmt.Errorf("cockroachdb.Open: %v", err) } batches := [][]string{ @@ -316,4 +285,8 @@ func (h *Helper) SetUp() error { return nil } -var _ testsuite.Helper = &Helper{} +func NewHelper(connURL cockroachdb.ConnectionURL) *Helper { + return &Helper{ + connURL: connURL, + } +} diff --git a/tests/entrypoint_test.go b/tests/entrypoint_test.go new file mode 100644 index 00000000..1f549707 --- /dev/null +++ b/tests/entrypoint_test.go @@ -0,0 +1,136 @@ +package db_test + +import ( + "fmt" + "os" + "os/exec" + "testing" + + "github.com/stretchr/testify/suite" +) + +type testHelperConfig struct { + initFn func() (Helper, string, int) + + withoutGeneric bool + withoutRecord bool + withoutSQL bool +} + +func serverUp(t *testing.T, name string, host string, port int) { + cmd := exec.Command("make", "-C", "ansible", "server-up") + + cmd.Env = append( + os.Environ(), + "TARGET="+name, + "CONTAINER_BIND_HOST="+host, + "CONTAINER_BIND_PORT="+fmt.Sprintf("%d", port), + ) + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + err := cmd.Run() + if err != nil { + t.Fatalf("could not start server: %v", err) + } +} + +func serverDown(t *testing.T, name string) { + cmd := exec.Command("make", "-C", "ansible", "server-down") + + cmd.Env = append( + os.Environ(), + "TARGET="+name, + ) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + err := cmd.Run() + if err != nil { + t.Fatalf("could not stop server: %v", err) + } +} + +func Test(t *testing.T) { + testCfgs := map[string]testHelperConfig{ + "postgresql-17": { + initFn: newPostgreSQLTestHelper(), + }, + "postgresql-16": { + initFn: newPostgreSQLTestHelper(), + }, + "postgresql-15": { + initFn: newPostgreSQLTestHelper(), + }, + "mysql-latest": { + initFn: newMySQLTestHelper(), + }, + "mysql-lts": { + initFn: newMySQLTestHelper(), + }, + "mysql-5": { + initFn: newMySQLTestHelper(), + }, + + "cockroachdb-v23": { + initFn: newCockroachDBTestHelper(), + }, + "cockroachdb-v22": { + initFn: newCockroachDBTestHelper(), + }, + + "mssql-2022": { + initFn: newMSSQLTestHelper(), + withoutRecord: true, + }, + "mssql-2019": { + initFn: newMSSQLTestHelper(), + withoutRecord: true, // TODO: fix MSSQL record tests + }, + + "mongodb-8": { + initFn: newMongoDBTestHelper(), + withoutSQL: true, + withoutRecord: true, + }, + "mongodb-7": { + initFn: newMongoDBTestHelper(), + withoutSQL: true, + withoutRecord: true, + }, + } + + for name, cfg := range testCfgs { + t.Run(name, func(t *testing.T) { + helper, bindAddr, bindPort := cfg.initFn() + + serverUp(t, name, bindAddr, bindPort) + defer serverDown(t, name) + + t.Run("Generic", func(t *testing.T) { + if cfg.withoutGeneric { + t.Skip("Generic tests are disabled for this adapter") + } + + suite.Run(t, &GenericTestSuite{Helper: helper}) + }) + + t.Run("Record", func(t *testing.T) { + if cfg.withoutRecord { + t.Skip("Record tests are disabled for this adapter") + } + + suite.Run(t, &RecordTestSuite{Helper: helper}) + }) + + t.Run("SQL", func(t *testing.T) { + if cfg.withoutSQL { + t.Skip("SQL tests are disabled for this adapter") + } + + suite.Run(t, &SQLTestSuite{Helper: helper}) + }) + }) + } +} diff --git a/internal/testsuite/generic_suite.go b/tests/generic_suite_test.go similarity index 94% rename from internal/testsuite/generic_suite.go rename to tests/generic_suite_test.go index 92181d38..4eb9e05b 100644 --- a/internal/testsuite/generic_suite.go +++ b/tests/generic_suite_test.go @@ -1,25 +1,4 @@ -// Copyright (c) 2012-present The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package testsuite +package db_test import ( "database/sql/driver" diff --git a/tests/mongo/helper.go b/tests/mongo/helper.go new file mode 100644 index 00000000..21797bd0 --- /dev/null +++ b/tests/mongo/helper.go @@ -0,0 +1,82 @@ +package mongo_test + +import ( + "context" + "fmt" + + db "github.com/upper/db/v4" + mongodrv "go.mongodb.org/mongo-driver/mongo" + + "github.com/upper/db/v4/adapter/mongo" +) + +type Helper struct { + sess db.Session + + connURL mongo.ConnectionURL +} + +func (h *Helper) Session() db.Session { + return h.sess +} + +func (h *Helper) Adapter() string { + return "mongo" +} + +func (h *Helper) TearDown() error { + return h.sess.Close() +} + +func (h *Helper) SetUp() error { + ctx := context.Background() + + var err error + + h.sess, err = mongo.Open(h.connURL) + if err != nil { + return fmt.Errorf("mongo.Open: %w", err) + } + + mgdb, ok := h.sess.Driver().(*mongodrv.Client) + if !ok { + panic("expecting *mongo.Client") + } + + var col *mongodrv.Collection + col = mgdb.Database(h.connURL.Database).Collection("birthdays") + _ = col.Drop(ctx) + + col = mgdb.Database(h.connURL.Database).Collection("fibonacci") + _ = col.Drop(ctx) + + col = mgdb.Database(h.connURL.Database).Collection("is_even") + _ = col.Drop(ctx) + + col = mgdb.Database(h.connURL.Database).Collection("CaSe_TesT") + _ = col.Drop(ctx) + + // Getting a pointer to the "artist" collection. + artist := h.sess.Collection("artist") + + _ = artist.Truncate() + + /* + for i := 0; i < 999; i++ { + _, err = artist.Insert(artistType{ + Name: fmt.Sprintf("artist-%d", i), + }) + if err != nil { + return fmt.Errorf("insert: %w", err) + } + } + */ + + return nil +} + +func NewHelper(connURL mongo.ConnectionURL) *Helper { + return &Helper{ + connURL: connURL, + } +} diff --git a/adapter/mssql/helper_test.go b/tests/mssql/helper.go similarity index 68% rename from adapter/mssql/helper_test.go rename to tests/mssql/helper.go index 83f20134..38d2422a 100644 --- a/adapter/mssql/helper_test.go +++ b/tests/mssql/helper.go @@ -1,44 +1,17 @@ -// Copyright (c) 2012-today The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - package mssql import ( "database/sql" - "os" + "fmt" db "github.com/upper/db/v4" - "github.com/upper/db/v4/internal/testsuite" + "github.com/upper/db/v4/adapter/mssql" ) -var settings = ConnectionURL{ - Database: os.Getenv("DB_NAME"), - User: os.Getenv("DB_USERNAME"), - Password: os.Getenv("DB_PASSWORD"), - Host: os.Getenv("DB_HOST") + ":" + os.Getenv("DB_PORT"), - Options: map[string]string{}, -} - type Helper struct { sess db.Session + + connURL mssql.ConnectionURL } func (h *Helper) Session() db.Session { @@ -56,9 +29,9 @@ func (h *Helper) TearDown() error { func (h *Helper) SetUp() error { var err error - h.sess, err = Open(settings) + h.sess, err = mssql.Open(h.connURL) if err != nil { - return err + return fmt.Errorf("error opening session: %v", err) } batch := []string{ @@ -185,4 +158,8 @@ func (h *Helper) SetUp() error { return nil } -var _ testsuite.Helper = &Helper{} +func NewHelper(connURL mssql.ConnectionURL) *Helper { + return &Helper{ + connURL: connURL, + } +} diff --git a/adapter/mysql/helper_test.go b/tests/mysql/helper.go similarity index 77% rename from adapter/mysql/helper_test.go rename to tests/mysql/helper.go index 671851bb..2daa7405 100644 --- a/adapter/mysql/helper_test.go +++ b/tests/mysql/helper.go @@ -1,53 +1,19 @@ -// Copyright (c) 2012-today The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package mysql +package mysql_test import ( "database/sql" "fmt" - "os" "time" db "github.com/upper/db/v4" + "github.com/upper/db/v4/adapter/mysql" "github.com/upper/db/v4/internal/sqladapter" - "github.com/upper/db/v4/internal/testsuite" ) -var settings = ConnectionURL{ - Database: os.Getenv("DB_NAME"), - User: os.Getenv("DB_USERNAME"), - Password: os.Getenv("DB_PASSWORD"), - Host: os.Getenv("DB_HOST") + ":" + os.Getenv("DB_PORT"), - Options: map[string]string{ - // See https://github.com/go-sql-driver/mysql/issues/9 - "parseTime": "true", - // Might require you to use mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql - "time_zone": fmt.Sprintf(`'%s'`, testsuite.TimeZone), - "loc": testsuite.TimeZone, - }, -} - type Helper struct { sess db.Session + + connURL mysql.ConnectionURL } func cleanUp(sess db.Session) error { @@ -119,9 +85,9 @@ func (h *Helper) TearDown() error { func (h *Helper) SetUp() error { var err error - h.sess, err = Open(settings) + h.sess, err = mysql.Open(h.connURL) if err != nil { - return err + return fmt.Errorf("Error opening database: %w", err) } batch := []string{ @@ -294,4 +260,8 @@ func (h *Helper) SetUp() error { return nil } -var _ testsuite.Helper = &Helper{} +func NewHelper(connURL mysql.ConnectionURL) *Helper { + return &Helper{ + connURL: connURL, + } +} diff --git a/adapter/mysql/mysql_test.go b/tests/mysql/mysql.go similarity index 83% rename from adapter/mysql/mysql_test.go rename to tests/mysql/mysql.go index 122d9cdb..567ba49f 100644 --- a/adapter/mysql/mysql_test.go +++ b/tests/mysql/mysql.go @@ -1,25 +1,4 @@ -// Copyright (c) 2012-today The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package mysql +package mysql_test import ( "database/sql" @@ -27,12 +6,11 @@ import ( "fmt" "math/rand" "strconv" - "testing" "time" "github.com/stretchr/testify/suite" "github.com/upper/db/v4" - "github.com/upper/db/v4/internal/testsuite" + "github.com/upper/db/v4/adapter/mysql" ) type int64Compat int64 @@ -67,27 +45,24 @@ type customJSON struct { } func (c customJSON) Value() (driver.Value, error) { - return JSONValue(c) + return mysql.JSONValue(c) } func (c *customJSON) Scan(src interface{}) error { - return ScanJSON(c, src) + return mysql.ScanJSON(c, src) } type autoCustomJSON struct { N string `json:"name"` V float64 `json:"value"` - *JSONConverter + *mysql.JSONConverter } -var ( - _ = driver.Valuer(&customJSON{}) - _ = sql.Scanner(&customJSON{}) -) - type AdapterTests struct { - testsuite.Suite + *Helper + + suite.Suite } func (s *AdapterTests) SetupSuite() { @@ -209,10 +184,10 @@ func (s *AdapterTests) TestMySQLTypes() { type MyType struct { ID int64 `db:"id,omitempty"` - JSONMap JSONMap `db:"json_map"` + JSONMap mysql.JSONMap `db:"json_map"` - JSONObject JSONMap `db:"json_object"` - JSONArray JSONArray `db:"json_array"` + JSONObject mysql.JSONMap `db:"json_object"` + JSONArray mysql.JSONArray `db:"json_array"` CustomJSONObject customJSON `db:"custom_json_object"` AutoCustomJSONObject autoCustomJSON `db:"auto_custom_json_object"` @@ -256,13 +231,13 @@ func (s *AdapterTests) TestMySQLTypes() { N: "World", }, }, - JSONArray: JSONArray{float64(1), float64(2), float64(3), float64(4)}, + JSONArray: mysql.JSONArray{float64(1), float64(2), float64(3), float64(4)}, }, MyType{ - JSONArray: JSONArray{}, + JSONArray: mysql.JSONArray{}, }, MyType{ - JSONArray: JSONArray(nil), + JSONArray: mysql.JSONArray(nil), }, MyType{}, MyType{ @@ -395,6 +370,7 @@ func (s *AdapterTests) TestMySQLTypes() { } } -func TestAdapter(t *testing.T) { - suite.Run(t, &AdapterTests{}) -} +var ( + _ = driver.Valuer(&customJSON{}) + _ = sql.Scanner(&customJSON{}) +) diff --git a/adapter/postgresql/helper_test.go b/tests/postgresql/helper.go similarity index 80% rename from adapter/postgresql/helper_test.go rename to tests/postgresql/helper.go index a5a8da02..08c04244 100644 --- a/adapter/postgresql/helper_test.go +++ b/tests/postgresql/helper.go @@ -1,50 +1,20 @@ -// Copyright (c) 2012-today The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package postgresql +package postgresql_test import ( "database/sql" "fmt" - "os" db "github.com/upper/db/v4" + "github.com/upper/db/v4/adapter/postgresql" "github.com/upper/db/v4/internal/sqladapter" - "github.com/upper/db/v4/internal/testsuite" ) -var settings = ConnectionURL{ - Database: os.Getenv("DB_NAME"), - User: os.Getenv("DB_USERNAME"), - Password: os.Getenv("DB_PASSWORD"), - Host: os.Getenv("DB_HOST") + ":" + os.Getenv("DB_PORT"), - Options: map[string]string{ - "timezone": testsuite.TimeZone, - }, -} - const preparedStatementsKey = "pg_prepared_statements_count" type Helper struct { sess db.Session + + connURL postgresql.ConnectionURL } func cleanUp(sess db.Session) error { @@ -74,7 +44,7 @@ func getStats(sess db.Session) (map[string]int, error) { var value int err := row.Scan(&value) if err != nil { - return nil, err + return nil, fmt.Errorf("Error getting prepared statements count: %w", err) } stats[preparedStatementsKey] = value @@ -87,23 +57,15 @@ func (h *Helper) Session() db.Session { } func (h *Helper) Adapter() string { - return Adapter -} - -func (h *Helper) TearDown() error { - if err := cleanUp(h.sess); err != nil { - return err - } - - return h.sess.Close() + return postgresql.Adapter } func (h *Helper) SetUp() error { var err error - h.sess, err = Open(settings) + h.sess, err = postgresql.Open(h.connURL) if err != nil { - return err + return fmt.Errorf("Error opening database: %w", err) } batch := []string{ @@ -339,4 +301,16 @@ func (h *Helper) SetUp() error { return nil } -var _ testsuite.Helper = &Helper{} +func (h *Helper) TearDown() error { + if err := cleanUp(h.sess); err != nil { + return err + } + + return h.sess.Close() +} + +func NewHelper(connURL postgresql.ConnectionURL) *Helper { + return &Helper{ + connURL: connURL, + } +} diff --git a/adapter/postgresql/postgresql_test.go b/tests/postgresql/postgresql.go similarity index 87% rename from adapter/postgresql/postgresql_test.go rename to tests/postgresql/postgresql.go index ec56d583..441d0a1d 100644 --- a/adapter/postgresql/postgresql_test.go +++ b/tests/postgresql/postgresql.go @@ -1,25 +1,4 @@ -// Copyright (c) 2012-today The upper.io/db authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package postgresql +package postgresql_test import ( "context" @@ -29,6 +8,7 @@ import ( "fmt" "math/rand" "net" + "os" "strings" "sync" "testing" @@ -38,7 +18,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" db "github.com/upper/db/v4" - "github.com/upper/db/v4/internal/testsuite" + "github.com/upper/db/v4/adapter/postgresql" ) type customJSONBObjectArray []customJSONB @@ -47,24 +27,24 @@ func (customJSONBObjectArray) ConvertValue(in interface{}) interface { sql.Scanner driver.Valuer } { - return &JSONB{in} + return &postgresql.JSONB{in} } type customJSONBObjectMap map[string]customJSONB func (c customJSONBObjectMap) Value() (driver.Value, error) { - return JSONBValue(c) + return postgresql.JSONBValue(c) } func (c *customJSONBObjectMap) Scan(src interface{}) error { - return ScanJSONB(c, src) + return postgresql.ScanJSONB(c, src) } type customJSONB struct { N string `json:"name"` V float64 `json:"value"` - *JSONBConverter + *postgresql.JSONBConverter } type int64Compat int64 @@ -86,7 +66,7 @@ func (ua uint8CompatArray) Value() (driver.Value, error) { } func (ua *uint8CompatArray) Scan(src interface{}) error { - decoded := Bytea{} + decoded := postgresql.Bytea{} if err := decoded.Scan(src); err != nil { return nil } @@ -104,7 +84,7 @@ func (ua *uint8CompatArray) Scan(src interface{}) error { type int64CompatArray []int64Compat func (i64a int64CompatArray) Value() (driver.Value, error) { - v := make(Int64Array, len(i64a)) + v := make(postgresql.Int64Array, len(i64a)) for i := range i64a { v[i] = int64(i64a[i]) } @@ -112,7 +92,7 @@ func (i64a int64CompatArray) Value() (driver.Value, error) { } func (i64a *int64CompatArray) Scan(src interface{}) error { - s := Int64Array{} + s := postgresql.Int64Array{} if err := s.Scan(src); err != nil { return err } @@ -128,7 +108,9 @@ func (i64a *int64CompatArray) Scan(src interface{}) error { } type AdapterTests struct { - testsuite.Suite + *Helper + + suite.Suite } func (s *AdapterTests) SetupSuite() { @@ -203,9 +185,9 @@ func (s *AdapterTests) Test_Issue469_BadConnection() { func testPostgreSQLTypes(t *testing.T, sess db.Session) { type PGTypeInline struct { - IntegerArrayPtr *Int64Array `db:"integer_array_ptr,omitempty"` - StringArrayPtr *StringArray `db:"string_array_ptr,omitempty"` - JSONBMapPtr *JSONBMap `db:"jsonb_map_ptr,omitempty"` + IntegerArrayPtr *postgresql.Int64Array `db:"integer_array_ptr,omitempty"` + StringArrayPtr *postgresql.StringArray `db:"string_array_ptr,omitempty"` + JSONBMapPtr *postgresql.JSONBMap `db:"jsonb_map_ptr,omitempty"` } type PGTypeAutoInline struct { @@ -225,9 +207,9 @@ func testPostgreSQLTypes(t *testing.T, sess db.Session) { Int64Value int64Compat `db:"int64_value"` Int64ValueArray *int64CompatArray `db:"int64_value_array"` - IntegerArray Int64Array `db:"integer_array"` - StringArray StringArray `db:"string_array,stringarray"` - JSONBMap JSONBMap `db:"jsonb_map"` + IntegerArray postgresql.Int64Array `db:"integer_array"` + StringArray postgresql.StringArray `db:"string_array,stringarray"` + JSONBMap postgresql.JSONBMap `db:"jsonb_map"` RawJSONBMap *json.RawMessage `db:"raw_jsonb_map,omitempty"` RawJSONBText *json.RawMessage `db:"raw_jsonb_text,omitempty"` @@ -236,8 +218,8 @@ func testPostgreSQLTypes(t *testing.T, sess db.Session) { PGTypeAutoInline `db:",inline"` - JSONBObject JSONB `db:"jsonb_object"` - JSONBArray JSONBArray `db:"jsonb_array"` + JSONBObject postgresql.JSONB `db:"jsonb_object"` + JSONBArray postgresql.JSONBArray `db:"jsonb_array"` CustomJSONBObject customJSONB `db:"custom_jsonb_object"` AutoCustomJSONBObject customJSONB `db:"auto_custom_jsonb_object"` @@ -257,9 +239,9 @@ func testPostgreSQLTypes(t *testing.T, sess db.Session) { UIntCompatValue uintCompat `db:"uinteger_compat_value"` StringCompatValue stringCompat `db:"string_compat_value"` - Int64CompatValueJSONBArray JSONBArray `db:"integer_compat_value_jsonb_array"` - UIntCompatValueJSONBArray JSONBArray `db:"uinteger_compat_value_jsonb_array"` - StringCompatValueJSONBArray JSONBArray `db:"string_compat_value_jsonb_array"` + Int64CompatValueJSONBArray postgresql.JSONBArray `db:"integer_compat_value_jsonb_array"` + UIntCompatValueJSONBArray postgresql.JSONBArray `db:"uinteger_compat_value_jsonb_array"` + StringCompatValueJSONBArray postgresql.JSONBArray `db:"string_compat_value_jsonb_array"` StringValuePtr *string `db:"string_value_ptr,omitempty"` IntegerValuePtr *int64 `db:"integer_value_ptr,omitempty"` @@ -277,9 +259,9 @@ func testPostgreSQLTypes(t *testing.T, sess db.Session) { uuidStringValue := "52356d08-6a16-4839-9224-75f0a547e13c" - integerArrayValue := Int64Array{1, 2, 3, 4} - stringArrayValue := StringArray{"a", "b", "c"} - jsonbMapValue := JSONBMap{"Hello": "World"} + integerArrayValue := postgresql.Int64Array{1, 2, 3, 4} + stringArrayValue := postgresql.StringArray{"a", "b", "c"} + jsonbMapValue := postgresql.JSONBMap{"Hello": "World"} rawJSONBMap := json.RawMessage(`{"foo": "bar"}`) rawJSONBText := json.RawMessage(`{"age": [{">": "1h"}]}`) @@ -318,14 +300,14 @@ func testPostgreSQLTypes(t *testing.T, sess db.Session) { StringCompatValue: "abc", }, PGType{ - Int64CompatValueJSONBArray: JSONBArray{1.0, -2.0, 3.0, -4.0}, - UIntCompatValueJSONBArray: JSONBArray{1.0, 2.0, 3.0, 4.0}, - StringCompatValueJSONBArray: JSONBArray{"a", "b", "", "c"}, + Int64CompatValueJSONBArray: postgresql.JSONBArray{1.0, -2.0, 3.0, -4.0}, + UIntCompatValueJSONBArray: postgresql.JSONBArray{1.0, 2.0, 3.0, 4.0}, + StringCompatValueJSONBArray: postgresql.JSONBArray{"a", "b", "", "c"}, }, PGType{ - Int64CompatValueJSONBArray: JSONBArray(nil), - UIntCompatValueJSONBArray: JSONBArray(nil), - StringCompatValueJSONBArray: JSONBArray(nil), + Int64CompatValueJSONBArray: postgresql.JSONBArray(nil), + UIntCompatValueJSONBArray: postgresql.JSONBArray(nil), + StringCompatValueJSONBArray: postgresql.JSONBArray(nil), }, PGType{ IntegerValuePtr: &integerValue, @@ -350,7 +332,7 @@ func testPostgreSQLTypes(t *testing.T, sess db.Session) { }, PGType{ PGTypeAutoInline: PGTypeAutoInline{ - AutoIntegerArray: Int64Array{1, 2, 3, 4}, + AutoIntegerArray: postgresql.Int64Array{1, 2, 3, 4}, AutoStringArray: nil, }, }, @@ -377,7 +359,7 @@ func testPostgreSQLTypes(t *testing.T, sess db.Session) { "Roses": "red", }, }, - JSONBArray: JSONBArray{float64(1), float64(2), float64(3), float64(4)}, + JSONBArray: postgresql.JSONBArray{float64(1), float64(2), float64(3), float64(4)}, }, PGType{ PGTypeAutoInline: PGTypeAutoInline{ @@ -388,13 +370,13 @@ func testPostgreSQLTypes(t *testing.T, sess db.Session) { PGTypeAutoInline: PGTypeAutoInline{ AutoJSONBMap: map[string]interface{}{}, }, - JSONBArray: JSONBArray{}, + JSONBArray: postgresql.JSONBArray{}, }, PGType{ PGTypeAutoInline: PGTypeAutoInline{ AutoJSONBMap: map[string]interface{}(nil), }, - JSONBArray: JSONBArray(nil), + JSONBArray: postgresql.JSONBArray(nil), }, PGType{ PGTypeAutoInline: PGTypeAutoInline{ @@ -669,10 +651,10 @@ func (s *AdapterTests) TestOptionTypes() { // An option type to pointer jsonb field type optionType2 struct { - ID int64 `db:"id,omitempty"` - Name string `db:"name"` - Tags StringArray `db:"tags"` - Settings *JSONBMap `db:"settings"` + ID int64 `db:"id,omitempty"` + Name string `db:"name"` + Tags postgresql.StringArray `db:"tags"` + Settings *postgresql.JSONBMap `db:"settings"` } item2 := optionType2{ @@ -699,7 +681,7 @@ func (s *AdapterTests) TestOptionTypes() { s.Equal(len(item2Chk.Tags), len(item2.Tags)) // Update the value - m := JSONBMap{} + m := postgresql.JSONBMap{} m["lang"] = "javascript" m["num"] = 31337 item2.Settings = &m @@ -715,16 +697,16 @@ func (s *AdapterTests) TestOptionTypes() { // An option type to pointer string array field type optionType3 struct { - ID int64 `db:"id,omitempty"` - Name string `db:"name"` - Tags *StringArray `db:"tags"` - Settings JSONBMap `db:"settings"` + ID int64 `db:"id,omitempty"` + Name string `db:"name"` + Tags *postgresql.StringArray `db:"tags"` + Settings postgresql.JSONBMap `db:"settings"` } item3 := optionType3{ Name: "Julia", Tags: nil, - Settings: JSONBMap{"girl": true, "lang": true}, + Settings: postgresql.JSONBMap{"girl": true, "lang": true}, } record, err = optionTypes.Insert(item3) @@ -745,10 +727,10 @@ type Settings struct { } func (s *Settings) Scan(src interface{}) error { - return ScanJSONB(s, src) + return postgresql.ScanJSONB(s, src) } func (s Settings) Value() (driver.Value, error) { - return JSONBValue(s) + return postgresql.JSONBValue(s) } func (s *AdapterTests) TestOptionTypeJsonbStruct() { @@ -760,10 +742,10 @@ func (s *AdapterTests) TestOptionTypeJsonbStruct() { s.NoError(err) type OptionType struct { - ID int64 `db:"id,omitempty"` - Name string `db:"name"` - Tags StringArray `db:"tags"` - Settings Settings `db:"settings"` + ID int64 `db:"id,omitempty"` + Name string `db:"name"` + Tags postgresql.StringArray `db:"tags"` + Settings Settings `db:"settings"` } item1 := &OptionType{ @@ -891,12 +873,12 @@ func (s *AdapterTests) Test_Issue370_InsertUUID() { { type itemT struct { - ID Int64Array `db:"id"` - Name string `db:"name"` + ID postgresql.Int64Array `db:"id"` + Name string `db:"name"` } item1 := itemT{ - ID: Int64Array{1, 2, 3}, + ID: postgresql.Int64Array{1, 2, 3}, Name: "Vojtech", } @@ -944,18 +926,17 @@ var _ interface { } = &issue602Organization{} func (s *AdapterTests) Test_Issue602_IncorrectBinaryFormat() { - settingsWithBinaryMode := ConnectionURL{ - Database: settings.Database, - User: settings.User, - Password: settings.Password, - Host: settings.Host, + settingsWithBinaryMode := postgresql.ConnectionURL{ + Database: s.Helper.connURL.Database, + User: s.Helper.connURL.User, + Password: s.Helper.connURL.Password, + Host: s.Helper.connURL.Host, Options: map[string]string{ - "timezone": testsuite.TimeZone, - //"binary_parameters": "yes", + "timezone": os.Getenv("DB_TIMEZONE"), }, } - sess, err := Open(settingsWithBinaryMode) + sess, err := postgresql.Open(settingsWithBinaryMode) if err != nil { s.T().Errorf("%v", err) } @@ -1111,18 +1092,17 @@ func (s *AdapterTests) Test_Issue391_TextMode() { } func (s *AdapterTests) Test_Issue391_BinaryMode() { - settingsWithBinaryMode := ConnectionURL{ - Database: settings.Database, - User: settings.User, - Password: settings.Password, - Host: settings.Host, + settingsWithBinaryMode := postgresql.ConnectionURL{ + Database: s.Helper.connURL.Database, + User: s.Helper.connURL.User, + Password: s.Helper.connURL.Password, + Host: s.Helper.connURL.Host, Options: map[string]string{ - "timezone": testsuite.TimeZone, - //"binary_parameters": "yes", + "timezone": os.Getenv("DB_TIMEZONE"), }, } - sess, err := Open(settingsWithBinaryMode) + sess, err := postgresql.Open(settingsWithBinaryMode) if err != nil { s.T().Errorf("%v", err) } @@ -1153,9 +1133,9 @@ func (s *AdapterTests) TestStringAndInt64Array() { s.NoError(err) type arrayType struct { - ID int64 `db:"id,pk"` - Integers Int64Array `db:"integers"` - Strings StringArray `db:"strings"` + ID int64 `db:"id,pk"` + Integers postgresql.Int64Array `db:"integers"` + Strings postgresql.StringArray `db:"strings"` } tt := []arrayType{ diff --git a/internal/testsuite/record_suite.go b/tests/record_suite_test.go similarity index 85% rename from internal/testsuite/record_suite.go rename to tests/record_suite_test.go index 825d46d4..bb78a20b 100644 --- a/internal/testsuite/record_suite.go +++ b/tests/record_suite_test.go @@ -1,4 +1,4 @@ -package testsuite +package db_test import ( "context" @@ -79,26 +79,27 @@ func (*User) Store(sess db.Session) db.Store { type RecordTestSuite struct { suite.Suite + Helper } func (s *RecordTestSuite) AfterTest(suiteName, testName string) { err := s.TearDown() - s.NoError(err) + s.Require().NoError(err) } func (s *RecordTestSuite) BeforeTest(suiteName, testName string) { err := s.SetUp() - s.NoError(err) + s.Require().NoError(err) sess := s.Helper.Session() cols, err := sess.Collections() - s.NoError(err) + s.Require().NoError(err) for i := range cols { err = cols[i].Truncate() - s.NoError(err) + s.Require().NoError(err) } } @@ -108,32 +109,32 @@ func (s *RecordTestSuite) TestFindOne() { user := User{Username: "jose"} err = sess.Save(&user) - s.NoError(err) + s.Require().NoError(err) s.NotZero(user.ID) userID := user.ID user = User{} err = Users(sess).Find(userID).One(&user) - s.NoError(err) + s.Require().NoError(err) s.Equal("jose", user.Username) user = User{} err = sess.Get(&user, db.Cond{"username": "jose"}) - s.NoError(err) + s.Require().NoError(err) s.Equal("jose", user.Username) user.Username = "Catalina" err = sess.Save(&user) - s.NoError(err) + s.Require().NoError(err) user = User{} err = sess.Get(&user, userID) - s.NoError(err) + s.Require().NoError(err) s.Equal("Catalina", user.Username) err = sess.Delete(&user) - s.NoError(err) + s.Require().NoError(err) err = sess.Get(&user, userID) s.Error(err) @@ -150,7 +151,7 @@ func (s *RecordTestSuite) TestAccounts() { user := User{Username: "peter"} err := sess.Save(&user) - s.NoError(err) + s.Require().NoError(err) user = User{Username: "peter"} err = sess.Save(&user) @@ -158,38 +159,38 @@ func (s *RecordTestSuite) TestAccounts() { account1 := Account{Name: "skywalker"} err = sess.Save(&account1) - s.NoError(err) + s.Require().NoError(err) account2 := Account{} err = sess.Get(&account2, account1.ID) - s.NoError(err) + s.Require().NoError(err) s.Equal(account1.Name, account2.Name) var account3 Account err = sess.Get(&account3, account1.ID) - s.NoError(err) + s.Require().NoError(err) s.Equal(account1.Name, account3.Name) var a Account err = sess.Get(&a, account1.ID) - s.NoError(err) + s.Require().NoError(err) s.NotNil(a) account1.Disabled = true err = sess.Save(&account1) - s.NoError(err) + s.Require().NoError(err) count, err := Accounts(sess).Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(1), count) err = sess.Delete(&account1) - s.NoError(err) + s.Require().NoError(err) count, err = Accounts(sess).Find().Count() - s.NoError(err) + s.Require().NoError(err) s.Zero(count) } @@ -198,34 +199,34 @@ func (s *RecordTestSuite) TestDelete() { account := Account{Name: "Pressly"} err := sess.Save(&account) - s.NoError(err) + s.Require().NoError(err) s.NotZero(account.ID) // Delete by query -- without callbacks err = Accounts(sess). Find(account.ID). Delete() - s.NoError(err) + s.Require().NoError(err) count, err := Accounts(sess).Find(account.ID).Count() s.Zero(count) - s.NoError(err) + s.Require().NoError(err) } func (s *RecordTestSuite) TestSlices() { sess := s.Session() err := sess.Save(&Account{Name: "Apple"}) - s.NoError(err) + s.Require().NoError(err) err = sess.Save(&Account{Name: "Google"}) - s.NoError(err) + s.Require().NoError(err) var accounts []*Account err = Accounts(sess). Find(db.Cond{}). All(&accounts) - s.NoError(err) + s.Require().NoError(err) s.Len(accounts, 2) } @@ -233,10 +234,10 @@ func (s *RecordTestSuite) TestSelectOnlyIDs() { sess := s.Session() err := sess.Save(&Account{Name: "Apple"}) - s.NoError(err) + s.Require().NoError(err) err = sess.Save(&Account{Name: "Google"}) - s.NoError(err) + s.Require().NoError(err) var ids []struct { Id int64 `db:"id"` @@ -245,7 +246,7 @@ func (s *RecordTestSuite) TestSelectOnlyIDs() { err = Accounts(sess). Find(). Select("id").All(&ids) - s.NoError(err) + s.Require().NoError(err) s.Len(ids, 2) s.NotEmpty(ids[0]) } @@ -255,7 +256,7 @@ func (s *RecordTestSuite) TestTx() { user := User{Username: "peter"} err := sess.Save(&user) - s.NoError(err) + s.Require().NoError(err) // This transaction should fail because user is a UNIQUE value and we already // have a "peter". @@ -299,7 +300,7 @@ func (s *RecordTestSuite) TestTx() { return nil }) - s.NoError(err) + s.Require().NoError(err) // If the transaction above was successful, this one will fail. err = sess.Tx(func(tx db.Session) error { @@ -323,15 +324,15 @@ func (s *RecordTestSuite) TestInheritedTx() { user := User{Username: "peter"} err := sess.Save(&user) - s.NoError(err) + s.Require().NoError(err) // Create a transaction sqlTx, err := sqlDB.Begin() - s.NoError(err) + s.Require().NoError(err) // And pass that transaction to upper/db, this whole session is a transaction. upperTx, err := sqlbuilder.BindTx(s.Adapter(), sqlTx) - s.NoError(err) + s.Require().NoError(err) // Should fail because user is a UNIQUE value and we already have a "peter". err = upperTx.Save(&User{Username: "peter"}) @@ -339,7 +340,7 @@ func (s *RecordTestSuite) TestInheritedTx() { // The transaction is controlled outside upper/db. err = sqlTx.Rollback() - s.NoError(err) + s.Require().NoError(err) // The sqlTx is worthless now. err = upperTx.Save(&User{Username: "peter-2"}) @@ -347,49 +348,49 @@ func (s *RecordTestSuite) TestInheritedTx() { // But we can create a new one. sqlTx, err = sqlDB.Begin() - s.NoError(err) + s.Require().NoError(err) s.NotNil(sqlTx) // And create another session. upperTx, err = sqlbuilder.BindTx(s.Adapter(), sqlTx) - s.NoError(err) + s.Require().NoError(err) // Adding two new values. err = upperTx.Save(&User{Username: "Joe-2"}) - s.NoError(err) + s.Require().NoError(err) err = upperTx.Save(&User{Username: "Cool-2"}) - s.NoError(err) + s.Require().NoError(err) // And a value that is going to be rolled back. err = upperTx.Save(&Account{Name: "Rolled back"}) - s.NoError(err) + s.Require().NoError(err) // This session happens to be a transaction, let's rollback everything. err = sqlTx.Rollback() - s.NoError(err) + s.Require().NoError(err) // Start again. sqlTx, err = sqlDB.Begin() - s.NoError(err) + s.Require().NoError(err) tx, err := sqlbuilder.BindTx(s.Adapter(), sqlTx) - s.NoError(err) + s.Require().NoError(err) // Attempt to add two unique values. err = tx.Save(&User{Username: "Joe-2"}) - s.NoError(err) + s.Require().NoError(err) err = tx.Save(&User{Username: "Cool-2"}) - s.NoError(err) + s.Require().NoError(err) // And a value that is going to be commited. err = tx.Save(&Account{Name: "Commited!"}) - s.NoError(err) + s.Require().NoError(err) // Yes, commit them. err = sqlTx.Commit() - s.NoError(err) + s.Require().NoError(err) } func (s *RecordTestSuite) TestUnknownCollection() { @@ -400,7 +401,7 @@ func (s *RecordTestSuite) TestUnknownCollection() { s.Error(err) _, err = sess.Collection("users").Insert(&User{Username: "Foo"}) - s.NoError(err) + s.Require().NoError(err) } func (s *RecordTestSuite) TestContextCanceled() { @@ -409,7 +410,7 @@ func (s *RecordTestSuite) TestContextCanceled() { sess := s.Session() err = sess.Collection("users").Truncate() - s.NoError(err) + s.Require().NoError(err) { ctx, cancelFn := context.WithTimeout(context.Background(), time.Minute) @@ -422,7 +423,7 @@ func (s *RecordTestSuite) TestContextCanceled() { s.Error(err) c, err := sess.Collection("users").Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(0), c) } } diff --git a/internal/testsuite/sql_suite.go b/tests/sql_suite_test.go similarity index 89% rename from internal/testsuite/sql_suite.go rename to tests/sql_suite_test.go index 4deecea9..ce5b9d5f 100644 --- a/internal/testsuite/sql_suite.go +++ b/tests/sql_suite_test.go @@ -1,4 +1,4 @@ -package testsuite +package db_test import ( "context" @@ -54,11 +54,6 @@ func (f *customType) UnmarshalDB(in interface{}) error { return nil } -var ( - _ = db.Marshaler(&customType{}) - _ = db.Unmarshaler(&customType{}) -) - type SQLTestSuite struct { suite.Suite @@ -67,12 +62,12 @@ type SQLTestSuite struct { func (s *SQLTestSuite) AfterTest(suiteName, testName string) { err := s.TearDown() - s.NoError(err) + s.Require().NoError(err) } func (s *SQLTestSuite) BeforeTest(suiteName, testName string) { err := s.SetUp() - s.NoError(err) + s.Require().NoError(err) sess := s.Session() @@ -84,7 +79,7 @@ func (s *SQLTestSuite) BeforeTest(suiteName, testName string) { _, err := artist.Insert(map[string]string{ "name": artistName, }) - s.NoError(err) + s.Require().NoError(err) } } @@ -94,14 +89,6 @@ func (s *SQLTestSuite) TestPreparedStatementsCache() { sess.SetPreparedStatementCache(true) defer sess.SetPreparedStatementCache(false) - var tMu sync.Mutex - tFatal := func(err error) { - tMu.Lock() - defer tMu.Unlock() - - s.T().Errorf("tmu: %v", err) - } - // This limit was chosen because, by default, MySQL accepts 16k statements // and dies. See https://github.com/upper/db/issues/287 limit := 20000 @@ -128,9 +115,7 @@ func (s *SQLTestSuite) TestPreparedStatementsCache() { var count map[string]uint64 err := res.One(&count) - if err != nil { - tFatal(err) - } + s.Require().NoError(err) }(i) } wg.Wait() @@ -152,9 +137,7 @@ func (s *SQLTestSuite) TestPreparedStatementsCache() { _, err := sess.Collection("artist").Insert(artistType{ Name: fmt.Sprintf("artist-%d", i), }) - if err != nil { - tFatal(err) - } + s.Require().NoError(err) }(i) } wg.Wait() @@ -169,9 +152,7 @@ func (s *SQLTestSuite) TestPreparedStatementsCache() { Name: fmt.Sprintf("artist-%d", i), } err := sess.Collection("artist").InsertReturning(&artist) - if err != nil { - tFatal(err) - } + s.Require().NoError(err) }(i) } wg.Wait() @@ -184,14 +165,13 @@ func (s *SQLTestSuite) TestTruncateAllCollections() { sess := s.Session() collections, err := sess.Collections() - s.NoError(err) + s.Require().NoError(err) s.True(len(collections) > 0) for _, col := range collections { if ok, _ := col.Exists(); ok { - if err = col.Truncate(); err != nil { - s.NoError(err) - } + err := col.Truncate() + s.Require().NoError(err) } } } @@ -210,10 +190,10 @@ func (s *SQLTestSuite) TestQueryLogger() { sess := s.Session() _, err := sess.Collection("artist").Find().Count() - s.Equal(nil, err) + s.Require().NoError(err) _, err = sess.Collection("artist_x").Find().Count() - s.NotEqual(nil, err) + s.Error(err) } func (s *SQLTestSuite) TestExpectCursorError() { @@ -223,8 +203,8 @@ func (s *SQLTestSuite) TestExpectCursorError() { res := artist.Find(-1) c, err := res.Count() - s.Equal(uint64(0), c) - s.NoError(err) + s.Require().NoError(err) + s.Zero(c) var item map[string]interface{} err = res.One(&item) @@ -241,17 +221,17 @@ func (s *SQLTestSuite) TestInsertDefault() { artist := sess.Collection("artist") err := artist.Truncate() - s.NoError(err) + s.Require().NoError(err) id, err := artist.Insert(&artistType{}) - s.NoError(err) + s.Require().NoError(err) s.NotNil(id) err = artist.Truncate() - s.NoError(err) + s.Require().NoError(err) id, err = artist.Insert(nil) - s.NoError(err) + s.Require().NoError(err) s.NotNil(id) } @@ -261,14 +241,15 @@ func (s *SQLTestSuite) TestInsertReturning() { artist := sess.Collection("artist") err := artist.Truncate() - s.NoError(err) + s.Require().NoError(err) itemMap := map[string]string{ "name": "Ozzie", } s.Zero(itemMap["id"], "Must be zero before inserting") + err = artist.InsertReturning(&itemMap) - s.NoError(err) + s.Require().NoError(err) s.NotZero(itemMap["id"], "Must not be zero after inserting") itemStruct := struct { @@ -279,13 +260,14 @@ func (s *SQLTestSuite) TestInsertReturning() { "Flea", } s.Zero(itemStruct.ID, "Must be zero before inserting") + err = artist.InsertReturning(&itemStruct) - s.NoError(err) + s.Require().NoError(err) s.NotZero(itemStruct.ID, "Must not be zero after inserting") count, err := artist.Find().Count() - s.NoError(err) - s.Equal(uint64(2), count, "Expecting 2 elements") + s.Require().NoError(err) + s.Equal(uint64(2), count) itemStruct2 := struct { ID int `db:"id,omitempty"` @@ -295,6 +277,7 @@ func (s *SQLTestSuite) TestInsertReturning() { "Slash", } s.Zero(itemStruct2.ID, "Must be zero before inserting") + err = artist.InsertReturning(itemStruct2) s.Error(err, "Should not happen, using a pointer should be enforced") s.Zero(itemStruct2.ID, "Must still be zero because there was no insertion") @@ -303,13 +286,14 @@ func (s *SQLTestSuite) TestInsertReturning() { "name": "Janus", } s.Zero(itemMap2["id"], "Must be zero before inserting") + err = artist.InsertReturning(itemMap2) s.Error(err, "Should not happen, using a pointer should be enforced") s.Zero(itemMap2["id"], "Must still be zero because there was no insertion") // Counting elements, must be exactly 2 elements. count, err = artist.Find().Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(2), count, "Expecting 2 elements") } @@ -317,7 +301,7 @@ func (s *SQLTestSuite) TestInsertReturningWithinTransaction() { sess := s.Session() err := sess.Collection("artist").Truncate() - s.NoError(err) + s.Require().NoError(err) err = sess.Tx(func(tx db.Session) error { artist := tx.Collection("artist") @@ -326,8 +310,9 @@ func (s *SQLTestSuite) TestInsertReturningWithinTransaction() { "name": "Ozzie", } s.Zero(itemMap["id"], "Must be zero before inserting") + err = artist.InsertReturning(&itemMap) - s.NoError(err) + s.Require().NoError(err) s.NotZero(itemMap["id"], "Must not be zero after inserting") itemStruct := struct { @@ -338,12 +323,13 @@ func (s *SQLTestSuite) TestInsertReturningWithinTransaction() { "Flea", } s.Zero(itemStruct.ID, "Must be zero before inserting") + err = artist.InsertReturning(&itemStruct) - s.NoError(err) + s.Require().NoError(err) s.NotZero(itemStruct.ID, "Must not be zero after inserting") count, err := artist.Find().Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(2), count, "Expecting 2 elements") itemStruct2 := struct { @@ -354,6 +340,7 @@ func (s *SQLTestSuite) TestInsertReturningWithinTransaction() { "Slash", } s.Zero(itemStruct2.ID, "Must be zero before inserting") + err = artist.InsertReturning(itemStruct2) s.Error(err, "Should not happen, using a pointer should be enforced") s.Zero(itemStruct2.ID, "Must still be zero because there was no insertion") @@ -362,13 +349,14 @@ func (s *SQLTestSuite) TestInsertReturningWithinTransaction() { "name": "Janus", } s.Zero(itemMap2["id"], "Must be zero before inserting") + err = artist.InsertReturning(itemMap2) s.Error(err, "Should not happen, using a pointer should be enforced") s.Zero(itemMap2["id"], "Must still be zero because there was no insertion") // Counting elements, must be exactly 2 elements. count, err = artist.Find().Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(2), count, "Expecting 2 elements") return fmt.Errorf("rolling back for no reason") @@ -377,7 +365,7 @@ func (s *SQLTestSuite) TestInsertReturningWithinTransaction() { // Expecting no elements. count, err := sess.Collection("artist").Find().Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(0), count, "Expecting 0 elements, everything was rolled back!") } @@ -387,14 +375,14 @@ func (s *SQLTestSuite) TestInsertIntoArtistsTable() { artist := sess.Collection("artist") err := artist.Truncate() - s.NoError(err) + s.Require().NoError(err) itemMap := map[string]string{ "name": "Ozzie", } record, err := artist.Insert(itemMap) - s.NoError(err) + s.Require().NoError(err) s.NotNil(record) if pk, ok := record.ID().(int64); !ok || pk == 0 { @@ -409,7 +397,7 @@ func (s *SQLTestSuite) TestInsertIntoArtistsTable() { } record, err = artist.Insert(itemStruct) - s.NoError(err) + s.Require().NoError(err) s.NotNil(record) if pk, ok := record.ID().(int64); !ok || pk == 0 { @@ -424,7 +412,7 @@ func (s *SQLTestSuite) TestInsertIntoArtistsTable() { } record, err = artist.Insert(&itemStruct2) - s.NoError(err) + s.Require().NoError(err) s.NotNil(record) if pk, ok := record.ID().(int64); !ok || pk == 0 { @@ -435,34 +423,34 @@ func (s *SQLTestSuite) TestInsertIntoArtistsTable() { Name: "Janus", } record, err = artist.Insert(&itemStruct3) - s.NoError(err) + s.Require().NoError(err) if s.Adapter() != "ql" { s.NotZero(record) // QL always inserts an ID. } // Counting elements, must be exactly 4 elements. count, err := artist.Find().Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(4), count) count, err = artist.Find(db.Cond{"name": db.Eq("Ozzie")}).Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(1), count) count, err = artist.Find("name", "Ozzie").And("name", "Flea").Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(0), count) count, err = artist.Find(db.Or(db.Cond{"name": "Ozzie"}, db.Cond{"name": "Flea"})).Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(2), count) count, err = artist.Find(db.And(db.Cond{"name": "Ozzie"}, db.Cond{"name": "Flea"})).Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(0), count) count, err = artist.Find(db.Cond{"name": "Ozzie"}).And(db.Cond{"name": "Flea"}).Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(0), count) } @@ -483,13 +471,13 @@ func (s *SQLTestSuite) TestGetOneResult() { _, err := artist.Insert(map[string]string{ "name": fmt.Sprintf("Artist %d", i), }) - s.NoError(err) + s.Require().NoError(err) } // Fetching one struct. var someArtist artistType err := artist.Find().Limit(1).One(&someArtist) - s.NoError(err) + s.Require().NoError(err) s.NotZero(someArtist.Name) if s.Adapter() != "ql" { @@ -499,7 +487,7 @@ func (s *SQLTestSuite) TestGetOneResult() { // Fetching a pointer to a pointer. var someArtistObj *artistType err = artist.Find().Limit(1).One(&someArtistObj) - s.NoError(err) + s.Require().NoError(err) s.NotZero(someArtist.Name) if s.Adapter() != "ql" { s.NotZero(someArtist.ID) @@ -514,7 +502,7 @@ func (s *SQLTestSuite) TestGetWithOffset() { // Fetching one struct. var artists []artistType err := artist.Find().Offset(1).All(&artists) - s.NoError(err) + s.Require().NoError(err) s.Equal(3, len(artists)) } @@ -529,17 +517,17 @@ func (s *SQLTestSuite) TestGetResultsOneByOne() { res := artist.Find() err := res.Err() - s.NoError(err) + s.Require().NoError(err) for res.Next(&rowMap) { s.NotZero(rowMap["id"]) s.NotZero(rowMap["name"]) } err = res.Err() - s.NoError(err) + s.Require().NoError(err) err = res.Close() - s.NoError(err) + s.Require().NoError(err) // Dumping into a tagged struct. rowStruct2 := struct { @@ -554,10 +542,10 @@ func (s *SQLTestSuite) TestGetResultsOneByOne() { s.NotZero(rowStruct2.Value2) } err = res.Err() - s.NoError(err) + s.Require().NoError(err) err = res.Close() - s.NoError(err) + s.Require().NoError(err) // Dumping into a slice of maps. allRowsMap := []map[string]interface{}{} @@ -565,7 +553,7 @@ func (s *SQLTestSuite) TestGetResultsOneByOne() { res = artist.Find() err = res.All(&allRowsMap) - s.NoError(err) + s.Require().NoError(err) s.Equal(4, len(allRowsMap)) for _, singleRowMap := range allRowsMap { @@ -601,7 +589,7 @@ func (s *SQLTestSuite) TestGetResultsOneByOne() { res = artist.Find() err = res.All(&allRowsStruct2) - s.NoError(err) + s.Require().NoError(err) s.Equal(4, len(allRowsStruct2)) @@ -616,7 +604,7 @@ func (s *SQLTestSuite) TestGetAllResults() { artist := sess.Collection("artist") total, err := artist.Find().Count() - s.NoError(err) + s.Require().NoError(err) s.NotZero(total) // Fetching all artists into struct @@ -625,7 +613,7 @@ func (s *SQLTestSuite) TestGetAllResults() { res := artist.Find() err = res.All(&artists) - s.NoError(err) + s.Require().NoError(err) s.Equal(len(artists), int(total)) s.NotZero(artists[0].Name) @@ -636,7 +624,7 @@ func (s *SQLTestSuite) TestGetAllResults() { res = artist.Find() err = res.All(&artistObjs) - s.NoError(err) + s.Require().NoError(err) s.Equal(len(artistObjs), int(total)) s.NotZero(artistObjs[0].Name) @@ -661,7 +649,7 @@ func (s *SQLTestSuite) TestInlineStructs() { review := sess.Collection("review") err := review.Truncate() - s.NoError(err) + s.Require().NoError(err) rec := reviewType{ PublicationID: 123, @@ -681,7 +669,7 @@ func (s *SQLTestSuite) TestInlineStructs() { rec.Details.Created = createdAt record, err := review.Insert(rec) - s.NoError(err) + s.Require().NoError(err) s.NotZero(record.ID().(int64)) rec.ID = record.ID().(int64) @@ -690,7 +678,7 @@ func (s *SQLTestSuite) TestInlineStructs() { res := review.Find() err = res.One(&recChk) - s.NoError(err) + s.Require().NoError(err) s.Equal(rec, recChk) } @@ -703,7 +691,7 @@ func (s *SQLTestSuite) TestUpdate() { _, err := artist.Insert(map[string]string{ "name": "Ozzie", }) - s.NoError(err) + s.Require().NoError(err) // Defining destination struct value := struct { @@ -719,7 +707,7 @@ func (s *SQLTestSuite) TestUpdate() { res := artist.Find(cond).Limit(1) err = res.One(&value) - s.NoError(err) + s.Require().NoError(err) res = artist.Find(value.ID) @@ -729,11 +717,11 @@ func (s *SQLTestSuite) TestUpdate() { } err = res.Update(rowMap) - s.NoError(err) + s.Require().NoError(err) // Pulling it again. err = res.One(&value) - s.NoError(err) + s.Require().NoError(err) // Verifying. s.Equal(value.Name, rowMap["name"]) @@ -747,7 +735,7 @@ func (s *SQLTestSuite) TestUpdate() { // Pulling it again. err = res.One(&value) - s.NoError(err) + s.Require().NoError(err) // Verifying. s.Equal(value.Name, strings.ToLower(rowMap["name"].(string))) @@ -761,7 +749,7 @@ func (s *SQLTestSuite) TestUpdate() { // Pulling it again. err = res.One(&value) - s.NoError(err) + s.Require().NoError(err) // Verifying. s.Equal(value.Name, strings.ToUpper(rowMap["name"].(string))) @@ -775,7 +763,7 @@ func (s *SQLTestSuite) TestUpdate() { // Pulling it again. err = res.One(&value) - s.NoError(err) + s.Require().NoError(err) // Verifying. s.Equal(value.Name, strings.ToLower(rowMap["name"].(string))) @@ -787,11 +775,11 @@ func (s *SQLTestSuite) TestUpdate() { }{strings.ToLower(value.Name)} err = res.Update(rowStruct) - s.NoError(err) + s.Require().NoError(err) // Pulling it again. err = res.One(&value) - s.NoError(err) + s.Require().NoError(err) // Verifying s.Equal(value.Name, rowStruct.Name) @@ -802,11 +790,11 @@ func (s *SQLTestSuite) TestUpdate() { }{"john"} err = res.Update(rowStruct2) - s.NoError(err) + s.Require().NoError(err) // Pulling it again. err = res.One(&value) - s.NoError(err) + s.Require().NoError(err) // Verifying s.Equal(value.Name, rowStruct2.Value1) @@ -817,11 +805,11 @@ func (s *SQLTestSuite) TestUpdate() { }{"anderson"} err = res.Update(rowStruct3) - s.NoError(err) + s.Require().NoError(err) // Pulling it again. err = res.One(&value) - s.NoError(err) + s.Require().NoError(err) // Verifying s.Equal(value.Name, rowStruct3.Value1) @@ -844,10 +832,10 @@ func (s *SQLTestSuite) TestFunction() { res := artist.Find(cond) err := res.One(&rowStruct) - s.NoError(err) + s.Require().NoError(err) total, err := res.Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(4), total) // Testing conditions @@ -858,29 +846,29 @@ func (s *SQLTestSuite) TestFunction() { res = artist.Find(cond) err = res.One(&rowStruct) - s.NoError(err) + s.Require().NoError(err) total, err = res.Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(4), total) res = artist.Find().Select("name") var rowMap map[string]interface{} err = res.One(&rowMap) - s.NoError(err) + s.Require().NoError(err) total, err = res.Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(4), total) res = artist.Find().Select("name") err = res.One(&rowMap) - s.NoError(err) + s.Require().NoError(err) total, err = res.Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(4), total) } @@ -898,7 +886,7 @@ func (s *SQLTestSuite) TestNullableFields() { col := sess.Collection(`data_types`) err := col.Truncate() - s.NoError(err) + s.Require().NoError(err) // Testing insertion of invalid nulls. test := testType{ @@ -909,11 +897,11 @@ func (s *SQLTestSuite) TestNullableFields() { } id, err := col.Insert(testType{}) - s.NoError(err) + s.Require().NoError(err) // Testing fetching of invalid nulls. err = col.Find(id).One(&test) - s.NoError(err) + s.Require().NoError(err) s.False(test.NullInt64Test.Valid) s.False(test.NullFloat64Test.Valid) @@ -928,11 +916,11 @@ func (s *SQLTestSuite) TestNullableFields() { } id, err = col.Insert(test) - s.NoError(err) + s.Require().NoError(err) // Testing fetching of valid nulls. err = col.Find(id).One(&test) - s.NoError(err) + s.Require().NoError(err) s.True(test.NullInt64Test.Valid) s.True(test.NullBoolTest.Valid) @@ -950,13 +938,13 @@ func (s *SQLTestSuite) TestGroup() { stats := sess.Collection("stats_test") err := stats.Truncate() - s.NoError(err) + s.Require().NoError(err) // Adding row append. for i := 0; i < 100; i++ { numeric, value := rand.Intn(5), rand.Intn(100) _, err := stats.Insert(statsType{numeric, value}) - s.NoError(err) + s.Require().NoError(err) } // Testing GROUP BY @@ -969,7 +957,7 @@ func (s *SQLTestSuite) TestGroup() { var results []map[string]interface{} err = res.All(&results) - s.NoError(err) + s.Require().NoError(err) s.Equal(5, len(results)) } @@ -981,14 +969,14 @@ func (s *SQLTestSuite) TestInsertAndDelete() { res := artist.Find() total, err := res.Count() - s.NoError(err) + s.Require().NoError(err) s.Greater(total, uint64(0)) err = res.Delete() - s.NoError(err) + s.Require().NoError(err) total, err = res.Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(0), total) } @@ -1011,7 +999,7 @@ func (s *SQLTestSuite) TestCompositeKeys() { } id, err := compositeKeys.Insert(&item) - s.NoError(err) + s.Require().NoError(err) s.NotZero(id) var item2 itemWithCompoundKey @@ -1019,7 +1007,7 @@ func (s *SQLTestSuite) TestCompositeKeys() { // Finding by ID err = compositeKeys.Find(id).One(&item2) - s.NoError(err) + s.Require().NoError(err) s.Equal(item2.SomeVal, item.SomeVal) } @@ -1034,7 +1022,7 @@ func (s *SQLTestSuite) TestCompositeKeys() { } err := compositeKeys.InsertReturning(&item) - s.NoError(err) + s.Require().NoError(err) } } @@ -1049,24 +1037,24 @@ func (s *SQLTestSuite) TestTransactionsAndRollback() { err := sess.Tx(func(tx db.Session) error { artist := tx.Collection("artist") err := artist.Truncate() - s.NoError(err) + s.Require().NoError(err) _, err = artist.Insert(artistType{1, "First"}) - s.NoError(err) + s.Require().NoError(err) return nil }) - s.NoError(err) + s.Require().NoError(err) err = sess.Tx(func(tx db.Session) error { artist := tx.Collection("artist") _, err = artist.Insert(artistType{2, "Second"}) - s.NoError(err) + s.Require().NoError(err) // Won't fail. _, err = artist.Insert(artistType{3, "Third"}) - s.NoError(err) + s.Require().NoError(err) // Will fail. _, err = artist.Insert(artistType{1, "Duplicated"}) @@ -1080,7 +1068,7 @@ func (s *SQLTestSuite) TestTransactionsAndRollback() { artist := sess.Collection("artist") count, err := artist.Find().Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(1), count) err = sess.Tx(func(tx db.Session) error { @@ -1088,11 +1076,11 @@ func (s *SQLTestSuite) TestTransactionsAndRollback() { // Won't fail. _, err = artist.Insert(artistType{2, "Second"}) - s.NoError(err) + s.Require().NoError(err) // Won't fail. _, err = artist.Insert(artistType{3, "Third"}) - s.NoError(err) + s.Require().NoError(err) return fmt.Errorf("rollback for no reason") }) @@ -1102,7 +1090,7 @@ func (s *SQLTestSuite) TestTransactionsAndRollback() { artist = sess.Collection("artist") count, err = artist.Find().Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(1), count) // Attempt to add some rows. @@ -1111,21 +1099,21 @@ func (s *SQLTestSuite) TestTransactionsAndRollback() { // Won't fail. _, err = artist.Insert(artistType{2, "Second"}) - s.NoError(err) + s.Require().NoError(err) // Won't fail. _, err = artist.Insert(artistType{3, "Third"}) - s.NoError(err) + s.Require().NoError(err) return nil }) - s.NoError(err) + s.Require().NoError(err) // Let's verify we have 3 rows. artist = sess.Collection("artist") count, err = artist.Find().Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(3), count) } @@ -1168,7 +1156,7 @@ func (s *SQLTestSuite) TestDataTypes() { // Removing all data. err := dataTypes.Truncate() - s.NoError(err) + s.Require().NoError(err) testTimeZone := time.Local switch s.Adapter() { @@ -1202,7 +1190,7 @@ func (s *SQLTestSuite) TestDataTypes() { int64(time.Second * time.Duration(7331)), } id, err := dataTypes.Insert(testValues) - s.NoError(err) + s.Require().NoError(err) s.NotNil(id) // Defining our set. @@ -1213,14 +1201,14 @@ func (s *SQLTestSuite) TestDataTypes() { res := dataTypes.Find(cond) count, err := res.Count() - s.NoError(err) + s.Require().NoError(err) s.NotZero(count) // Trying to dump the subject into an empty structure of the same type. var item testValuesStruct err = res.One(&item) - s.NoError(err) + s.Require().NoError(err) s.NotNil(item.DateD) s.NotNil(item.Date) @@ -1243,7 +1231,7 @@ func (s *SQLTestSuite) TestUpdateWithNullColumn() { artist := sess.Collection("artist") err := artist.Truncate() - s.NoError(err) + s.Require().NoError(err) type Artist struct { ID int64 `db:"id,omitempty"` @@ -1252,21 +1240,21 @@ func (s *SQLTestSuite) TestUpdateWithNullColumn() { name := "José" id, err := artist.Insert(Artist{0, &name}) - s.NoError(err) + s.Require().NoError(err) var item Artist err = artist.Find(id).One(&item) - s.NoError(err) + s.Require().NoError(err) s.NotEqual(nil, item.Name) s.Equal(name, *item.Name) err = artist.Find(id).Update(Artist{Name: nil}) - s.NoError(err) + s.Require().NoError(err) var item2 Artist err = artist.Find(id).One(&item2) - s.NoError(err) + s.Require().NoError(err) s.Equal((*string)(nil), item2.Name) } @@ -1276,7 +1264,7 @@ func (s *SQLTestSuite) TestBatchInsert() { for batchSize := 0; batchSize < 17; batchSize++ { err := sess.Collection("artist").Truncate() - s.NoError(err) + s.Require().NoError(err) q := sess.SQL().InsertInto("artist").Columns("name") @@ -1299,16 +1287,16 @@ func (s *SQLTestSuite) TestBatchInsert() { }() err = batch.Wait() - s.NoError(err) - s.NoError(batch.Err()) + s.Require().NoError(err) + s.Require().NoError(batch.Err()) c, err := sess.Collection("artist").Find().Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(totalItems), c) for i := 0; i < totalItems; i++ { c, err := sess.Collection("artist").Find(db.Cond{"name": fmt.Sprintf("artist-%d", i)}).Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(1), c) } } @@ -1319,7 +1307,7 @@ func (s *SQLTestSuite) TestBatchInsertNoColumns() { for batchSize := 0; batchSize < 17; batchSize++ { err := sess.Collection("artist").Truncate() - s.NoError(err) + s.Require().NoError(err) batch := sess.SQL().InsertInto("artist").Batch(batchSize) @@ -1336,16 +1324,16 @@ func (s *SQLTestSuite) TestBatchInsertNoColumns() { }() err = batch.Wait() - s.NoError(err) - s.NoError(batch.Err()) + s.Require().NoError(err) + s.Require().NoError(batch.Err()) c, err := sess.Collection("artist").Find().Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(totalItems), c) for i := 0; i < totalItems; i++ { c, err := sess.Collection("artist").Find(db.Cond{"name": fmt.Sprintf("artist-%d", i)}).Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(1), c) } } @@ -1363,7 +1351,7 @@ func (s *SQLTestSuite) TestBatchInsertReturningKeys() { sess := s.Session() err := sess.Collection("artist").Truncate() - s.NoError(err) + s.Require().NoError(err) batchSize, totalItems := 7, 12 @@ -1392,14 +1380,14 @@ func (s *SQLTestSuite) TestBatchInsertReturningKeys() { // Make sure count matches. c, err := sess.Collection("artist").Find(db.Cond{"id": keys}).Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(len(keyMap)), c) } - s.NoError(batch.Err()) + s.Require().NoError(batch.Err()) // Count all new elements c, err := sess.Collection("artist").Find().Count() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(totalItems), c) } @@ -1407,7 +1395,7 @@ func (s *SQLTestSuite) TestPaginator() { sess := s.Session() err := sess.Collection("artist").Truncate() - s.NoError(err) + s.Require().NoError(err) batch := sess.SQL().InsertInto("artist").Batch(100) @@ -1422,8 +1410,8 @@ func (s *SQLTestSuite) TestPaginator() { }() err = batch.Wait() - s.NoError(err) - s.NoError(batch.Err()) + s.Require().NoError(err) + s.Require().NoError(batch.Err()) q := sess.SQL().SelectFrom("artist") if s.Adapter() == "ql" { @@ -1437,44 +1425,44 @@ func (s *SQLTestSuite) TestPaginator() { var zerothPage []artistType err = paginator.Page(0).All(&zerothPage) - s.NoError(err) + s.Require().NoError(err) s.Equal(pageSize, len(zerothPage)) var firstPage []artistType err = paginator.Page(1).All(&firstPage) - s.NoError(err) + s.Require().NoError(err) s.Equal(pageSize, len(firstPage)) s.Equal(zerothPage, firstPage) var secondPage []artistType err = paginator.Page(2).All(&secondPage) - s.NoError(err) + s.Require().NoError(err) s.Equal(pageSize, len(secondPage)) totalPages, err := paginator.TotalPages() - s.NoError(err) + s.Require().NoError(err) s.NotZero(totalPages) s.Equal(uint(77), totalPages) totalEntries, err := paginator.TotalEntries() - s.NoError(err) + s.Require().NoError(err) s.NotZero(totalEntries) s.Equal(uint64(999), totalEntries) var lastPage []artistType err = paginator.Page(totalPages).All(&lastPage) - s.NoError(err) + s.Require().NoError(err) s.Equal(11, len(lastPage)) var beyondLastPage []artistType err = paginator.Page(totalPages + 1).All(&beyondLastPage) - s.NoError(err) + s.Require().NoError(err) s.Equal(0, len(beyondLastPage)) var hundredthPage []artistType err = paginator.Page(100).All(&hundredthPage) - s.NoError(err) + s.Require().NoError(err) s.Equal(0, len(hundredthPage)) for i := uint(0); i < totalPages; i++ { @@ -1485,7 +1473,7 @@ func (s *SQLTestSuite) TestPaginator() { if err != nil { s.T().Errorf("%v", err) } - s.NoError(err) + s.Require().NoError(err) if len(items) < 1 { s.Equal(totalPages+1, i) break @@ -1522,7 +1510,7 @@ func (s *SQLTestSuite) TestPaginator() { var items []artistType err := current.All(&items) - s.NoError(err) + s.Require().NoError(err) if len(items) < 1 { s.Equal(uint(0), i) @@ -1549,12 +1537,12 @@ func (s *SQLTestSuite) TestPaginator() { count, err := resultPaginator.TotalPages() s.Equal(uint(67), count) - s.NoError(err) + s.Require().NoError(err) var items []artistType fifthPage := 5 err = resultPaginator.Page(uint(fifthPage)).All(&items) - s.NoError(err) + s.Require().NoError(err) for j := 0; j < len(items); j++ { s.Equal(fmt.Sprintf("artist-%d", int(fifteenResults)*(fifthPage-1)+j), items[j].Name) @@ -1565,7 +1553,7 @@ func (s *SQLTestSuite) TestPaginator() { var items []artistType err = resultPaginator.All(&items) - s.NoError(err) + s.Require().NoError(err) if len(items) < 1 { break @@ -1582,7 +1570,7 @@ func (s *SQLTestSuite) TestPaginator() { var items []artistType err = resultPaginator.All(&items) - s.NoError(err) + s.Require().NoError(err) if len(items) < 1 { s.Equal(uint(0), i) @@ -1601,16 +1589,16 @@ func (s *SQLTestSuite) TestPaginator() { paginator := q.Paginate(0) totalPages, err := paginator.TotalPages() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint(1), totalPages) totalEntries, err := paginator.TotalEntries() - s.NoError(err) + s.Require().NoError(err) s.Equal(uint64(999), totalEntries) var allItems []artistType err = paginator.Page(0).All(&allItems) - s.NoError(err) + s.Require().NoError(err) s.Equal(totalEntries, uint64(len(allItems))) } @@ -1620,7 +1608,7 @@ func (s *SQLTestSuite) TestPaginator_Issue607() { sess := s.Session() err := sess.Collection("artist").Truncate() - s.NoError(err) + s.Require().NoError(err) // Add first batch { @@ -1637,19 +1625,19 @@ func (s *SQLTestSuite) TestPaginator_Issue607() { }() err = batch.Wait() - s.NoError(err) - s.NoError(batch.Err()) + s.Require().NoError(err) + s.Require().NoError(batch.Err()) } artists := []*artistType{} paginator := sess.SQL().Select("name").From("artist").Paginate(10) err = paginator.Page(1).All(&artists) - s.NoError(err) + s.Require().NoError(err) { totalPages, err := paginator.TotalPages() - s.NoError(err) + s.Require().NoError(err) s.NotZero(totalPages) s.Equal(uint(5), totalPages) } @@ -1669,13 +1657,13 @@ func (s *SQLTestSuite) TestPaginator_Issue607() { }() err = batch.Wait() - s.NoError(err) - s.NoError(batch.Err()) + s.Require().NoError(err) + s.Require().NoError(batch.Err()) } { totalPages, err := paginator.TotalPages() - s.NoError(err) + s.Require().NoError(err) s.NotZero(totalPages) s.Equal(uint(10), totalPages, "expect number of pages to change") } @@ -1690,11 +1678,11 @@ func (s *SQLTestSuite) TestPaginator_Issue607() { paginator = sess.SQL().Select("name").From("artist").Where(cond).Paginate(10) err = paginator.Page(1).All(&artists) - s.NoError(err) + s.Require().NoError(err) { totalPages, err := paginator.TotalPages() - s.NoError(err) + s.Require().NoError(err) s.NotZero(totalPages) s.Equal(uint(5), totalPages, "expect same 5 pages from the first batch") } @@ -1707,18 +1695,18 @@ func (s *SQLTestSuite) TestSession() { var all []map[string]interface{} err := sess.Collection("artist").Truncate() - s.NoError(err) + s.Require().NoError(err) _, err = sess.SQL().InsertInto("artist").Values(struct { Name string `db:"name"` }{"Rinko Kikuchi"}).Exec() - s.NoError(err) + s.Require().NoError(err) // Using explicit iterator. iter := sess.SQL().SelectFrom("artist").Iterator() err = iter.All(&all) - s.NoError(err) + s.Require().NoError(err) s.NotZero(all) // Using explicit iterator to fetch one item. @@ -1726,7 +1714,7 @@ func (s *SQLTestSuite) TestSession() { iter = sess.SQL().SelectFrom("artist").Iterator() err = iter.One(&item) - s.NoError(err) + s.Require().NoError(err) s.NotZero(item) // Using explicit iterator and NextScan. @@ -1741,10 +1729,10 @@ func (s *SQLTestSuite) TestSession() { err = iter.NextScan(&id, &name) } - s.NoError(err) + s.Require().NoError(err) s.NotZero(id) s.NotEmpty(name) - s.NoError(iter.Close()) + s.Require().NoError(iter.Close()) err = iter.NextScan(&id, &name) s.Error(err) @@ -1759,7 +1747,7 @@ func (s *SQLTestSuite) TestSession() { err = iter.ScanOne(&id, &name) } - s.NoError(err) + s.Require().NoError(err) s.NotZero(id) s.NotEmpty(name) @@ -1789,7 +1777,7 @@ func (s *SQLTestSuite) TestSession() { q := sess.SQL().SelectFrom("artist") err = q.All(&all) - s.NoError(err) + s.Require().NoError(err) s.NotZero(all) err = sess.Tx(func(tx db.Session) error { @@ -1797,13 +1785,13 @@ func (s *SQLTestSuite) TestSession() { s.NotZero(iter) err = q.All(&all) - s.NoError(err) + s.Require().NoError(err) s.NotZero(all) return nil }) - s.NoError(err) + s.Require().NoError(err) } func (s *SQLTestSuite) TestExhaustConnectionPool() { @@ -1855,7 +1843,7 @@ func (s *SQLTestSuite) TestExhaustConnectionPool() { s.Error(err) s.True(errors.Is(err, errRolledBack)) } else { - s.NoError(err) + s.Require().NoError(err) } }(&wg, i) } @@ -1870,17 +1858,17 @@ func (s *SQLTestSuite) TestCustomType() { artist := sess.Collection("artist") err := artist.Truncate() - s.NoError(err) + s.Require().NoError(err) id, err := artist.Insert(artistWithCustomType{ Custom: customType{Val: []byte("some name")}, }) - s.NoError(err) + s.Require().NoError(err) s.NotNil(id) var bar artistWithCustomType err = artist.Find(id).One(&bar) - s.NoError(err) + s.Require().NoError(err) s.Equal("foo: some name", string(bar.Custom.Val)) } @@ -1890,7 +1878,7 @@ func (s *SQLTestSuite) Test_Issue565() { Name: "Lucy", Born: time.Now(), }) - s.NoError(err) + s.Require().NoError(err) ctxKeyCarry := db.ContextKey("carry") @@ -1938,7 +1926,7 @@ func (s *SQLTestSuite) Test_Issue565() { var result birthday err := sess.Collection("birthdays").Find().Select("name").One(&result) - s.NoError(err) + s.Require().NoError(err) s.NotZero(result.Name) s.NotZero(ctx.Value(ctxKeyCarry)) @@ -1956,7 +1944,7 @@ func (s *SQLTestSuite) TestSelectFromSubquery() { }), ).As("_q") err := q.All(&artists) - s.NoError(err) + s.Require().NoError(err) s.NotZero(len(artists)) } @@ -1969,9 +1957,13 @@ func (s *SQLTestSuite) TestSelectFromSubquery() { }), ).As("_q") err := q.All(&artists) - s.NoError(err) + s.Require().NoError(err) s.NotZero(len(artists)) } - } + +var ( + _ = db.Marshaler(&customType{}) + _ = db.Unmarshaler(&customType{}) +)