diff --git a/README.md b/README.md new file mode 100644 index 0000000..2147085 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Run tests + +Use lastest postgres docker from https://hub.docker.com/r/library/postgres/ and run: + + env POSTGRES_DSN="postgres://test:test@192.168.99.100:32771/test?sslmode=disable" go test -v ./... diff --git a/shows.go b/shows/shows.go similarity index 74% rename from shows.go rename to shows/shows.go index cbd8939..ddff65d 100644 --- a/shows.go +++ b/shows/shows.go @@ -10,53 +10,6 @@ import ( "github.com/odwrtw/polochon/lib" ) -var Schema = sqly.Schema{ - Require: []sqly.Schema{ - users.Schema, - }, - Tables: []sqly.SchemaTable{ - sqly.SchemaTable{ - Name: "shows", - Sql: ` - CREATE TABLE shows ( - id SERIAL PRIMARY KEY, - imdbid text NOT NULL UNIQUE, - title text NOT NULL, - updated timestamp DEFAULT current_timestamp, - created timestamp DEFAULT current_timestamp - ); - `}, - sqly.SchemaTable{ - Name: "episodes", - Sql: ` - CREATE TABLE episodes ( - id SERIAL PRIMARY KEY, - shows_id integer REFERENCES shows (id) ON DELETE CASCADE, - title text NOT NULL, - season integer NOT NULL, - episode integer NOT NULL, - updated timestamp DEFAULT current_timestamp, - created timestamp DEFAULT current_timestamp - ); - `}, - sqly.SchemaTable{ - Name: "shows_tracked", - Sql: ` - CREATE TABLE shows_tracked ( - shows_id integer NOT NULL REFERENCES shows (id) ON DELETE CASCADE, - users_id integer NOT NULL REFERENCES users (id) ON DELETE CASCADE, - season integer NOT NULL, - episode integer NOT NULL - ); - `}, - }, - Drop: ` - DROP TABLE shows_tracked; - DROP TABLE episodes; - DROP TABLE shows; - `, -} - const ( addShowQuery = `INSERT INTO shows (imdbid, title) VALUES (:imdbid, :title) RETURNING id;` getShowQuery = `SELECT * FROM shows WHERE imdbid=$1;` diff --git a/shows_test.go b/shows/shows_test.go similarity index 93% rename from shows_test.go rename to shows/shows_test.go index 459d4ab..71422c3 100644 --- a/shows_test.go +++ b/shows/shows_test.go @@ -12,6 +12,7 @@ import ( "github.com/jmoiron/sqlx" _ "github.com/lib/pq" + _ "github.com/mattes/migrate/driver/postgres" "github.com/odwrtw/polochon/lib" ) @@ -68,27 +69,22 @@ const showNFO1E2 = ` ` var db *sqlx.DB +var pgdsn string func init() { var err error - pgdsn := os.Getenv("POSTGRES_DSN") + pgdsn = os.Getenv("POSTGRES_DSN") db, err = sqlx.Connect("postgres", pgdsn) if err != nil { fmt.Printf("Unavailable PG tests:\n %v\n", err) os.Exit(1) } - - err = sqly.InitDB(db) - if err != nil { - fmt.Printf("Unavailable PG tests:\n %v\n", err) - os.Exit(1) - } } func TestAddRemoveShow(t *testing.T) { - sqly.RunWithSchema(db, Schema, t, func(db *sqlx.DB, t *testing.T) { + sqly.RunWithLastestMigration(db, pgdsn, t, func(db *sqlx.DB, t *testing.T) { nfo := strings.NewReader(showNFO1) s := &polochon.Show{} polochon.ReadNFO(nfo, s) @@ -143,7 +139,7 @@ func TestAddRemoveShow(t *testing.T) { } func TestTrackedShow(t *testing.T) { - sqly.RunWithSchema(db, Schema, t, func(db *sqlx.DB, t *testing.T) { + sqly.RunWithLastestMigration(db, pgdsn, t, func(db *sqlx.DB, t *testing.T) { nfo := strings.NewReader(showNFO1) s := &polochon.Show{} polochon.ReadNFO(nfo, s) diff --git a/sql/0001_initial.down.sql b/sql/0001_initial.down.sql new file mode 100644 index 0000000..d52d74c --- /dev/null +++ b/sql/0001_initial.down.sql @@ -0,0 +1,9 @@ +DROP TABLE shows_tracked; +DROP TABLE episodes; +DROP TABLE shows; + +DROP TABLE tokens; +DROP TABLE users; + +DROP FUNCTION update_modified_column(); + diff --git a/sql/0001_initial.up.sql b/sql/0001_initial.up.sql new file mode 100644 index 0000000..0b902b4 --- /dev/null +++ b/sql/0001_initial.up.sql @@ -0,0 +1,54 @@ +CREATE OR REPLACE FUNCTION update_modified_column() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated = now(); + RETURN NEW; +END; $$ language 'plpgsql'; + +CREATE TABLE users ( + id SERIAL PRIMARY KEY, + name text NOT NULL UNIQUE, + updated timestamp DEFAULT current_timestamp, + created timestamp DEFAULT current_timestamp +); + +CREATE TRIGGER update_users BEFORE UPDATE ON users FOR EACH ROW EXECUTE PROCEDURE update_modified_column(); + +CREATE TABLE tokens ( + id SERIAL, + value text NOT NULL UNIQUE, + users_id integer REFERENCES users (id) ON DELETE CASCADE, + updated timestamp DEFAULT current_timestamp, + created timestamp DEFAULT current_timestamp +); + +CREATE TRIGGER update_tokens BEFORE UPDATE ON tokens FOR EACH ROW EXECUTE PROCEDURE update_modified_column(); + +CREATE TABLE shows ( + id SERIAL PRIMARY KEY, + imdbid text NOT NULL UNIQUE, + title text NOT NULL, + updated timestamp DEFAULT current_timestamp, + created timestamp DEFAULT current_timestamp +); + +CREATE TRIGGER update_shows BEFORE UPDATE ON shows FOR EACH ROW EXECUTE PROCEDURE update_modified_column(); + +CREATE TABLE episodes ( + id SERIAL PRIMARY KEY, + shows_id integer REFERENCES shows (id) ON DELETE CASCADE, + title text NOT NULL, + season integer NOT NULL, + episode integer NOT NULL, + updated timestamp DEFAULT current_timestamp, + created timestamp DEFAULT current_timestamp +); + +CREATE TRIGGER update_episodes BEFORE UPDATE ON episodes FOR EACH ROW EXECUTE PROCEDURE update_modified_column(); + +CREATE TABLE shows_tracked ( + shows_id integer NOT NULL REFERENCES shows (id) ON DELETE CASCADE, + users_id integer NOT NULL REFERENCES users (id) ON DELETE CASCADE, + season integer NOT NULL, + episode integer NOT NULL +); diff --git a/sqltest/sqltest.go b/sqltest/sqltest.go deleted file mode 100644 index 64f268e..0000000 --- a/sqltest/sqltest.go +++ /dev/null @@ -1,39 +0,0 @@ -package sqltest - -import ( - "fmt" - "strings" - "testing" - - "github.com/jmoiron/sqlx" -) - -func MultiExec(e sqlx.Execer, query string) error { - stmts := strings.Split(query, ";\n") - if len(strings.Trim(stmts[len(stmts)-1], " \n\t\r")) == 0 { - stmts = stmts[:len(stmts)-1] - } - for _, s := range stmts { - _, err := e.Exec(s) - if err != nil { - return fmt.Errorf("%s\n%s", err, s) - } - } - return nil -} - -func RunWithSchema(db *sqlx.DB, create, drop string, t *testing.T, test func(db *sqlx.DB, t *testing.T)) { - defer func() { - err := MultiExec(db, drop) - if err != nil { - t.Fatalf("%s", err.Error()) - } - }() - - err := MultiExec(db, create) - if err != nil { - t.Fatalf("%s", err.Error()) - } - - test(db, t) -} diff --git a/sqly/sqly.go b/sqly/sqly.go index 234184e..c0e3713 100644 --- a/sqly/sqly.go +++ b/sqly/sqly.go @@ -2,28 +2,13 @@ package sqly import ( "fmt" - "strings" "testing" "time" "github.com/jmoiron/sqlx" + "github.com/mattes/migrate/migrate" ) -const initDBQuery = ` -CREATE OR REPLACE FUNCTION update_modified_column() -RETURNS TRIGGER AS $$ -BEGIN NEW.updated = now(); RETURN NEW; END; $$ language 'plpgsql'; -` - -// InitDB add some function to the database and template -func InitDB(db *sqlx.DB) error { - err := MultiExec(db, initDBQuery) - if err != nil { - return err - } - return nil -} - // BaseTable have to be embeded in all your struct which reflect a table type BaseTable struct { ID int @@ -31,84 +16,25 @@ type BaseTable struct { Created time.Time } -type SchemaTable struct { - Name string - Sql string -} - -type Schema struct { - // Create contains all tables, the key is the name and the - // value the sql - Tables []SchemaTable - - // Drop contains the drop sql - Drop string - - // Require add schema before create and delete after - Require []Schema -} - -func (s Schema) Create(db *sqlx.DB) error { - for _, sch := range s.Require { - err := sch.Create(db) - if err != nil { - return err - } - } - - for _, table := range s.Tables { - _, err := db.Exec(table.Sql) - if err != nil { - return fmt.Errorf("%s\n%s", err, table.Sql) - } - trigger := fmt.Sprintf("CREATE TRIGGER update_%s BEFORE UPDATE ON %s FOR EACH ROW EXECUTE PROCEDURE update_modified_column();", table.Name, table.Name) - _, err = db.Exec(trigger) - if err != nil { - return fmt.Errorf("%s\n%s", err, trigger) - } - } - return nil -} - -func (s Schema) Delete(db *sqlx.DB) error { - err := MultiExec(db, s.Drop) - if err != nil { - return err - } - for _, sch := range s.Require { - err := sch.Delete(db) - if err != nil { - return err - } - } - return nil -} - -func MultiExec(e sqlx.Execer, query string) error { - stmts := strings.Split(query, ";\n") - if len(strings.Trim(stmts[len(stmts)-1], " \n\t\r")) == 0 { - stmts = stmts[:len(stmts)-1] - } - for _, s := range stmts { - _, err := e.Exec(s) - if err != nil { - return fmt.Errorf("%s\n%s", err, s) - } - } - return nil -} - -func RunWithSchema(db *sqlx.DB, schema Schema, t *testing.T, test func(db *sqlx.DB, t *testing.T)) { +func RunWithLastestMigration(db *sqlx.DB, pgdsn string, t *testing.T, test func(db *sqlx.DB, t *testing.T)) { defer func() { - err := schema.Delete(db) - if err != nil { - t.Fatalf("%s", err.Error()) + allErrors, ok := migrate.DownSync(pgdsn, "../sql") + if !ok { + fmt.Println("Oh no ...") + for _, err := range allErrors { + t.Log(err) + t.Fatal("We get some errors when reset the database schema") + } } }() - err := schema.Create(db) - if err != nil { - t.Fatalf("%s", err.Error()) + allErrors, ok := migrate.UpSync(pgdsn, "../sql") + if !ok { + fmt.Println("Oh no ...") + for _, err := range allErrors { + t.Log(err) + t.Fatal("Impossible to run test we get some errors when initialize the database schema") + } } test(db, t) diff --git a/tools/run.go b/tools/run.go deleted file mode 100644 index 303fba7..0000000 --- a/tools/run.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - "flag" - - "github.com/jmoiron/sqlx" - "github.com/kr/pretty" - _ "github.com/lib/pq" -) - -var pg string -var sqlFile string - -func init() { - flag.StringVar(&pg, "pg", "", "postgres database's connection string") - flag.StringVar(&sqlFile, "file", "", "path to a sql file") -} - -func main() { - flag.Parse() - - db := sqlx.MustConnect("postgres", pg) - err := db.Ping() - if err != nil { - pretty.Println(err) - } - - if sqlFile != "" { - r, err := sqlx.LoadFile(db, sqlFile) - if err != nil { - pretty.Println(err) - } - pretty.Println(r) - } -} diff --git a/users/users.go b/users/users.go index 4673480..0f86f3b 100644 --- a/users/users.go +++ b/users/users.go @@ -8,35 +8,6 @@ import ( "gitlab.quimbo.fr/odwrtw/canape-sql/sqly" ) -var Schema = sqly.Schema{ - Tables: []sqly.SchemaTable{ - sqly.SchemaTable{ - Name: "users", - Sql: ` - CREATE TABLE users ( - id SERIAL PRIMARY KEY, - name text NOT NULL UNIQUE, - updated timestamp DEFAULT current_timestamp, - created timestamp DEFAULT current_timestamp - );`}, - sqly.SchemaTable{ - Name: "tokens", - Sql: ` - CREATE TABLE tokens ( - id SERIAL, - value text NOT NULL UNIQUE, - users_id integer REFERENCES users (id) ON DELETE CASCADE, - updated timestamp DEFAULT current_timestamp, - created timestamp DEFAULT current_timestamp - ); - `}, - }, - Drop: ` - DROP TABLE tokens; - DROP TABLE users; - `, -} - const ( addUserQuery = `INSERT INTO users (name) VALUES ($1) RETURNING id;` getUserQuery = `SELECT * FROM users WHERE name=$1;` diff --git a/users/users_test.go b/users/users_test.go index 49b20c4..08a1cc8 100644 --- a/users/users_test.go +++ b/users/users_test.go @@ -9,30 +9,26 @@ import ( "github.com/jmoiron/sqlx" "github.com/lib/pq" + _ "github.com/mattes/migrate/driver/postgres" ) var db *sqlx.DB +var pgdsn string func init() { var err error - pgdsn := os.Getenv("POSTGRES_DSN") + pgdsn = os.Getenv("POSTGRES_DSN") db, err = sqlx.Connect("postgres", pgdsn) if err != nil { fmt.Printf("Unavailable PG tests:\n %v\n", err) os.Exit(1) } - - err = sqly.InitDB(db) - if err != nil { - fmt.Printf("Unavailable PG tests:\n %v\n", err) - os.Exit(1) - } } func TestUser(t *testing.T) { - sqly.RunWithSchema(db, Schema, t, func(db *sqlx.DB, t *testing.T) { + sqly.RunWithLastestMigration(db, pgdsn, t, func(db *sqlx.DB, t *testing.T) { // Add a new user u := &User{Name: "plop"} @@ -100,7 +96,7 @@ func TestUser(t *testing.T) { } func TestTokenAddDelete(t *testing.T) { - sqly.RunWithSchema(db, Schema, t, func(db *sqlx.DB, t *testing.T) { + sqly.RunWithLastestMigration(db, pgdsn, t, func(db *sqlx.DB, t *testing.T) { // Add a new user u := &User{Name: "plop"} err := u.Add(db) @@ -160,7 +156,7 @@ func TestTokenAddDelete(t *testing.T) { } func TestTokenCheck(t *testing.T) { - sqly.RunWithSchema(db, Schema, t, func(db *sqlx.DB, t *testing.T) { + sqly.RunWithLastestMigration(db, pgdsn, t, func(db *sqlx.DB, t *testing.T) { u := &User{Name: "plop"} u.Add(db) token, err := u.NewToken(db) @@ -186,7 +182,7 @@ func TestTokenCheck(t *testing.T) { } func TestAutoUpdateCols(t *testing.T) { - sqly.RunWithSchema(db, Schema, t, func(db *sqlx.DB, t *testing.T) { + sqly.RunWithLastestMigration(db, pgdsn, t, func(db *sqlx.DB, t *testing.T) { u := &User{Name: "plop"} u.Add(db) u.Name = "toto"