diff --git a/.github/workflows/tests-postgresql.yml b/.github/workflows/tests-postgresql.yml new file mode 100644 index 0000000..41500a7 --- /dev/null +++ b/.github/workflows/tests-postgresql.yml @@ -0,0 +1,32 @@ +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + tests-postgresql: + runs-on: ubuntu-latest + + services: + db: + image: postgres:14 + env: + POSTGRES_PASSWORD: postgres + POSTGRES_DB: test + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v4 + with: + go-version: 1.18.x + - run: go test -race ./... + env: + DATABASE_DSN: "postgres://postgres:postgres@localhost:5432/test?sslmode=disable" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests-sqlite.yml similarity index 78% rename from .github/workflows/tests.yml rename to .github/workflows/tests-sqlite.yml index beaac74..7ecf571 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests-sqlite.yml @@ -5,7 +5,7 @@ on: branches: [main] jobs: - tests: + tests-sqlite: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -13,3 +13,5 @@ jobs: with: go-version: 1.18.x - run: go test -race ./... + env: + DATABASE_DSN: "file:/tmp/test.db" diff --git a/go.mod b/go.mod index 42c82cd..0864923 100644 --- a/go.mod +++ b/go.mod @@ -25,13 +25,21 @@ require ( github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/gofiber/adaptor/v2 v2.1.25 // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/jackc/pgx/v5 v5.3.1 // indirect - github.com/mattn/go-sqlite3 v1.14.17 // indirect + github.com/lib/pq v1.10.9 + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.18 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mattn/go-sqlite3 v1.14.17 github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/philhofer/fwd v1.1.2 // indirect github.com/prometheus/client_golang v1.12.2 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect + github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect + github.com/tinylib/msgp v1.1.8 // indirect golang.org/x/net v0.9.0 // indirect google.golang.org/protobuf v1.26.0 // indirect ) @@ -53,21 +61,13 @@ require ( github.com/go-playground/universal-translator v0.18.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgx/v5 v5.3.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/klauspost/compress v1.16.3 // indirect github.com/leodido/go-urn v1.2.1 // indirect - github.com/lib/pq v1.10.9 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect - github.com/philhofer/fwd v1.1.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rivo/uniseg v0.2.0 // indirect - github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect - github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect - github.com/tinylib/msgp v1.1.8 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.47.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect diff --git a/go.sum b/go.sum index 3979cbe..64c17c3 100644 --- a/go.sum +++ b/go.sum @@ -191,7 +191,6 @@ github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bY github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= diff --git a/internal/database/database.go b/internal/database/database.go index 55875f9..5c7d79e 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -1,29 +1,55 @@ package database import ( + "fmt" "log" + "strings" - "github.com/italia/developers-italia-api/internal/common" + "github.com/italia/developers-italia-api/internal/models" + + "gorm.io/driver/postgres" + "gorm.io/driver/sqlite" "gorm.io/gorm" + "gorm.io/gorm/logger" ) -type Database interface { - Init(dsn string) (*gorm.DB, error) -} +func NewDatabase(connection string) (*gorm.DB, error) { + var ( + database *gorm.DB + err error + ) -//nolintlint:ireturn -func NewDatabase(env common.Environment) Database { - if env.IsTest() { + switch { + case strings.HasPrefix(connection, "file:"): log.Println("using SQLite database") - return &SQLiteDB{ - dsn: env.Database, - } + database, err = gorm.Open(sqlite.Open(connection), &gorm.Config{TranslateError: true}) + case strings.HasPrefix(connection, "postgres:"): + log.Println("using Postgres database") + + database, err = gorm.Open(postgres.Open(connection), &gorm.Config{ + TranslateError: true, + PrepareStmt: true, + // Disable logging in production + Logger: logger.Default.LogMode(logger.Silent), + }) } - log.Println("using Postgres database") + if err != nil { + return nil, fmt.Errorf("can't open database: %w", err) + } - return &PostgresDB{ - dsn: env.Database, + if err = database.AutoMigrate( + &models.Publisher{}, + &models.Event{}, + &models.CodeHosting{}, + &models.Log{}, + &models.Software{}, + &models.SoftwareURL{}, + &models.Webhook{}, + ); err != nil { + return nil, fmt.Errorf("can't migrate database: %w", err) } + + return database, nil } diff --git a/internal/database/postgres_database.go b/internal/database/postgres_database.go deleted file mode 100644 index bdbb144..0000000 --- a/internal/database/postgres_database.go +++ /dev/null @@ -1,42 +0,0 @@ -package database - -import ( - "fmt" - - "github.com/italia/developers-italia-api/internal/models" - "gorm.io/driver/postgres" - "gorm.io/gorm" - "gorm.io/gorm/logger" -) - -type PostgresDB struct { - dsn string -} - -func (d *PostgresDB) Init(dsn string) (*gorm.DB, error) { - var err error - - database, err := gorm.Open(postgres.Open(dsn), &gorm.Config{ - TranslateError: true, - PrepareStmt: true, - // Disable logging in production - Logger: logger.Default.LogMode(logger.Silent), - }) - if err != nil { - return nil, fmt.Errorf("can't open database: %w", err) - } - - if err = database.AutoMigrate( - &models.Publisher{}, - &models.Event{}, - &models.CodeHosting{}, - &models.Log{}, - &models.Software{}, - &models.SoftwareURL{}, - &models.Webhook{}, - ); err != nil { - return nil, fmt.Errorf("can't migrate database: %w", err) - } - - return database, nil -} diff --git a/internal/database/sqlite_database.go b/internal/database/sqlite_database.go deleted file mode 100644 index 069bf5e..0000000 --- a/internal/database/sqlite_database.go +++ /dev/null @@ -1,36 +0,0 @@ -package database - -import ( - "fmt" - - "github.com/italia/developers-italia-api/internal/models" - "gorm.io/driver/sqlite" - "gorm.io/gorm" -) - -type SQLiteDB struct { - dsn string -} - -func (d *SQLiteDB) Init(dsn string) (*gorm.DB, error) { - var err error - - database, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{TranslateError: true}) - if err != nil { - return nil, fmt.Errorf("can't open database: %w", err) - } - - if err = database.AutoMigrate( - &models.Publisher{}, - &models.Event{}, - &models.CodeHosting{}, - &models.Log{}, - &models.Software{}, - &models.SoftwareURL{}, - &models.Webhook{}, - ); err != nil { - return nil, fmt.Errorf("can't migrate database: %w", err) - } - - return database, nil -} diff --git a/main.go b/main.go index 9fdc41b..5331e95 100644 --- a/main.go +++ b/main.go @@ -34,9 +34,7 @@ func Setup() *fiber.App { panic(err) } - db := database.NewDatabase(common.EnvironmentConfig) - - gormDB, err := db.Init(common.EnvironmentConfig.Database) + gormDB, err := database.NewDatabase(common.EnvironmentConfig.Database) if err != nil { panic(err) } diff --git a/main_test.go b/main_test.go index 56a03d0..e76e774 100644 --- a/main_test.go +++ b/main_test.go @@ -16,6 +16,9 @@ import ( "github.com/go-testfixtures/testfixtures/v3" "github.com/gofiber/fiber/v2" "github.com/stretchr/testify/assert" + + _ "github.com/mattn/go-sqlite3" + _ "github.com/lib/pq" ) const UUID_REGEXP = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" @@ -23,6 +26,7 @@ const UUID_REGEXP = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12 var ( app *fiber.App db *sql.DB + dbDriver string goodToken = "Bearer v2.local.TwwHUQEi8hr2Eo881_Bs5vK9dHOR5BgEU24QRf-U7VmUwI1yOEA6mFT0EsXioMkFT_T-jjrtIJ_Nv8f6hR6ifJXUOuzWEkm9Ijq1mqSjQatD3aDqKMyjjBA" badToken = "Bearer v2.local.UngfrCDNwGUw4pff2oBNoyxYvOErcbVVqLndl6nzONafUCzktaOeMSmoI7B0h62zoxXXLqTm_Phl" ) @@ -43,23 +47,36 @@ type TestCase struct { } func init() { - _ = os.Remove("./test.db") + // Test on SQLite by default if DATABASE_DSN is not set + if _, exists := os.LookupEnv("DATABASE_DSN"); !exists { + _ = os.Setenv("DATABASE_DSN", "file:./test.db") + _ = os.Remove("./test.db") + } - _ = os.Setenv("DATABASE_DSN", "file:./test.db") _ = os.Setenv("ENVIRONMENT", "test") // echo -n 'test-paseto-key-dont-use-in-prod' | base64 _ = os.Setenv("PASETO_KEY", "dGVzdC1wYXNldG8ta2V5LWRvbnQtdXNlLWluLXByb2Q=") + dsn := os.Getenv("DATABASE_DSN") + switch { + case strings.HasPrefix(dsn, "postgres:"): + dbDriver = "postgres" + default: + dbDriver = "sqlite3" + } + var err error - db, err = sql.Open("sqlite3", os.Getenv("DATABASE_DSN")) + db, err = sql.Open(dbDriver, dsn) if err != nil { log.Fatal(err) } // This is needed, otherwise we get a database-locked error // TODO: investigate the root cause - _, _ = db.Exec("PRAGMA journal_mode=WAL;") + if dbDriver == "sqlite3" { + _, _ = db.Exec("PRAGMA journal_mode=WAL;") + } // Setup the app as it is done in the main function app = Setup() @@ -74,7 +91,7 @@ func TestMain(m *testing.M) { func loadFixtures(t *testing.T) { fixtures, err := testfixtures.New( testfixtures.Database(db), - testfixtures.Dialect("sqlite"), + testfixtures.Dialect(dbDriver), testfixtures.Directory("test/testdata/fixtures/"), ) assert.Nil(t, err)