package service import ( "context" "errors" "testing" "wucher/internal/domain/complaint" "wucher/internal/shared/pkg/userctx" ) type stubComplaintRepo struct { row *complaint.Complaint open bool unactioned bool active []complaint.Complaint createErr error created *complaint.Complaint markedHeli []byte markedComplaint []byte markedFlight []byte } func (s *stubComplaintRepo) Create(_ context.Context, row *complaint.Complaint) error { s.created = row return s.createErr } func (s *stubComplaintRepo) Update(_ context.Context, _ *complaint.Complaint) error { return nil } func (s *stubComplaintRepo) Delete(_ context.Context, _, _ []byte) error { return nil } func (s *stubComplaintRepo) GetByID(_ context.Context, _ []byte) (*complaint.Complaint, error) { return s.row, nil } func (s *stubComplaintRepo) ListByHelicopter(_ context.Context, _ []byte, _, _ int) ([]complaint.Complaint, int64, error) { return s.active, int64(len(s.active)), nil } func (s *stubComplaintRepo) List(_ context.Context, _ complaint.ListFilter, _ string, _, _ int) ([]complaint.Complaint, int64, error) { return s.active, int64(len(s.active)), nil } func (s *stubComplaintRepo) HelicopterHasOpenComplaint(_ context.Context, _ []byte) (bool, error) { return s.open, nil } func (s *stubComplaintRepo) ActiveGroundingByHelicopterIDs(_ context.Context, _ [][]byte) ([]complaint.Complaint, error) { return s.active, nil } func (s *stubComplaintRepo) OpenByHelicopterIDs(_ context.Context, _ [][]byte) ([]complaint.Complaint, error) { return s.active, nil } func (s *stubComplaintRepo) HelicopterHasUnactionedComplaint(_ context.Context, _ []byte) (bool, error) { return s.unactioned, nil } func (s *stubComplaintRepo) MarkFixedByHelicopter(_ context.Context, helicopterID, _, _ []byte) error { s.markedHeli = helicopterID return nil } func (s *stubComplaintRepo) MarkFixedByComplaint(_ context.Context, complaintID, _, _ []byte) error { s.markedComplaint = complaintID return nil } func (s *stubComplaintRepo) MarkFixedByFlight(_ context.Context, flightID, _, _ []byte) error { s.markedFlight = flightID return nil } func TestComplaintServiceDelegation(t *testing.T) { repo := &stubComplaintRepo{ row: &complaint.Complaint{}, open: true, unactioned: true, active: []complaint.Complaint{{}}, } svc := NewComplaintService(repo) ctx := context.Background() if err := svc.Create(ctx, &complaint.Complaint{}); err != nil { t.Fatalf("Create: %v", err) } if err := svc.Update(ctx, &complaint.Complaint{}); err != nil { t.Fatalf("Update: %v", err) } if err := svc.Delete(ctx, []byte("x"), nil); err != nil { t.Fatalf("Delete: %v", err) } if _, err := svc.GetByID(ctx, []byte("x")); err != nil { t.Fatalf("GetByID: %v", err) } if rows, _, err := svc.ListByHelicopter(ctx, []byte("h"), 10, 0); err != nil || len(rows) != 1 { t.Fatalf("ListByHelicopter: %v", err) } if _, _, err := svc.List(ctx, complaint.ListFilter{}, "", 10, 0); err != nil { t.Fatalf("List: %v", err) } if open, err := svc.HelicopterHasOpenComplaint(ctx, []byte("h")); err != nil || !open { t.Fatalf("HelicopterHasOpenComplaint: %v", err) } if un, err := svc.HelicopterHasUnactionedComplaint(ctx, []byte("h")); err != nil || !un { t.Fatalf("HelicopterHasUnactionedComplaint: %v", err) } if _, err := svc.ActiveGroundingByHelicopterIDs(ctx, [][]byte{[]byte("h")}); err != nil { t.Fatalf("ActiveGroundingByHelicopterIDs: %v", err) } if err := svc.MarkFixedByHelicopter(ctx, []byte("heli"), []byte("easa"), []byte("actor")); err != nil || string(repo.markedHeli) != "heli" { t.Fatalf("MarkFixedByHelicopter: %v", err) } repo.createErr = errors.New("boom") if err := svc.Create(ctx, &complaint.Complaint{}); err == nil { t.Fatal("expected create error to propagate") } } type stubNameGetter struct{} func (stubNameGetter) GetLastName(_ context.Context, id []byte) (string, error) { return "Name-" + string(id), nil } func TestComplaintServiceResolveUserNames(t *testing.T) { svc := NewComplaintService(&stubComplaintRepo{}) ctx := userctx.WithGetter(context.Background(), stubNameGetter{}) rows := []complaint.Complaint{ {ReportedBy: []byte("u1"), MELClassifiedBy: []byte("u1"), NSRDecidedBy: []byte("u2")}, {}, // all-empty ids skipped } names, err := svc.ResolveUserNames(ctx, rows) if err != nil { t.Fatalf("err: %v", err) } if names["u1"] != "Name-u1" || names["u2"] != "Name-u2" { t.Fatalf("unexpected names: %v", names) } if len(names) != 2 { t.Fatalf("dup/empty ids should be deduped/skipped, got %v", names) } // No getter in context -> no names resolved. empty, _ := svc.ResolveUserNames(context.Background(), rows) if len(empty) != 0 { t.Fatalf("expected no names without getter, got %v", empty) } }