diff --git a/modules/nats/internal/keeper/keeper.go b/modules/nats/internal/keeper/keeper.go index 14efcae5..cc38e5c1 100644 --- a/modules/nats/internal/keeper/keeper.go +++ b/modules/nats/internal/keeper/keeper.go @@ -349,6 +349,8 @@ func (k *Keeper) makeEntry(held *Asked, e catalogue.BusStateEntry) { wasOrphan := m.Orphaned m.Plan, m.Orphaned = plan, false if wasOrphan { + // Asked again: kept, and its binding said again with created (design 25 § 12.3). + m.Created = false k.clear(conditionOrphaned, module, e.Name) } k.ensure(held, &m) diff --git a/modules/nats/internal/keeper/keeper_test.go b/modules/nats/internal/keeper/keeper_test.go index b098340c..0515c06c 100644 --- a/modules/nats/internal/keeper/keeper_test.go +++ b/modules/nats/internal/keeper/keeper_test.go @@ -367,6 +367,40 @@ func TestTheKeeperMakesWhatAVerdictAsksForAndSaysIt(t *testing.T) { t.Errorf("created %v, said %v: its binding said, nothing made twice", bus.created, out.events) } }}, + // CAT-N5: orphans (design 25 § 12.2-12.3): an empty manifest-accepted asks nothing now, so what was made is an + // orphan, said and raised, kept until a drop; asked again it is kept and its binding said again; a module missing + // from the first view (set aside to wait) keeps its key; one never made leaves nothing behind. + {"an empty verdict orphans what was made, and asking again keeps it", func(t *testing.T, k *Keeper, bus *fakeBus, out *said) { + k.Heard(Accepted{Module: "m", Revision: 1, BusState: []catalogue.BusStateEntry{drafts}}) + out.reset() + k.Heard(Accepted{Module: "m", Revision: 2}) + a, held := k.Held()["m"] + if !held || !a.Made["drafts"].Orphaned || len(bus.deleted) != 0 || fmt.Sprint(out.events) != "[orphaned m drafts]" || + fmt.Sprint(out.conds) != "[raised nats.orphaned.m_drafts]" { + t.Fatalf("held %v %+v, deleted %v, said %v %v: orphaned, kept, raised", held, a, bus.deleted, out.events, out.conds) + } + k.Heard(Accepted{Module: "m", Revision: 3}) + k.Heard(Accepted{Module: "m", Revision: 4, BusState: []catalogue.BusStateEntry{drafts}}) + if fmt.Sprint(out.events) != "[orphaned m drafts created m drafts]" || out.conds[len(out.conds)-1] != "cleared nats.orphaned.m_drafts" { + t.Errorf("said %v %v: orphaned once, then created again and cleared", out.events, out.conds) + } + }}, + {"a module missing from the first view keeps its key; one never made leaves none", func(t *testing.T, k *Keeper, bus *fakeBus, out *said) { + k.Heard(Accepted{Module: "m", Revision: 1, BusState: []catalogue.BusStateEntry{drafts}}) + k.View = func() (FirstView, error) { return FirstView{Revision: 5}, nil } + if _, err := k.AskFirstView(0); err != nil { + t.Fatal(err) + } + if a := k.Held()["m"]; len(a.Entries) != 1 || a.Made["drafts"].Orphaned { + t.Errorf("held %+v: kept as asked, never orphaned by a first view", a) + } + bus.refuse["KV_n_x"] = errors.New("no") + k.Heard(Accepted{Module: "n", Revision: 1, BusState: []catalogue.BusStateEntry{entry("x", catalogue.StorageKeyValue, catalogue.TemplateState, nil)}}) + k.Heard(Accepted{Module: "n", Revision: 2}) + if _, held := k.Held()["n"]; held { + t.Errorf("n's key is still held: nothing of it was made, so its empty verdict ends it") + } + }}, } { t.Run(row.name, func(t *testing.T) { k, bus, _, out := newKeeper(FirstView{Revision: 1})