package service import ( "context" "errors" "testing" "wucher/internal/domain/medicine" ) type medicineRepoMock struct { createCalled bool updateCalled bool deleteCalled bool getByIDCalled bool listCalled bool lastCreate *medicine.Medicine lastUpdate *medicine.Medicine lastDeleteID []byte lastDeleteBy []byte lastGetByID []byte lastListFilter string lastListSort string lastListLimit int lastListOffset int getByIDResult *medicine.Medicine listResult []medicine.Medicine listTotal int64 createErr error updateErr error deleteErr error getByIDErr error listErr error } func (m *medicineRepoMock) Create(_ context.Context, row *medicine.Medicine) error { m.createCalled = true m.lastCreate = row return m.createErr } func (m *medicineRepoMock) Update(_ context.Context, row *medicine.Medicine) error { m.updateCalled = true m.lastUpdate = row return m.updateErr } func (m *medicineRepoMock) Delete(_ context.Context, id []byte, deletedBy []byte) error { m.deleteCalled = true m.lastDeleteID = id m.lastDeleteBy = deletedBy return m.deleteErr } func (m *medicineRepoMock) GetByID(_ context.Context, id []byte) (*medicine.Medicine, error) { m.getByIDCalled = true m.lastGetByID = id return m.getByIDResult, m.getByIDErr } func (m *medicineRepoMock) List(_ context.Context, filter, sort string, limit, offset int) ([]medicine.Medicine, int64, error) { m.listCalled = true m.lastListFilter = filter m.lastListSort = sort m.lastListLimit = limit m.lastListOffset = offset return m.listResult, m.listTotal, m.listErr } func TestNewMedicineService(t *testing.T) { repo := &medicineRepoMock{} svc := NewMedicineService(repo) if svc == nil || svc.repo == nil { t.Fatalf("expected service and repo initialized") } } func TestMedicineServiceCreate(t *testing.T) { repo := &medicineRepoMock{} svc := NewMedicineService(repo) row := &medicine.Medicine{Name: "Paracetamol", Pack:"500mg", Column: 1} 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 TestMedicineServiceUpdate(t *testing.T) { repo := &medicineRepoMock{} svc := NewMedicineService(repo) row := &medicine.Medicine{Name: "Paracetamol", Pack:"500mg", Column: 1} 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 TestMedicineServiceDelete(t *testing.T) { repo := &medicineRepoMock{} svc := NewMedicineService(repo) id := []byte("id") deletedBy := []byte("by") if err := svc.Delete(context.Background(), id, deletedBy); err != nil { t.Fatalf("unexpected error: %v", err) } if !repo.deleteCalled { t.Fatalf("expected Delete forwarded to repo") } if string(repo.lastDeleteID) != string(id) || string(repo.lastDeleteBy) != string(deletedBy) { t.Fatalf("expected delete params forwarded") } repo.deleteErr = errors.New("delete failed") if err := svc.Delete(context.Background(), id, deletedBy); err == nil { t.Fatalf("expected error from repo") } } func TestMedicineServiceGetByID(t *testing.T) { expected := &medicine.Medicine{Name: "Paracetamol", Pack:"500mg", Column: 1} repo := &medicineRepoMock{getByIDResult: expected} svc := NewMedicineService(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 TestMedicineServiceList(t *testing.T) { repo := &medicineRepoMock{ listResult: []medicine.Medicine{{Name: "Paracetamol", Pack:"500mg", Column: 1}}, listTotal: 1, } svc := NewMedicineService(repo) rows, total, err := svc.List(context.Background(), "find", "column", 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.lastListSort != "`column` ASC" || repo.lastListLimit != 10 || repo.lastListOffset != 20 { t.Fatalf("unexpected list args forwarded") } rows, total, err = svc.List(context.Background(), "find", "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.lastListSort != "name ASC" { t.Fatalf("unexpected name sort 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") } } func TestNormalizeMedicineSort(t *testing.T) { cases := map[string]string{ "name": "name ASC", "-name": "name DESC", "note": "COALESCE(NULLIF(note, ''), pack) ASC", "-note": "COALESCE(NULLIF(note, ''), pack) DESC", "pack": "pack ASC", "-pack": "pack DESC", "unit": "unit ASC", "-unit": "unit DESC", "column": "`column` ASC", "-column": "`column` 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 := normalizeMedicineSort(in); got != want { t.Fatalf("normalizeMedicineSort(%q) = %q, want %q", in, got, want) } } }