Files
backend_fiesta/services/catalogueUpload_test.go
2026-08-31 12:33:10 +05:30

161 lines
5.2 KiB
Go

package services
import (
"errors"
"testing"
"nearle/models"
"nearle/repositories"
)
/*
What these defend.
A receipt exists for one reason: the ingest service's batch id is the ONLY
credential for reading a result back, it is handed out exactly once, and an
unreviewed drop is deleted after seven days. So the failure that matters is not
a wrong count — it is a receipt that gets stored without the id, or a receipt
whose status is reported as something more finished than it is.
`pending` in particular is not a formality. Nothing runs on arrival at the
ingest service; a drop waits for an admin to press Start. A receipt that
defaulted to `queued` would claim the pipeline had the file when it is in fact
waiting for a person, and the counts beside it — all zero, because nothing has
run — would read as an import that found nothing.
*/
// fakeUploadRepo records what reached the repository. Embedded rather than
// implemented: a method these tests do not exercise is nil and panics loudly if
// the service starts calling it, which is the failure worth having.
type fakeUploadRepo struct {
repositories.CatalogueUploadRepository
recorded []models.CatalogueUpload
updated []models.CatalogueUpload
shelved []struct {
batchID string
shelved, skipped int
}
err error
}
func (f *fakeUploadRepo) Record(upload *models.CatalogueUpload) error {
if f.err != nil {
return f.err
}
f.recorded = append(f.recorded, *upload)
return nil
}
func (f *fakeUploadRepo) UpdateStatus(upload *models.CatalogueUpload) error {
if f.err != nil {
return f.err
}
f.updated = append(f.updated, *upload)
return nil
}
func (f *fakeUploadRepo) MarkShelved(batchID string, shelved, skipped int) error {
if f.err != nil {
return f.err
}
f.shelved = append(f.shelved, struct {
batchID string
shelved, skipped int
}{batchID, shelved, skipped})
return nil
}
func (f *fakeUploadRepo) List(tenantID, locationID, pageNo, pageSize int) ([]models.CatalogueUpload, error) {
return nil, f.err
}
// A drop that has only just been accepted has not run, and the receipt has to
// say so. `queued` would be wrong in the specific way that matters: it means
// "approved, waiting for the worker", and this file is waiting for a person.
func TestRecordDefaultsToPendingNotQueued(t *testing.T) {
repo := &fakeUploadRepo{}
svc := NewCatalogueUploadService(repo)
upload := &models.CatalogueUpload{Batchid: "abc123", Tenantid: 1135}
if err := svc.Record(upload); err != nil {
t.Fatalf("Record: %v", err)
}
if len(repo.recorded) != 1 {
t.Fatalf("want 1 recorded receipt, got %d", len(repo.recorded))
}
if got := repo.recorded[0].Laststatus; got != "pending" {
t.Errorf("a just-accepted drop should read pending, got %q", got)
}
}
// A caller who already knows the status keeps it. The default is a fallback for
// a fresh drop, not an override — a receipt being re-recorded from a poll that
// already saw `running` must not be walked backwards.
func TestRecordKeepsAnExplicitStatus(t *testing.T) {
repo := &fakeUploadRepo{}
svc := NewCatalogueUploadService(repo)
if err := svc.Record(&models.CatalogueUpload{
Batchid: "abc123", Tenantid: 1135, Laststatus: "running",
}); err != nil {
t.Fatalf("Record: %v", err)
}
if got := repo.recorded[0].Laststatus; got != "running" {
t.Errorf("want the caller's own status preserved, got %q", got)
}
}
// A repository failure is the caller's problem, not something to swallow. The
// receipt is the only durable copy of the batch id, so a write that did not
// happen must never look like one that did.
func TestRecordSurfacesRepositoryFailure(t *testing.T) {
boom := errors.New("unique violation")
svc := NewCatalogueUploadService(&fakeUploadRepo{err: boom})
err := svc.Record(&models.CatalogueUpload{Batchid: "abc123", Tenantid: 1135})
if !errors.Is(err, boom) {
t.Fatalf("want the repository error surfaced, got %v", err)
}
}
// Zero is a real answer. A run that inserted nothing because every product was
// already present is a complete success, and the counts have to be storable as
// zero rather than treated as "not supplied".
func TestUpdateStatusCarriesZeroCounts(t *testing.T) {
repo := &fakeUploadRepo{}
svc := NewCatalogueUploadService(repo)
if err := svc.UpdateStatus(&models.CatalogueUpload{
Batchid: "abc123", Laststatus: "done", Inserted: 0, Skipped: 12,
}); err != nil {
t.Fatalf("UpdateStatus: %v", err)
}
if len(repo.updated) != 1 {
t.Fatalf("want 1 update, got %d", len(repo.updated))
}
if repo.updated[0].Inserted != 0 || repo.updated[0].Skipped != 12 {
t.Errorf("counts mangled: %+v", repo.updated[0])
}
}
// Shelving is a separate claim from ingestion and travels separately. The
// ingest service confirms the shared global catalogue, which holds no price and
// no stock; only this records that a shop can actually sell the products.
func TestMarkShelvedPassesBothCounts(t *testing.T) {
repo := &fakeUploadRepo{}
svc := NewCatalogueUploadService(repo)
if err := svc.MarkShelved("abc123", 18, 2); err != nil {
t.Fatalf("MarkShelved: %v", err)
}
if len(repo.shelved) != 1 {
t.Fatalf("want 1 shelving record, got %d", len(repo.shelved))
}
got := repo.shelved[0]
if got.batchID != "abc123" || got.shelved != 18 || got.skipped != 2 {
t.Errorf("want abc123/18/2, got %v", got)
}
}