Added a ClassificationsUpdated event

SaveOpportunities now reports the classification codes a save brings into
use (AddedClassifications, in full) and takes out of use
(RemovedClassificationIDs), by comparing the codes in use before and after
the save inside its transaction. The fetcher publishes them as a
ClassificationsUpdated event, {"added":[codes],"removed":[ids]}, never null,
before the save's OpportunitiesUpdated, so clients can keep their list of
classifications without asking the API again.
This commit is contained in:
bwilliams committed 2026-10-16 15:24:37 +01:00
1 parent aa06b94870
commit 51d6470649
6 files changed
+287 -3

No files matched your search

+66
View File
@@ -944,3 +944,69 @@ func TestListClassifications(t *testing.T) {
t.Errorf("after the change: codes = %s; want 03000000", got)
}
}
// SaveOpportunities reports the classification codes a save brings into use
// and those it takes out of use, and none when the set doesn't change.
func TestSaveOpportunitiesClassificationChanges(t *testing.T) {
s := openStore(t)
ctx := context.Background()
save := func(opportunity *types.Opportunity) store.SaveMultipleResult {
t.Helper()
total, err := s.SaveOpportunities(ctx, []*types.Opportunity{opportunity})
if err != nil {
t.Fatal(err)
}
return total
}
codesOf := func(classifications []types.ClassificationCode) string {
var codes []string
for _, classification := range classifications {
codes = append(codes, classification.Code)
}
return strings.Join(codes, ",")
}
total := save(opportunity("ocds-1", "First", 1, "r1"))
if codesOf(total.AddedClassifications) != "03000000,72000000" ||
total.RemovedClassificationIDs != nil {
t.Fatalf(
"first save: added %v, removed %v; want both codes added",
total.AddedClassifications, total.RemovedClassificationIDs,
)
}
removedID := total.AddedClassifications[1].ID // 72000000
total = save(opportunity("ocds-2", "Second", 1, "r1"))
if total.AddedClassifications != nil ||
total.RemovedClassificationIDs != nil {
t.Errorf(
"codes already in use: added %v, removed %v; want none",
total.AddedClassifications, total.RemovedClassificationIDs,
)
}
// ocds-1 drops 72000000, which ocds-2 still has: still in use.
newer := opportunity("ocds-1", "Newer", 2, "r2")
newer.Classifications = newer.Classifications[1:]
total = save(newer)
if total.AddedClassifications != nil ||
total.RemovedClassificationIDs != nil {
t.Errorf(
"code still in use: added %v, removed %v; want none",
total.AddedClassifications, total.RemovedClassificationIDs,
)
}
// ocds-2 drops it too: it goes out of use.
newer = opportunity("ocds-2", "Newer", 2, "r2")
newer.Classifications = newer.Classifications[1:]
total = save(newer)
if total.AddedClassifications != nil ||
len(total.RemovedClassificationIDs) != 1 ||
total.RemovedClassificationIDs[0] != removedID {
t.Errorf(
"code out of use: added %v, removed %v; want removed [%d]",
total.AddedClassifications, total.RemovedClassificationIDs, removedID,
)
}
}