package service import ( "context" "testing" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "apskel-pos-be/internal/entities" "apskel-pos-be/internal/logger" "apskel-pos-be/internal/models" "apskel-pos-be/internal/repository" ) type discrepancyFinderFake struct { found []repository.WalletDiscrepancy } func (f *discrepancyFinderFake) FindDiscrepancies(context.Context, int) ([]repository.WalletDiscrepancy, error) { return f.found, nil } type orgUsersFake map[uuid.UUID][]*entities.User func (f orgUsersFake) GetByOrganizationID(_ context.Context, org uuid.UUID) ([]*entities.User, error) { return f[org], nil } type notifierFake struct { sent []*models.SendNotificationRequest } func (f *notifierFake) Send(_ context.Context, req *models.SendNotificationRequest) (*models.NotificationResponse, error) { f.sent = append(f.sent, req) return &models.NotificationResponse{}, nil } func TestWalletReconciliationJob(t *testing.T) { logger.Setup("fatal", "json") org := uuid.New() admin, owner, manager, cashier := uuid.New(), uuid.New(), uuid.New(), uuid.New() users := orgUsersFake{org: { {ID: admin, Role: entities.RoleAdmin}, {ID: owner, Role: entities.RoleOwner}, {ID: manager, Role: entities.RoleManager}, {ID: cashier, Role: entities.RoleCashier}, }} finder := &discrepancyFinderFake{} notifier := ¬ifierFake{} job := NewWalletReconciliationJob(finder, users, notifier) ctx := context.Background() // Consistent data: nothing reported. found, err := job.RunOnce(ctx) require.NoError(t, err) assert.Empty(t, found) assert.Empty(t, notifier.sent) // A discrepancy notifies the organization's admins, owners and managers. customer := uuid.New() lot := uuid.New() finder.found = []repository.WalletDiscrepancy{ {Check: repository.WalletCheckBalanceVsLots, OrganizationID: org, CustomerID: customer, Currency: "POINT", Expected: 100, Actual: 105}, {Check: repository.WalletCheckLotVsAllocations, OrganizationID: org, CustomerID: customer, Currency: "POINT", ObjectID: &lot, Expected: 50, Actual: 55}, } found, err = job.RunOnce(ctx) require.NoError(t, err) assert.Len(t, found, 2) require.Len(t, notifier.sent, 1) sent := notifier.sent[0] assert.ElementsMatch(t, []uuid.UUID{admin, owner, manager}, sent.ReceiverIDs, "cashiers are not told") assert.Equal(t, &org, sent.NotifiableID) assert.Equal(t, 2, sent.Data["discrepancies"]) assert.Equal(t, []string{customer.String()}, sent.Data["customer_ids"]) assert.Equal(t, entities.NotificationPriorityHigh, sent.Priority) // The same problem, still unfixed and in a different order, does not notify again. finder.found = []repository.WalletDiscrepancy{finder.found[1], finder.found[0]} _, err = job.RunOnce(ctx) require.NoError(t, err) assert.Len(t, notifier.sent, 1) // A changed problem does. finder.found = finder.found[:1] _, err = job.RunOnce(ctx) require.NoError(t, err) assert.Len(t, notifier.sent, 2) // Once clean the organization is forgotten, so the same problem coming back // notifies again. previous := finder.found finder.found = nil _, err = job.RunOnce(ctx) require.NoError(t, err) assert.Len(t, notifier.sent, 2) finder.found = previous _, err = job.RunOnce(ctx) require.NoError(t, err) assert.Len(t, notifier.sent, 3) } func TestWalletReconciliationJobStartStop(t *testing.T) { logger.Setup("fatal", "json") job := NewWalletReconciliationJob(&discrepancyFinderFake{}, orgUsersFake{}, ¬ifierFake{}) job.Start(0) job.Stop() job.Stop() // stopping twice is harmless }