Skip to content

Commit

Permalink
add integration test for postgres:10/integer data types
Browse files Browse the repository at this point in the history
- support multiple SQL data files
- the output directory is a temporary one
- make the tests running in parallel

add integration test for postgres:10/int2
add integration test for postgres:10/int4
add integration test for postgres:10/int8
add integration test for postgres:10/smallint
add integration test for postgres:10/integer
add integration test for postgres:10/bigint
add integration test for postgres:10/smallserial
add integration test for postgres:10/serial
add integration test for postgres:10/bigserial
add integration test for postgres:10/serial2
add integration test for postgres:10/serial4
add integration test for postgres:10/serial8
  • Loading branch information
fraenky8 committed Feb 25, 2020
1 parent 09bc032 commit dc0b516
Show file tree
Hide file tree
Showing 185 changed files with 2,162 additions and 68 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ _testmain.go

.DS_Store

test/*/output
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ install: ## Installs tables-to-go. Same behavior like `go install
go install -mod=vendor .

test:
go test -race ./...
go test -mod=vendor -race ./...

integration-test:
go test -race -tags=integration ./...
go test -mod=vendor -race -tags=integration ./...

sqlite3: ## Installs tables-to-go with sqlite3 driver and the \
## User Authentication feature enabled. \
Expand Down
72 changes: 50 additions & 22 deletions pkg/database/postgresql.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,19 @@ import (
_ "github.com/lib/pq"
)

// Postgresql implemenmts the Database interface with help of generalDatabase
const (
PostgresqlColumnTypePrimaryKey = "PRIMARY KEY"
PostgresqlColumnTypeAutoIncrement = "nextval"
PostgresqlColumnTypeSerial = "serial"
)

// Postgresql implements the Database interface with help of generalDatabase
type Postgresql struct {
*GeneralDatabase

defaultUserName string

integerDataTypes map[string]struct{}
}

// NewPostgresql creates a new Postgresql database
Expand All @@ -25,6 +33,21 @@ func NewPostgresql(s *settings.Settings) *Postgresql {
driver: dbTypeToDriverMap[s.DbType],
},
defaultUserName: "postgres",

integerDataTypes: map[string]struct{}{
"smallint": {},
"int2": {},
"integer": {},
"int4": {},
"bigint": {},
"int8": {},
"smallserial": {},
"serial2": {},
"serial": {},
"serial4": {},
"bigserial": {},
"serial8": {},
},
}
}

Expand All @@ -43,7 +66,7 @@ func (pg *Postgresql) DSN() string {
pg.Settings.Host, pg.Settings.Port, user, pg.Settings.DbName, pg.Settings.Pswd)
}

// GetDriverImportLibrary returns the golang sql driver specific fot the MySQL database
// GetDriverImportLibrary returns the golang sql driver specific fot the Postgres database
func (pg *Postgresql) GetDriverImportLibrary() string {
return "pg \"github.com/lib/pq\""
}
Expand Down Expand Up @@ -115,15 +138,15 @@ func (pg *Postgresql) GetColumnsOfTable(table *Table) (err error) {

// IsPrimaryKey checks if column belongs to primary key
func (pg *Postgresql) IsPrimaryKey(column Column) bool {
return strings.Contains(column.ConstraintType.String, "PRIMARY KEY")
return strings.Contains(column.ConstraintType.String, PostgresqlColumnTypePrimaryKey)
}

// IsAutoIncrement checks if column is a serial column
func (pg *Postgresql) IsAutoIncrement(column Column) bool {
return strings.Contains(column.DefaultValue.String, "nextval")
return strings.Contains(column.DefaultValue.String, PostgresqlColumnTypeAutoIncrement)
}

// GetStringDatatypes returns the string datatypes for the postgre database
// GetStringDatatypes returns the string datatypes for the Postgres database
func (pg *Postgresql) GetStringDatatypes() []string {
return []string{
"character varying",
Expand All @@ -133,56 +156,61 @@ func (pg *Postgresql) GetStringDatatypes() []string {
}
}

// IsString returns true if colum is of type string for the postgre database
// IsString returns true if column is of type string for the Postgres database
func (pg *Postgresql) IsString(column Column) bool {
return pg.IsStringInSlice(column.DataType, pg.GetStringDatatypes())
}

// GetTextDatatypes returns the text datatypes for the postgre database
// GetTextDatatypes returns the text datatypes for the Postgres database
func (pg *Postgresql) GetTextDatatypes() []string {
return []string{
"text",
}
}

// IsText returns true if colum is of type text for the postgre database
// IsText returns true if column is of type text for the Postgres database
func (pg *Postgresql) IsText(column Column) bool {
return pg.IsStringInSlice(column.DataType, pg.GetTextDatatypes())
}

// GetIntegerDatatypes returns the integer datatypes for the postgre database
// GetIntegerDatatypes returns the integer datatypes for the Postgres database
// TODO remove these methods
func (pg *Postgresql) GetIntegerDatatypes() []string {
return []string{
"smallint",
"integer",
"bigint",
"smallserial",
"serial",
"bigserial",
"smallint", "int2",
"integer", "int4",
"bigint", "int8",
"smallserial", "serial2",
"serial", "serial4",
"bigserial", "serial8",
}
}

// IsInteger returns true if colum is of type integer for the postgre database
// IsInteger returns true if column is of type integer for the Postgres database
func (pg *Postgresql) IsInteger(column Column) bool {
return pg.IsStringInSlice(column.DataType, pg.GetIntegerDatatypes())
_, ok := pg.integerDataTypes[column.DataType]
return ok
}

// GetFloatDatatypes returns the float datatypes for the postgre database
// GetFloatDatatypes returns the float datatypes for the Postgres database
func (pg *Postgresql) GetFloatDatatypes() []string {
return []string{
"float",
"float4",
"float8",
"numeric",
"decimal",
"real",
"double precision",
}
}

// IsFloat returns true if colum is of type float for the postgre database
// IsFloat returns true if column is of type float for the Postgres database
func (pg *Postgresql) IsFloat(column Column) bool {
return pg.IsStringInSlice(column.DataType, pg.GetFloatDatatypes())
}

// GetTemporalDatatypes returns the temporal datatypes for the postgre database
// GetTemporalDatatypes returns the temporal datatypes for the Postgres database
func (pg *Postgresql) GetTemporalDatatypes() []string {
return []string{
"time",
Expand All @@ -195,12 +223,12 @@ func (pg *Postgresql) GetTemporalDatatypes() []string {
}
}

// IsTemporal returns true if colum is of type temporal for the postgre database
// IsTemporal returns true if column is of type temporal for the Postgres database
func (pg *Postgresql) IsTemporal(column Column) bool {
return pg.IsStringInSlice(column.DataType, pg.GetTemporalDatatypes())
}

// GetTemporalDriverDataType returns the time data type specific for the postgre database
// GetTemporalDriverDataType returns the time data type specific for the Postgres database
func (pg *Postgresql) GetTemporalDriverDataType() string {
return "pg.NullTime"
}
117 changes: 74 additions & 43 deletions test/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"os"
"path/filepath"
"sort"
"strings"
"testing"
"time"

Expand All @@ -24,13 +23,6 @@ import (
"github.com/fraenky8/tables-to-go/pkg/settings"
)

type cliWriter struct{}

func (c cliWriter) Write(tableName string, content string) error {
_, err := fmt.Println(content)
return err
}

type dbSettings struct {
*settings.Settings

Expand Down Expand Up @@ -80,8 +72,37 @@ func TestIntegration(t *testing.T) {
dockerImage: "mysql",
version: "8",
env: []string{
"MYSQL_DATABASE=public",
"MYSQL_ROOT_PASSWORD=mysecretpassword",
"MYSQL_DATABASE=" + s.DbName,
"MYSQL_ROOT_PASSWORD=" + s.Pswd,
},
Settings: s,
}

dbs.setSettings(s)

return dbs
}(),
},
{
desc: "postgres 10",
settings: func() *dbSettings {
s := settings.New()
s.DbType = settings.DbTypePostgresql
s.User = "postgres"
s.Pswd = "mysecretpassword"
s.DbName = "postgres"
s.Schema = "public"
s.Host = "localhost"
s.Port = "5432"
//s.Verbose = true
//s.VVerbose = true

dbs := &dbSettings{
dockerImage: "postgres",
version: "10",
env: []string{
"POSTGRES_DB=" + s.DbName,
"POSTGRES_PASSWORD=" + s.Pswd,
},
Settings: s,
}
Expand All @@ -94,7 +115,10 @@ func TestIntegration(t *testing.T) {
}

for _, test := range tests {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()

s := test.settings

db, purgeFn, err := setupDatabase(s)
Expand All @@ -114,15 +138,17 @@ func TestIntegration(t *testing.T) {

// TODO need flag for not removing generated output but
// save it into the expected directory
_ = os.RemoveAll(s.Settings.OutputFilePath)
_ = os.MkdirAll(s.Settings.OutputFilePath, 0755)
if !t.Failed() {
_ = os.RemoveAll(s.Settings.OutputFilePath)
}
}()

writer := output.NewFileWriter(s.Settings.OutputFilePath)
//writer := cliWriter{}
err = os.MkdirAll(s.Settings.OutputFilePath, 0755)
if err != nil {
t.Fatalf("could not create output file path: %v", err)
}

prefix := strings.Title(s.imageName())
s.Settings.Prefix = prefix + "_"
writer := output.NewFileWriter(s.Settings.OutputFilePath)

err = cli.Run(s.Settings, db, writer)
assert.NoError(t, err)
Expand All @@ -134,28 +160,27 @@ func TestIntegration(t *testing.T) {

func checkFiles(t *testing.T, s *dbSettings) {
expectedPattern := filepath.Join(s.getExceptedFilepath(), s.Settings.Prefix+"*")
expected, err := filepath.Glob(expectedPattern)
expectedFiles, err := filepath.Glob(expectedPattern)
assert.NoError(t, err)

actualPattern := filepath.Join(s.Settings.OutputFilePath, s.Settings.Prefix+"*")
actual, err := filepath.Glob(actualPattern)
actualFiles, err := filepath.Glob(actualPattern)
assert.NoError(t, err)

if len(expected) != len(actual) {
if len(expectedFiles) != len(actualFiles) {
t.Fatalf("expected and actual files differ in length: %v (%d) vs. %v (%d)",
expected, len(expected), actual, len(actual))
expectedFiles, len(expectedFiles), actualFiles, len(actualFiles))
}

sort.Strings(expected)
sort.Strings(actual)
sort.Strings(expectedFiles)
sort.Strings(actualFiles)

for i, ef := range expected {
af := actual[i]
f1, err := ioutil.ReadFile(ef)
for i := range expectedFiles {
expectedFile, err := ioutil.ReadFile(expectedFiles[i])
assert.NoError(t, err)
f2, err := ioutil.ReadFile(af)
actualFile, err := ioutil.ReadFile(actualFiles[i])
assert.NoError(t, err)
assert.True(t, bytes.Equal(f1, f2))
assert.Equal(t, expectedFile, actualFile)
}
}

Expand All @@ -165,13 +190,13 @@ func setupDatabase(settings *dbSettings) (database.Database, func() error, error
if err != nil {
return nil, nil, fmt.Errorf("error connecting to Docker: %v", err)
}
pool.MaxWait = 2 * time.Minute
pool.MaxWait = 1 * time.Minute

resource, err := pool.Run(settings.dockerImage, settings.version, settings.env)
if err != nil {
return nil, nil, fmt.Errorf("could not start resource: %s", err)
}
_ = resource.Expire(60)
_ = resource.Expire(45)

purgeFn := func() error {
if err := pool.Purge(resource); err != nil {
Expand Down Expand Up @@ -207,25 +232,31 @@ func setupDatabase(settings *dbSettings) (database.Database, func() error, error
}

func populateData(db *sqlx.DB, s *dbSettings) error {
// TODO account for multiple SQL files
f := filepath.Join(s.getTestdataFilepath(), s.imageName()+".sql")
data, err := ioutil.ReadFile(f)
dataPattern := filepath.Join(s.getTestdataFilepath(), "*.sql")
files, err := filepath.Glob(dataPattern)
if err != nil {
return fmt.Errorf("could not read sql testdata: %v", err)
return fmt.Errorf("could not find sql testdata: %v", err)
}

queries := bytes.Split(data, []byte(";"))

for _, query := range queries {
query = bytes.TrimSpace(query)
q := string(query)
if q == "" {
continue
for _, f := range files {
data, err := ioutil.ReadFile(f)
if err != nil {
return fmt.Errorf("could not read %q: %v", f, err)
}

_, err = db.Exec(q)
if err != nil {
return fmt.Errorf("could not insert testdata %q: %v", q, err)
queries := bytes.Split(data, []byte(";"))

for _, query := range queries {
query = bytes.TrimSpace(query)
q := string(query)
if q == "" {
continue
}

_, err = db.Exec(q)
if err != nil {
return fmt.Errorf("could not insert testdata %q: %v", q, err)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"github.com/go-sql-driver/mysql"
)

type Mysql8TestTemporals struct {
type TestTemporals struct {
T mysql.NullTime `db:"t"`
TNn time.Time `db:"t_nn"`
D mysql.NullTime `db:"d"`
Expand Down
Loading

0 comments on commit dc0b516

Please sign in to comment.