package main import ( "errors" "strings" "testing" ) // Every operation's event is sent once, after a restart too (to-be 55 ยง1, AC51): the message id is the record, the version // and the event; the last version sent is kept; a module stopped between an operation and its event sends it at start, // under the same id, and sends it once. func TestEveryOperationsEventIsSentOnceAfterARestartToo(t *testing.T) { w, f := ledgerWorld(t) type send struct{ event, id string } var sends []send down := false w.k.EmitID = func(event string, body any, id string) error { if down { return errors.New("the bus is down") } sends = append(sends, send{event, id}) return nil } r, err := w.k.ReserveNumber("author@g14", KindDecision, "a test record") if err != nil { t.Fatal(err) } key := Key{KindDecision, r.Number} if len(sends) != 1 || sends[0].event != EventReserved || sends[0].id != key.Kind+"."+strings.Repeat("0", 4-len(itoa(key.Number)))+itoa(key.Number)+".v1.reserved" { t.Fatalf("%+v", sends) } if k := w.k.sentUpTo(key); k != 1 { t.Fatalf("sent up to %d", k) } // Stopped between the operation and its event: the proposal is in the history, its event was not sent. down = true if _, err := w.k.Propose("author@g14", valid(r.Number)); err != nil { t.Fatal(err) } if w.k.sentUpTo(key) != 1 { t.Fatal("an event never sent was marked sent") } // A restart: a new knowledge base over the same ledger and states sends what the stop lost, once. down = false sends = nil again := &KB{Store: w.k.Store, States: w.k.States, EmitID: w.k.EmitID, Now: w.k.Now, Logf: w.k.Logf} n, err := again.ResendEvents(t.Context()) if err != nil || n != 1 || len(sends) != 1 || sends[0].event != EventProposed || !strings.HasSuffix(sends[0].id, ".v2.proposed") { t.Fatalf("%v: %d, %+v", err, n, sends) } if n, _ := again.ResendEvents(t.Context()); n != 0 || len(sends) != 1 { t.Fatalf("a second start sent %d again", n) } _ = f } func itoa(n int) string { return Key{KindDecision, n}.String()[4:] }