Skip to content

Commit 6c19abb

Browse files
authored
database + migrations (#39)
1 parent 6876e90 commit 6c19abb

File tree

11 files changed

+243
-0
lines changed

11 files changed

+243
-0
lines changed

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Toolbox and building blocks for new Go projects, to get started quickly and righ
1616
* Using https://pkg.go.dev/github.com/go-chi/chi/v5 for routing
1717
* [Urfave](https://cli.urfave.org/) for cli args
1818
* https://github.com/uber-go/nilaway
19+
* Postgres database with migrations
1920
* See also:
2021
* Public project setup: https://github.com/flashbots/flashbots-repository-template
2122
* Repository for common Go utilities: https://github.com/flashbots/go-utils
@@ -55,3 +56,19 @@ make lint
5556
make test
5657
make fmt
5758
```
59+
60+
61+
**Database tests (using a live Postgres instance)**
62+
63+
Database tests will be run if the `RUN_DB_TESTS` environment variable is set to `1`.
64+
65+
```bash
66+
# start the database
67+
docker run -d --name postgres-test -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=postgres postgres
68+
69+
# run the tests
70+
RUN_DB_TESTS=1 make test
71+
72+
# stop the database
73+
docker rm -f postgres-test
74+
```

common/utils.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package common
2+
3+
import "os"
4+
5+
func GetEnv(key, defaultValue string) string {
6+
if value, ok := os.LookupEnv(key); ok {
7+
return value
8+
}
9+
return defaultValue
10+
}

database/database.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Package database exposes the postgres database
2+
package database
3+
4+
import (
5+
"os"
6+
7+
"github.com/flashbots/go-template/database/migrations"
8+
"github.com/flashbots/go-template/database/vars"
9+
"github.com/jmoiron/sqlx"
10+
_ "github.com/lib/pq"
11+
migrate "github.com/rubenv/sql-migrate"
12+
)
13+
14+
type DatabaseService struct {
15+
DB *sqlx.DB
16+
}
17+
18+
func NewDatabaseService(dsn string) (*DatabaseService, error) {
19+
db, err := sqlx.Connect("postgres", dsn)
20+
if err != nil {
21+
return nil, err
22+
}
23+
24+
db.DB.SetMaxOpenConns(50)
25+
db.DB.SetMaxIdleConns(10)
26+
db.DB.SetConnMaxIdleTime(0)
27+
28+
if os.Getenv("DB_DONT_APPLY_SCHEMA") == "" {
29+
migrate.SetTable(vars.TableMigrations)
30+
_, err := migrate.Exec(db.DB, "postgres", migrations.Migrations, migrate.Up)
31+
if err != nil {
32+
return nil, err
33+
}
34+
}
35+
36+
dbService := &DatabaseService{DB: db} //nolint:exhaustruct
37+
err = dbService.prepareNamedQueries()
38+
return dbService, err
39+
}
40+
41+
func (s *DatabaseService) prepareNamedQueries() (err error) {
42+
return nil
43+
}
44+
45+
func (s *DatabaseService) Close() error {
46+
return s.DB.Close()
47+
}
48+
49+
func (s *DatabaseService) SomeQuery() (count uint64, err error) {
50+
query := `SELECT COUNT(*) FROM ` + vars.TableTest + `;`
51+
row := s.DB.QueryRow(query)
52+
err = row.Scan(&count)
53+
return count, err
54+
}

database/database_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package database
2+
3+
import (
4+
"os"
5+
"testing"
6+
7+
"github.com/flashbots/go-template/common"
8+
"github.com/flashbots/go-template/database/migrations"
9+
"github.com/flashbots/go-template/database/vars"
10+
"github.com/jmoiron/sqlx"
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
var (
15+
runDBTests = os.Getenv("RUN_DB_TESTS") == "1" //|| true
16+
testDBDSN = common.GetEnv("TEST_DB_DSN", "postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable")
17+
)
18+
19+
func resetDatabase(t *testing.T) *DatabaseService {
20+
t.Helper()
21+
if !runDBTests {
22+
t.Skip("Skipping database tests")
23+
}
24+
25+
// Wipe test database
26+
_db, err := sqlx.Connect("postgres", testDBDSN)
27+
require.NoError(t, err)
28+
_, err = _db.Exec(`DROP SCHEMA public CASCADE; CREATE SCHEMA public;`)
29+
require.NoError(t, err)
30+
31+
db, err := NewDatabaseService(testDBDSN)
32+
require.NoError(t, err)
33+
return db
34+
}
35+
36+
func TestMigrations(t *testing.T) {
37+
db := resetDatabase(t)
38+
query := `SELECT COUNT(*) FROM ` + vars.TableMigrations + `;`
39+
rowCount := 0
40+
err := db.DB.QueryRow(query).Scan(&rowCount)
41+
require.NoError(t, err)
42+
require.Len(t, migrations.Migrations.Migrations, rowCount)
43+
}
44+
45+
func Test_DB1(t *testing.T) {
46+
db := resetDatabase(t)
47+
x, err := db.SomeQuery()
48+
require.NoError(t, err)
49+
require.Equal(t, uint64(0), x)
50+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package migrations
2+
3+
import (
4+
"github.com/flashbots/go-template/database/vars"
5+
migrate "github.com/rubenv/sql-migrate"
6+
)
7+
8+
var Migration001InitDatabase = &migrate.Migration{
9+
Id: "001-init-database",
10+
Up: []string{`
11+
CREATE TABLE IF NOT EXISTS ` + vars.TableTest + ` (
12+
id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
13+
inserted_at timestamp NOT NULL default current_timestamp
14+
);
15+
`},
16+
Down: []string{`
17+
DROP TABLE IF EXISTS ` + vars.TableTest + `;
18+
`},
19+
DisableTransactionUp: false,
20+
DisableTransactionDown: false,
21+
}

database/migrations/migration.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Package migrations contains all the migration files
2+
package migrations
3+
4+
import (
5+
migrate "github.com/rubenv/sql-migrate"
6+
)
7+
8+
var Migrations = migrate.MemoryMigrationSource{
9+
Migrations: []*migrate.Migration{
10+
Migration001InitDatabase,
11+
},
12+
}

database/types.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package database
2+
3+
import (
4+
"database/sql"
5+
"time"
6+
)
7+
8+
func NewNullInt64(i int64) sql.NullInt64 {
9+
return sql.NullInt64{
10+
Int64: i,
11+
Valid: true,
12+
}
13+
}
14+
15+
func NewNullString(s string) sql.NullString {
16+
return sql.NullString{
17+
String: s,
18+
Valid: true,
19+
}
20+
}
21+
22+
// NewNullTime returns a sql.NullTime with the given time.Time. If the time is
23+
// the zero value, the NullTime is invalid.
24+
func NewNullTime(t time.Time) sql.NullTime {
25+
return sql.NullTime{
26+
Time: t,
27+
Valid: t != time.Time{},
28+
}
29+
}

database/types_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package database
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
func TestNewNullTime(t *testing.T) {
11+
var t1 time.Time
12+
nt1 := NewNullTime(t1)
13+
require.False(t, nt1.Valid)
14+
15+
t1 = time.Now()
16+
nt1 = NewNullTime(t1)
17+
require.True(t, nt1.Valid)
18+
require.Equal(t, t1, nt1.Time)
19+
}

database/vars/tables.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Package vars contains the database variables such as dynamic table names
2+
package vars
3+
4+
import "github.com/flashbots/go-template/common"
5+
6+
var (
7+
tablePrefix = common.GetEnv("DB_TABLE_PREFIX", "dev")
8+
9+
TableMigrations = tablePrefix + "_migrations"
10+
TableTest = tablePrefix + "_test"
11+
)

go.mod

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ require (
66
github.com/flashbots/go-utils v0.6.1-0.20240610084140-4461ab748667
77
github.com/go-chi/chi/v5 v5.0.12
88
github.com/google/uuid v1.6.0
9+
github.com/jmoiron/sqlx v1.4.0
10+
github.com/lib/pq v1.10.9
911
github.com/prometheus/client_golang v1.19.1
12+
github.com/rubenv/sql-migrate v1.7.0
1013
github.com/stretchr/testify v1.9.0
1114
github.com/urfave/cli/v2 v2.27.2
1215
go.opentelemetry.io/contrib/instrumentation/runtime v0.46.1
@@ -22,6 +25,7 @@ require (
2225
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
2326
github.com/davecgh/go-spew v1.1.1 // indirect
2427
github.com/ethereum/go-ethereum v1.13.14 // indirect
28+
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
2529
github.com/go-logr/logr v1.3.0 // indirect
2630
github.com/go-logr/stdr v1.2.2 // indirect
2731
github.com/holiman/uint256 v1.2.4 // indirect

0 commit comments

Comments
 (0)