Use migrate
This commit is contained in:
parent
d0bf7b4354
commit
ba833aa770
5
README.md
Normal file
5
README.md
Normal 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 ./...
|
@ -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;`
|
@ -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)
|
9
sql/0001_initial.down.sql
Normal file
9
sql/0001_initial.down.sql
Normal 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
54
sql/0001_initial.up.sql
Normal 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
|
||||
);
|
@ -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)
|
||||
}
|
106
sqly/sqly.go
106
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)
|
||||
|
35
tools/run.go
35
tools/run.go
@ -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)
|
||||
}
|
||||
}
|
@ -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;`
|
||||
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user