305 lines
9.7 KiB
Go
305 lines
9.7 KiB
Go
package mysql
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
|
|
"wucher/internal/domain/land"
|
|
"wucher/internal/shared/pkg/uuidv7"
|
|
)
|
|
|
|
func openLandTestDB(t *testing.T) *gorm.DB {
|
|
t.Helper()
|
|
dsn := fmt.Sprintf("file:land_test_%d?mode=memory&cache=shared", time.Now().UnixNano())
|
|
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{
|
|
NowFunc: func() time.Time { return time.Now().UTC() },
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("open sqlite: %v", err)
|
|
}
|
|
if err := db.AutoMigrate(&land.Land{}); err != nil {
|
|
t.Fatalf("auto migrate: %v", err)
|
|
}
|
|
if err := db.Exec(`CREATE TABLE IF NOT EXISTS federal_states (
|
|
id BLOB PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
land_id BLOB NOT NULL,
|
|
deleted_at DATETIME
|
|
)`).Error; err != nil {
|
|
t.Fatalf("create federal_states table: %v", err)
|
|
}
|
|
return db
|
|
}
|
|
|
|
func TestNewLandRepository(t *testing.T) {
|
|
db := openLandTestDB(t)
|
|
repo := NewLandRepository(db)
|
|
if repo == nil || repo.db == nil {
|
|
t.Fatalf("expected repository initialized")
|
|
}
|
|
}
|
|
|
|
func TestLandRepositoryCreate(t *testing.T) {
|
|
db := openLandTestDB(t)
|
|
repo := NewLandRepository(db)
|
|
row := &land.Land{Name: "Germany", LandISOCode: "DE"}
|
|
|
|
if err := repo.Create(context.Background(), row); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if len(row.ID) == 0 {
|
|
t.Fatalf("expected id set")
|
|
}
|
|
exportID := "DE001"
|
|
withExport := &land.Land{Name: "WithExport", LandISOCode: "DX", BMDExportID: &exportID}
|
|
if err := repo.Create(context.Background(), withExport); err != nil {
|
|
t.Fatalf("create with export id: %v", err)
|
|
}
|
|
gotWithExport, err := repo.GetByID(context.Background(), withExport.ID)
|
|
if err != nil {
|
|
t.Fatalf("get with export id: %v", err)
|
|
}
|
|
if gotWithExport == nil || gotWithExport.BMDExportID == nil || *gotWithExport.BMDExportID != "DE001" {
|
|
t.Fatalf("expected bmd_export_id persisted")
|
|
}
|
|
inactive := &land.Land{Name: "Inactive", LandISOCode: "IN", IsActive: false}
|
|
if err := repo.Create(context.Background(), inactive); err != nil {
|
|
t.Fatalf("create inactive: %v", err)
|
|
}
|
|
gotInactive, err := repo.GetByID(context.Background(), inactive.ID)
|
|
if err != nil {
|
|
t.Fatalf("get inactive: %v", err)
|
|
}
|
|
if gotInactive == nil || gotInactive.IsActive {
|
|
t.Fatalf("expected inactive land persisted as false")
|
|
}
|
|
|
|
sqlDB, _ := db.DB()
|
|
_ = sqlDB.Close()
|
|
if err := repo.Create(context.Background(), &land.Land{Name: "AfterClose", LandISOCode: "AF"}); err == nil {
|
|
t.Fatalf("expected error on closed DB")
|
|
}
|
|
}
|
|
|
|
func TestLandRepositoryUpdate(t *testing.T) {
|
|
db := openLandTestDB(t)
|
|
repo := NewLandRepository(db)
|
|
row := &land.Land{Name: "Old", LandISOCode: "OL"}
|
|
if err := repo.Create(context.Background(), row); err != nil {
|
|
t.Fatalf("seed create: %v", err)
|
|
}
|
|
|
|
row.Name = "New"
|
|
updatedExportID := "DE999"
|
|
row.BMDExportID = &updatedExportID
|
|
if err := repo.Update(context.Background(), row); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
loaded, err := repo.GetByID(context.Background(), row.ID)
|
|
if err != nil || loaded == nil || loaded.Name != "New" {
|
|
t.Fatalf("expected updated row")
|
|
}
|
|
if loaded.BMDExportID == nil || *loaded.BMDExportID != "DE999" {
|
|
t.Fatalf("expected updated bmd_export_id")
|
|
}
|
|
|
|
sqlDB, _ := db.DB()
|
|
_ = sqlDB.Close()
|
|
if err := repo.Update(context.Background(), row); err == nil {
|
|
t.Fatalf("expected error on closed DB")
|
|
}
|
|
}
|
|
|
|
func TestLandRepositoryDelete(t *testing.T) {
|
|
db := openLandTestDB(t)
|
|
repo := NewLandRepository(db)
|
|
row := &land.Land{Name: "DeleteMe", LandISOCode: "DM"}
|
|
if err := repo.Create(context.Background(), row); err != nil {
|
|
t.Fatalf("seed create: %v", err)
|
|
}
|
|
deletedBy := uuidv7.MustBytes()
|
|
|
|
if err := repo.Delete(context.Background(), row.ID, deletedBy); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
got, err := repo.GetByID(context.Background(), row.ID)
|
|
if err != nil {
|
|
t.Fatalf("get by id after delete: %v", err)
|
|
}
|
|
if got != nil {
|
|
t.Fatalf("expected soft deleted row hidden from GetByID")
|
|
}
|
|
}
|
|
|
|
func TestLandRepositoryGetByID(t *testing.T) {
|
|
t.Run("found", func(t *testing.T) {
|
|
db := openLandTestDB(t)
|
|
repo := NewLandRepository(db)
|
|
row := &land.Land{Name: "Found", LandISOCode: "FO"}
|
|
if err := repo.Create(context.Background(), row); err != nil {
|
|
t.Fatalf("seed create: %v", err)
|
|
}
|
|
|
|
got, err := repo.GetByID(context.Background(), row.ID)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if got == nil || got.Name != "Found" {
|
|
t.Fatalf("expected row found")
|
|
}
|
|
})
|
|
|
|
t.Run("not found", func(t *testing.T) {
|
|
db := openLandTestDB(t)
|
|
repo := NewLandRepository(db)
|
|
got, err := repo.GetByID(context.Background(), uuidv7.MustBytes())
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if got != nil {
|
|
t.Fatalf("expected nil for not found")
|
|
}
|
|
})
|
|
|
|
t.Run("db error", func(t *testing.T) {
|
|
db := openLandTestDB(t)
|
|
repo := NewLandRepository(db)
|
|
sqlDB, _ := db.DB()
|
|
_ = sqlDB.Close()
|
|
|
|
if _, err := repo.GetByID(context.Background(), uuidv7.MustBytes()); err == nil {
|
|
t.Fatalf("expected error on closed DB")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestLandRepositoryList(t *testing.T) {
|
|
t.Run("success without limit", func(t *testing.T) {
|
|
db := openLandTestDB(t)
|
|
repo := NewLandRepository(db)
|
|
_ = repo.Create(context.Background(), &land.Land{Name: "C", LandISOCode: "CC"})
|
|
_ = repo.Create(context.Background(), &land.Land{Name: "A", LandISOCode: "AA"})
|
|
|
|
rows, total, err := repo.List(context.Background(), "", "", 0, 0)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if total != 2 || len(rows) != 2 {
|
|
t.Fatalf("expected 2 rows, total=%d len=%d", total, len(rows))
|
|
}
|
|
})
|
|
|
|
t.Run("success with filter sort and limit", func(t *testing.T) {
|
|
db := openLandTestDB(t)
|
|
repo := NewLandRepository(db)
|
|
_ = repo.Create(context.Background(), &land.Land{Name: "Main Base", LandISOCode: "MB"})
|
|
_ = repo.Create(context.Background(), &land.Land{Name: "Backup", LandISOCode: "BK"})
|
|
|
|
rows, total, err := repo.List(context.Background(), "Main", "name DESC", 1, 0)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if total != 1 || len(rows) != 1 || rows[0].Name != "Main Base" {
|
|
t.Fatalf("unexpected list result")
|
|
}
|
|
})
|
|
|
|
t.Run("success filter by land iso code", func(t *testing.T) {
|
|
db := openLandTestDB(t)
|
|
repo := NewLandRepository(db)
|
|
_ = repo.Create(context.Background(), &land.Land{Name: "Germany", LandISOCode: "DE"})
|
|
_ = repo.Create(context.Background(), &land.Land{Name: "Indonesia", LandISOCode: "ID"})
|
|
|
|
rows, total, err := repo.List(context.Background(), "DE", "name ASC", 10, 0)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if total != 1 || len(rows) != 1 || rows[0].LandISOCode != "DE" {
|
|
t.Fatalf("expected filter to match land_iso_code, got total=%d len=%d", total, len(rows))
|
|
}
|
|
})
|
|
|
|
t.Run("includes federal state aggregate fields in view", func(t *testing.T) {
|
|
db := openLandTestDB(t)
|
|
repo := NewLandRepository(db)
|
|
row := &land.Land{Name: "Germany", LandISOCode: "DE"}
|
|
if err := repo.Create(context.Background(), row); err != nil {
|
|
t.Fatalf("seed create: %v", err)
|
|
}
|
|
if err := db.Table("federal_states").Create([]map[string]any{
|
|
{"id": uuidv7.MustBytes(), "name": "Bayern", "land_id": row.ID, "deleted_at": nil},
|
|
{"id": uuidv7.MustBytes(), "name": "Berlin", "land_id": row.ID, "deleted_at": nil},
|
|
}).Error; err != nil {
|
|
t.Fatalf("seed federal_states: %v", err)
|
|
}
|
|
|
|
rows, total, err := repo.ListView(context.Background(), "Germany", "", 10, 0)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if total != 1 || len(rows) != 1 {
|
|
t.Fatalf("unexpected rows: total=%d len=%d", total, len(rows))
|
|
}
|
|
if rows[0].FederalStateTotal != 2 || len(rows[0].FederalStateList) != 2 {
|
|
t.Fatalf("expected federal states attached in list result")
|
|
}
|
|
})
|
|
|
|
t.Run("count error", func(t *testing.T) {
|
|
db := openLandTestDB(t)
|
|
repo := NewLandRepository(db)
|
|
if err := db.Migrator().DropTable(&land.Land{}); err != nil {
|
|
t.Fatalf("drop table: %v", err)
|
|
}
|
|
|
|
if _, _, err := repo.List(context.Background(), "", "", 10, 0); err == nil {
|
|
t.Fatalf("expected count error")
|
|
}
|
|
})
|
|
|
|
t.Run("find error", func(t *testing.T) {
|
|
db := openLandTestDB(t)
|
|
repo := NewLandRepository(db)
|
|
_ = repo.Create(context.Background(), &land.Land{Name: "Main", LandISOCode: "MN"})
|
|
|
|
if _, _, err := repo.List(context.Background(), "", "name ASC, )", 10, 0); err == nil {
|
|
t.Fatalf("expected find error from invalid sort")
|
|
}
|
|
})
|
|
|
|
t.Run("default order active sortkey first and inactive last", func(t *testing.T) {
|
|
db := openLandTestDB(t)
|
|
repo := NewLandRepository(db)
|
|
_ = repo.Create(context.Background(), &land.Land{Name: "Gamma", LandISOCode: "GA", IsActive: true})
|
|
_ = repo.Create(context.Background(), &land.Land{Name: "Beta", LandISOCode: "BT", SortKey: intPtrLandRepo(0), IsActive: true})
|
|
_ = repo.Create(context.Background(), &land.Land{Name: "Charlie", LandISOCode: "CH", SortKey: intPtrLandRepo(2), IsActive: true})
|
|
_ = repo.Create(context.Background(), &land.Land{Name: "Alpha", LandISOCode: "AL", SortKey: intPtrLandRepo(1), IsActive: true})
|
|
_ = repo.Create(context.Background(), &land.Land{Name: "Zulu", LandISOCode: "ZU", IsActive: false})
|
|
_ = repo.Create(context.Background(), &land.Land{Name: "Bravo", LandISOCode: "BR", SortKey: intPtrLandRepo(9), IsActive: false})
|
|
|
|
rows, total, err := repo.List(context.Background(), "", "", 0, 0)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if total != 6 || len(rows) != 6 {
|
|
t.Fatalf("unexpected total/len total=%d len=%d", total, len(rows))
|
|
}
|
|
|
|
gotOrder := []string{rows[0].Name, rows[1].Name, rows[2].Name, rows[3].Name, rows[4].Name, rows[5].Name}
|
|
wantOrder := []string{"Beta", "Alpha", "Charlie", "Gamma", "Bravo", "Zulu"}
|
|
for i := range wantOrder {
|
|
if gotOrder[i] != wantOrder[i] {
|
|
t.Fatalf("unexpected default order: got=%v want=%v", gotOrder, wantOrder)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func intPtrLandRepo(v int) *int { return &v }
|