add unit tests helpers

This commit is contained in:
qpismont 2025-02-25 20:55:12 +00:00
parent 89c39515f0
commit f13230caba
13 changed files with 262 additions and 4 deletions

View file

@ -22,7 +22,7 @@ var ascii string
func main() { func main() {
fmt.Println(ascii) fmt.Println(ascii)
core.LoadEnvVars() core.LoadEnvVars(".env")
db := setupDB() db := setupDB()
router := setupRouter(db) router := setupRouter(db)

5
go.mod
View file

@ -6,14 +6,19 @@ require (
github.com/jackc/pgx v3.6.2+incompatible github.com/jackc/pgx v3.6.2+incompatible
github.com/jmoiron/sqlx v1.4.0 github.com/jmoiron/sqlx v1.4.0
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
github.com/magiconair/properties v1.8.9
github.com/stretchr/testify v1.10.0
) )
require ( require (
github.com/cockroachdb/apd v1.1.0 // indirect github.com/cockroachdb/apd v1.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/gofrs/uuid v4.4.0+incompatible // indirect
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect
golang.org/x/crypto v0.33.0 // indirect golang.org/x/crypto v0.33.0 // indirect
golang.org/x/text v0.22.0 // indirect golang.org/x/text v0.22.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
) )

12
go.sum
View file

@ -2,6 +2,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
@ -16,13 +18,23 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -0,0 +1,6 @@
package accounts
const (
SqlInsert = "INSERT INTO accounts (username, password, role_id) VALUES ($1, $2, $3) RETURNING id"
SqlFetchOneByUsername = "SELECT * FROM accounts WHERE username = $1"
)

View file

@ -0,0 +1,10 @@
package accounts
type Account struct {
Id int `db:"id" json:"id"`
Username string `db:"username" json:"username"`
Password string `db:"password" json:"-"`
RoleId int `db:"role_id" json:"role_id"`
CreatedAt string `db:"created_at" json:"created_at"`
UpdatedAt string `db:"updated_at" json:"updated_at"`
}

View file

@ -1,6 +1,8 @@
package accounts package accounts
import ( import (
"database/sql"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
) )
@ -11,3 +13,36 @@ type Repository struct {
func NewRepository(db *sqlx.DB) Repository { func NewRepository(db *sqlx.DB) Repository {
return Repository{db: db} return Repository{db: db}
} }
func (r *Repository) Insert(account *Account) (int, error) {
var id int
stmt, err := r.db.Prepare(SqlInsert)
if err != nil {
return id, err
}
defer stmt.Close()
err = stmt.QueryRow(account.Username, account.Password, account.RoleId).Scan(&id)
if err != nil {
return id, err
}
return id, nil
}
func (r *Repository) FetchOneByUsername(username string) (*Account, error) {
var account Account
err := r.db.Get(&account, SqlFetchOneByUsername, username)
if err != nil {
if err == sql.ErrNoRows {
return nil, nil
} else {
return nil, err
}
}
return &account, nil
}

View file

@ -0,0 +1,48 @@
package accounts
import (
"testing"
"gitea.qpismont.fr/qpismont/trepa/test"
"github.com/magiconair/properties/assert"
)
func TestRepository_Insert(t *testing.T) {
db := test.SetupTestDB(t, "../../")
defer db.Close()
repo := NewRepository(db)
account := &Account{
Username: "test",
Password: "test",
RoleId: 1,
}
id, err := repo.Insert(account)
if err != nil {
t.Fatalf("Failed to insert account: %v", err)
}
assert.Equal(t, id, 3)
}
func TestRepository_FetchOneByUsername(t *testing.T) {
db := test.SetupTestDB(t, "../../")
defer db.Close()
repo := NewRepository(db)
account, err := repo.FetchOneByUsername("admin")
if err != nil {
t.Fatalf("Failed to fetch account: %v", err)
}
if account == nil {
t.Fatalf("Account not found")
}
assert.Equal(t, account.Username, "admin")
assert.Equal(t, account.Password, "LOLPASSWORD")
assert.Equal(t, account.RoleId, 1)
}

View file

@ -0,0 +1,38 @@
package core
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestComputeDBURL(t *testing.T) {
LoadEnvVars("../../.env")
dbHost := MustGetEnvVar("TEST_DB_HOST")
dbPort := MustGetEnvVar("TEST_DB_PORT")
dbUser := MustGetEnvVar("TEST_DB_USER")
dbPassword := MustGetEnvVar("TEST_DB_PASSWORD")
dbName := MustGetEnvVar("TEST_DB_NAME")
dbURL := ComputeDBURL(dbHost, dbPort, dbUser, dbPassword, dbName)
assert.Equal(t, dbURL, "postgres://dev:dev@host.docker.internal:5432/trepa_test")
}
func TestSetupDB(t *testing.T) {
LoadEnvVars("../../.env")
dbHost := MustGetEnvVar("TEST_DB_HOST")
dbPort := MustGetEnvVar("TEST_DB_PORT")
dbUser := MustGetEnvVar("TEST_DB_USER")
dbPassword := MustGetEnvVar("TEST_DB_PASSWORD")
dbName := MustGetEnvVar("TEST_DB_NAME")
dbURL := ComputeDBURL(dbHost, dbPort, dbUser, dbPassword, dbName)
db, err := SetupDB(dbURL)
defer db.Close()
assert.NoError(t, err)
assert.NotNil(t, db)
}

View file

@ -7,10 +7,10 @@ import (
"github.com/joho/godotenv" "github.com/joho/godotenv"
) )
func LoadEnvVars() { func LoadEnvVars(path string) {
err := godotenv.Load() err := godotenv.Load(path)
if err != nil { if err != nil {
slog.Warn("Error loading .env file") slog.Warn("Error loading .env file", "path", path)
} }
} }

View file

@ -0,0 +1,17 @@
package core
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestMustGetEnvVar(t *testing.T) {
LoadEnvVars("../../.env")
assert.Equal(t, MustGetEnvVar("TEST_DB_HOST"), "host.docker.internal")
assert.Equal(t, MustGetEnvVar("TEST_DB_PORT"), "5432")
assert.Equal(t, MustGetEnvVar("TEST_DB_USER"), "dev")
assert.Equal(t, MustGetEnvVar("TEST_DB_PASSWORD"), "dev")
assert.Equal(t, MustGetEnvVar("TEST_DB_NAME"), "trepa_test")
}

View file

@ -39,6 +39,7 @@ func (r *Response) Write(b []byte) (int, error) {
return n, err return n, err
} }
// Implement http.Handler
type ServerMux struct { type ServerMux struct {
mux *http.ServeMux mux *http.ServeMux
} }

2
test/fixtures/00-accounts.sql vendored Normal file
View file

@ -0,0 +1,2 @@
INSERT INTO accounts (username, password, role_id) VALUES ('admin', 'LOLPASSWORD', 1);
INSERT INTO accounts (username, password, role_id) VALUES ('user', 'LOLPASSWORD', 2);

84
test/helpers.go Normal file
View file

@ -0,0 +1,84 @@
package test
import (
"os"
"path/filepath"
"testing"
"gitea.qpismont.fr/qpismont/trepa/internal/core"
"github.com/jmoiron/sqlx"
)
func SetupTestDB(test *testing.T, rootPath string) *sqlx.DB {
core.LoadEnvVars(rootPath + "/.env")
dbHost := core.MustGetEnvVar("TEST_DB_HOST")
dbPort := core.MustGetEnvVar("TEST_DB_PORT")
dbUser := core.MustGetEnvVar("TEST_DB_USER")
dbPassword := core.MustGetEnvVar("TEST_DB_PASSWORD")
dbName := core.MustGetEnvVar("TEST_DB_NAME")
dbExecute := initTestDB(test, dbHost, dbPort, dbUser, dbPassword, "postgres")
resetTestDB(dbExecute, test, dbName)
dbExecute.Close()
dbTest := initTestDB(test, dbHost, dbPort, dbUser, dbPassword, dbName)
executeMigrations(dbTest, test, rootPath)
executeFixtures(dbTest, test, rootPath)
return dbTest
}
func initTestDB(t *testing.T, dbHost, dbPort, dbUser, dbPassword, dbName string) *sqlx.DB {
dbURL := core.ComputeDBURL(dbHost, dbPort, dbUser, dbPassword, dbName)
db, err := core.SetupDB(dbURL)
if err != nil {
t.Fatalf("Failed to connect to test database: %v", err)
}
return db
}
func resetTestDB(db *sqlx.DB, t *testing.T, dbName string) {
_, err := db.Exec("DROP DATABASE IF EXISTS " + dbName + " WITH (FORCE);")
if err != nil {
t.Fatalf("Failed to drop test database: %v", err)
}
_, err = db.Exec("CREATE DATABASE " + dbName + ";")
if err != nil {
t.Fatalf("Failed to create test database: %v", err)
}
}
func executeMigrations(db *sqlx.DB, t *testing.T, rootPath string) {
rootPath = filepath.Join(rootPath, "migrations")
executeSqlFolder(db, t, rootPath)
}
func executeFixtures(db *sqlx.DB, t *testing.T, rootPath string) {
rootPath = filepath.Join(rootPath, "test", "fixtures")
executeSqlFolder(db, t, rootPath)
}
func executeSqlFolder(db *sqlx.DB, t *testing.T, folder string) {
files, err := filepath.Glob(folder + "/*.sql")
if err != nil {
t.Fatalf("Failed to read sql folder: %v", err)
}
for _, file := range files {
t.Log("Executing " + file)
sql, err := os.ReadFile(file)
if err != nil {
t.Fatalf("Failed to read sql file: %v", err)
}
_, err = db.Exec(string(sql))
if err != nil {
t.Fatalf("Failed to execute sql file: %v", err)
}
}
}