package service import ( "context" "errors" "testing" "wucher/internal/domain/facility" ) type facilityRepoMock struct { createCalled bool updateCalled bool deleteCalled bool getByIDCalled bool listCalled bool lastCreate *facility.Facility lastUpdate *facility.Facility lastDeleteID []byte lastGetByID []byte lastListFilter string lastListCategory string lastListSort string lastListLimit int lastListOffset int getByIDResult *facility.Facility listResult []facility.Facility listTotal int64 createErr error updateErr error deleteErr error getByIDErr error listErr error } func (m *facilityRepoMock) Create(_ context.Context, row *facility.Facility) error { m.createCalled = true m.lastCreate = row return m.createErr } func (m *facilityRepoMock) Update(_ context.Context, row *facility.Facility) error { m.updateCalled = true m.lastUpdate = row return m.updateErr } func (m *facilityRepoMock) Delete(_ context.Context, id []byte, _ []byte) error { m.deleteCalled = true m.lastDeleteID = id return m.deleteErr } func (m *facilityRepoMock) GetByID(_ context.Context, id []byte) (*facility.Facility, error) { m.getByIDCalled = true m.lastGetByID = id return m.getByIDResult, m.getByIDErr } func (m *facilityRepoMock) List(_ context.Context, filter, category, sort string, limit, offset int) ([]facility.Facility, int64, error) { m.listCalled = true m.lastListFilter = filter m.lastListCategory = category m.lastListSort = sort m.lastListLimit = limit m.lastListOffset = offset return m.listResult, m.listTotal, m.listErr } func TestNewFacilityService(t *testing.T) { repo := &facilityRepoMock{} svc := NewFacilityService(repo) if svc == nil || svc.repo == nil { t.Fatalf("expected service and repo initialized") } } func TestFacilityServiceCreate(t *testing.T) { repo := &facilityRepoMock{} svc := NewFacilityService(repo) row := &facility.Facility{Category: "HEMS", Name: "HEC Sling", Type: "Sling"} if err := svc.Create(context.Background(), row); err != nil { t.Fatalf("unexpected error: %v", err) } if !repo.createCalled || repo.lastCreate != row { t.Fatalf("expected Create forwarded to repo") } repo.createErr = errors.New("create failed") if err := svc.Create(context.Background(), row); err == nil { t.Fatalf("expected error from repo") } } func TestFacilityServiceUpdate(t *testing.T) { repo := &facilityRepoMock{} svc := NewFacilityService(repo) row := &facility.Facility{Category: "HEMS", Name: "HEC Sling", Type: "Sling"} if err := svc.Update(context.Background(), row); err != nil { t.Fatalf("unexpected error: %v", err) } if !repo.updateCalled || repo.lastUpdate != row { t.Fatalf("expected Update forwarded to repo") } repo.updateErr = errors.New("update failed") if err := svc.Update(context.Background(), row); err == nil { t.Fatalf("expected error from repo") } } func TestFacilityServiceDelete(t *testing.T) { repo := &facilityRepoMock{} svc := NewFacilityService(repo) id := []byte("id") if err := svc.Delete(context.Background(), id, nil); err != nil { t.Fatalf("unexpected error: %v", err) } if !repo.deleteCalled { t.Fatalf("expected Delete forwarded to repo") } if string(repo.lastDeleteID) != string(id) { t.Fatalf("expected delete id forwarded") } repo.deleteErr = errors.New("delete failed") if err := svc.Delete(context.Background(), id, nil); err == nil { t.Fatalf("expected error from repo") } } func TestFacilityServiceGetByID(t *testing.T) { expected := &facility.Facility{Category: "HEMS", Name: "HEC Sling", Type: "Sling"} repo := &facilityRepoMock{getByIDResult: expected} svc := NewFacilityService(repo) id := []byte("id") got, err := svc.GetByID(context.Background(), id) if err != nil { t.Fatalf("unexpected error: %v", err) } if got != expected { t.Fatalf("unexpected result") } if !repo.getByIDCalled || string(repo.lastGetByID) != string(id) { t.Fatalf("expected GetByID forwarded") } repo.getByIDErr = errors.New("get failed") if _, err := svc.GetByID(context.Background(), id); err == nil { t.Fatalf("expected error from repo") } } func TestFacilityServiceList(t *testing.T) { repo := &facilityRepoMock{ listResult: []facility.Facility{{Category: "HEMS", Name: "HEC Sling", Type: "Sling"}}, listTotal: 1, } svc := NewFacilityService(repo) rows, total, err := svc.List(context.Background(), "find", "HEMS", "-name", 10, 20) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(rows) != 1 || total != 1 { t.Fatalf("unexpected list result") } if !repo.listCalled { t.Fatalf("expected List forwarded to repo") } if repo.lastListFilter != "find" || repo.lastListCategory != "HEMS" || repo.lastListSort != "name DESC" || repo.lastListLimit != 10 || repo.lastListOffset != 20 { t.Fatalf("unexpected list args forwarded") } rows, total, err = svc.List(context.Background(), "find", "HEMS", "sortkey", 10, 20) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(rows) != 1 || total != 1 { t.Fatalf("unexpected list result") } if repo.lastListSort != "is_active DESC, CASE WHEN is_active = 1 AND sortkey IS NOT NULL AND sortkey >= 0 THEN 0 ELSE 1 END ASC, CASE WHEN is_active = 1 AND sortkey IS NOT NULL AND sortkey >= 0 THEN sortkey END ASC, name ASC" { t.Fatalf("unexpected sortkey args forwarded: %q", repo.lastListSort) } repo.listErr = errors.New("list failed") if _, _, err := svc.List(context.Background(), "", "", "unknown", 1, 0); err == nil { t.Fatalf("expected error from repo") } if repo.lastListSort != "" { t.Fatalf("expected unknown sort normalized to empty") } } func TestNormalizeFacilitySort(t *testing.T) { cases := map[string]string{ "sortkey": "is_active DESC, CASE WHEN is_active = 1 AND sortkey IS NOT NULL AND sortkey >= 0 THEN 0 ELSE 1 END ASC, CASE WHEN is_active = 1 AND sortkey IS NOT NULL AND sortkey >= 0 THEN sortkey END ASC, name ASC", "-sortkey": "is_active DESC, CASE WHEN is_active = 1 AND sortkey IS NOT NULL AND sortkey >= 0 THEN 0 ELSE 1 END ASC, CASE WHEN is_active = 1 AND sortkey IS NOT NULL AND sortkey >= 0 THEN sortkey END DESC, name ASC", "category": "category ASC", "-category": "category DESC", "name": "name ASC", "-name": "name DESC", "type": "type ASC", "-type": "type DESC", "is_active": "is_active ASC", "-is_active": "is_active DESC", "created_at": "created_at ASC", "-created_at": "created_at DESC", "updated_at": "updated_at ASC", "-updated_at": "updated_at DESC", "unknown": "", "": "", } for in, want := range cases { if got := normalizeFacilitySort(in); got != want { t.Fatalf("normalizeFacilitySort(%q) = %q, want %q", in, got, want) } } }