Use migrate

This commit is contained in:
Nicolas Duhamel 2016-02-16 21:51:06 +01:00
parent d0bf7b4354
commit ba833aa770
10 changed files with 96 additions and 260 deletions

5
README.md Normal file
View File

@ -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 ./...

View File

@ -10,53 +10,6 @@ import (
"github.com/odwrtw/polochon/lib" "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 ( const (
addShowQuery = `INSERT INTO shows (imdbid, title) VALUES (:imdbid, :title) RETURNING id;` addShowQuery = `INSERT INTO shows (imdbid, title) VALUES (:imdbid, :title) RETURNING id;`
getShowQuery = `SELECT * FROM shows WHERE imdbid=$1;` getShowQuery = `SELECT * FROM shows WHERE imdbid=$1;`

View File

@ -12,6 +12,7 @@ import (
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
_ "github.com/lib/pq" _ "github.com/lib/pq"
_ "github.com/mattes/migrate/driver/postgres"
"github.com/odwrtw/polochon/lib" "github.com/odwrtw/polochon/lib"
) )
@ -68,27 +69,22 @@ const showNFO1E2 = `
` `
var db *sqlx.DB var db *sqlx.DB
var pgdsn string
func init() { func init() {
var err error var err error
pgdsn := os.Getenv("POSTGRES_DSN") pgdsn = os.Getenv("POSTGRES_DSN")
db, err = sqlx.Connect("postgres", pgdsn) db, err = sqlx.Connect("postgres", pgdsn)
if err != nil { if err != nil {
fmt.Printf("Unavailable PG tests:\n %v\n", err) fmt.Printf("Unavailable PG tests:\n %v\n", err)
os.Exit(1) 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) { 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) nfo := strings.NewReader(showNFO1)
s := &polochon.Show{} s := &polochon.Show{}
polochon.ReadNFO(nfo, s) polochon.ReadNFO(nfo, s)
@ -143,7 +139,7 @@ func TestAddRemoveShow(t *testing.T) {
} }
func TestTrackedShow(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) nfo := strings.NewReader(showNFO1)
s := &polochon.Show{} s := &polochon.Show{}
polochon.ReadNFO(nfo, s) polochon.ReadNFO(nfo, s)

View File

@ -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();

54
sql/0001_initial.up.sql Normal file
View File

@ -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
);

View File

@ -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)
}

View File

@ -2,28 +2,13 @@ package sqly
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"time" "time"
"github.com/jmoiron/sqlx" "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 // BaseTable have to be embeded in all your struct which reflect a table
type BaseTable struct { type BaseTable struct {
ID int ID int
@ -31,84 +16,25 @@ type BaseTable struct {
Created time.Time Created time.Time
} }
type SchemaTable struct { func RunWithLastestMigration(db *sqlx.DB, pgdsn string, t *testing.T, test func(db *sqlx.DB, t *testing.T)) {
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)) {
defer func() { defer func() {
err := schema.Delete(db) allErrors, ok := migrate.DownSync(pgdsn, "../sql")
if err != nil { if !ok {
t.Fatalf("%s", err.Error()) 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) allErrors, ok := migrate.UpSync(pgdsn, "../sql")
if err != nil { if !ok {
t.Fatalf("%s", err.Error()) 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) test(db, t)

View File

@ -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)
}
}

View File

@ -8,35 +8,6 @@ import (
"gitlab.quimbo.fr/odwrtw/canape-sql/sqly" "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 ( const (
addUserQuery = `INSERT INTO users (name) VALUES ($1) RETURNING id;` addUserQuery = `INSERT INTO users (name) VALUES ($1) RETURNING id;`
getUserQuery = `SELECT * FROM users WHERE name=$1;` getUserQuery = `SELECT * FROM users WHERE name=$1;`

View File

@ -9,30 +9,26 @@ import (
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/lib/pq" "github.com/lib/pq"
_ "github.com/mattes/migrate/driver/postgres"
) )
var db *sqlx.DB var db *sqlx.DB
var pgdsn string
func init() { func init() {
var err error var err error
pgdsn := os.Getenv("POSTGRES_DSN") pgdsn = os.Getenv("POSTGRES_DSN")
db, err = sqlx.Connect("postgres", pgdsn) db, err = sqlx.Connect("postgres", pgdsn)
if err != nil { if err != nil {
fmt.Printf("Unavailable PG tests:\n %v\n", err) fmt.Printf("Unavailable PG tests:\n %v\n", err)
os.Exit(1) 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) { 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 // Add a new user
u := &User{Name: "plop"} u := &User{Name: "plop"}
@ -100,7 +96,7 @@ func TestUser(t *testing.T) {
} }
func TestTokenAddDelete(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 // Add a new user
u := &User{Name: "plop"} u := &User{Name: "plop"}
err := u.Add(db) err := u.Add(db)
@ -160,7 +156,7 @@ func TestTokenAddDelete(t *testing.T) {
} }
func TestTokenCheck(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 := &User{Name: "plop"}
u.Add(db) u.Add(db)
token, err := u.NewToken(db) token, err := u.NewToken(db)
@ -186,7 +182,7 @@ func TestTokenCheck(t *testing.T) {
} }
func TestAutoUpdateCols(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 := &User{Name: "plop"}
u.Add(db) u.Add(db)
u.Name = "toto" u.Name = "toto"