package mysql import ( "context" "fmt" "testing" "time" "gorm.io/driver/sqlite" "gorm.io/gorm" otherperson "wucher/internal/domain/other_person" ) func openOtherPersonTestDB(t *testing.T) *gorm.DB { t.Helper() dsn := fmt.Sprintf("file:other_person_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(&otherperson.OtherPerson{}); err != nil { t.Fatalf("auto migrate: %v", err) } return db } func TestOtherPersonRepositoryUpsertDedup(t *testing.T) { db := openOtherPersonTestDB(t) repo := NewOtherPersonRepository(db) ctx := context.Background() first := &otherperson.OtherPerson{Name: " Guest One ", MobilePhone: " +43-1 ", Email: " a@x.io "} if err := repo.Upsert(ctx, first); err != nil { t.Fatalf("first upsert: %v", err) } if len(first.ID) == 0 { t.Fatalf("expected id set on create") } // values should be trimmed on create. if first.Name != "Guest One" || first.MobilePhone != "+43-1" || first.Email != "a@x.io" { t.Fatalf("expected trimmed values, got %q/%q/%q", first.Name, first.MobilePhone, first.Email) } // Same identity (even with different whitespace) must reuse the existing row, not create a new one. second := &otherperson.OtherPerson{Name: "Guest One", MobilePhone: "+43-1", Email: "a@x.io"} if err := repo.Upsert(ctx, second); err != nil { t.Fatalf("second upsert: %v", err) } if string(second.ID) != string(first.ID) { t.Fatalf("expected reuse of existing id") } // A different identity creates a new row. third := &otherperson.OtherPerson{Name: "Guest Two", MobilePhone: "+43-2", Email: "b@x.io"} if err := repo.Upsert(ctx, third); err != nil { t.Fatalf("third upsert: %v", err) } if string(third.ID) == string(first.ID) { t.Fatalf("expected new id for distinct identity") } _, total, err := repo.Search(ctx, "", 0, 0) if err != nil { t.Fatalf("search: %v", err) } if total != 2 { t.Fatalf("expected 2 master rows, got %d", total) } } func TestOtherPersonRepositorySearch(t *testing.T) { db := openOtherPersonTestDB(t) repo := NewOtherPersonRepository(db) ctx := context.Background() for _, p := range []*otherperson.OtherPerson{ {Name: "Alice Anderson", MobilePhone: "111", Email: "alice@x.io"}, {Name: "Bob Brown", MobilePhone: "222", Email: "bob@x.io"}, } { if err := repo.Upsert(ctx, p); err != nil { t.Fatalf("seed: %v", err) } } rows, total, err := repo.Search(ctx, "alice", 0, 0) if err != nil { t.Fatalf("search by name: %v", err) } if total != 1 || len(rows) != 1 || rows[0].Name != "Alice Anderson" { t.Fatalf("expected only Alice, got total=%d rows=%d", total, len(rows)) } // Match on email fragment too. rows, total, err = repo.Search(ctx, "bob@", 0, 0) if err != nil { t.Fatalf("search by email: %v", err) } if total != 1 || len(rows) != 1 || rows[0].Name != "Bob Brown" { t.Fatalf("expected only Bob, got total=%d rows=%d", total, len(rows)) } // Empty query returns everything. _, total, err = repo.Search(ctx, " ", 0, 0) if err != nil { t.Fatalf("search empty: %v", err) } if total != 2 { t.Fatalf("expected 2 rows for empty query, got %d", total) } } func TestOtherPersonRepositoryCreateUpdateDelete(t *testing.T) { db := openOtherPersonTestDB(t) repo := NewOtherPersonRepository(db) ctx := context.Background() row := &otherperson.OtherPerson{Name: " Carol ", MobilePhone: " 333 ", Email: " carol@x.io "} if err := repo.Create(ctx, row); err != nil { t.Fatalf("create: %v", err) } if len(row.ID) == 0 || row.Name != "Carol" { t.Fatalf("expected trimmed create with id, got %q id=%d", row.Name, len(row.ID)) } // FindByIdentity trims and matches. found, err := repo.FindByIdentity(ctx, "Carol", "333", "carol@x.io") if err != nil { t.Fatalf("find identity: %v", err) } if found == nil || string(found.ID) != string(row.ID) { t.Fatalf("expected to find created row") } miss, err := repo.FindByIdentity(ctx, "Nobody", "", "") if err != nil { t.Fatalf("find miss: %v", err) } if miss != nil { t.Fatalf("expected nil for missing identity") } // Update replaces identity fields. row.Name = "Caroline" row.Email = "caroline@x.io" if err := repo.Update(ctx, row); err != nil { t.Fatalf("update: %v", err) } got, err := repo.GetByID(ctx, row.ID) if err != nil { t.Fatalf("get after update: %v", err) } if got == nil || got.Name != "Caroline" || got.Email != "caroline@x.io" { t.Fatalf("update not persisted: %+v", got) } // Delete soft-deletes: GetByID/Search no longer return it. if err := repo.Delete(ctx, row.ID, nil); err != nil { t.Fatalf("delete: %v", err) } got, err = repo.GetByID(ctx, row.ID) if err != nil { t.Fatalf("get after delete: %v", err) } if got != nil { t.Fatalf("expected soft-deleted row to be hidden") } _, total, err := repo.Search(ctx, "", 0, 0) if err != nil { t.Fatalf("search after delete: %v", err) } if total != 0 { t.Fatalf("expected 0 live rows after delete, got %d", total) } }