S1-S3: the node-engine seat's client asks a node once more when an ask came while one ran, and shows when the event was heard (issue 1966 note 170)
mesh/delivery merged: source.main moved to novox/mesh-contracts@f5d76e84ca71
mesh/delivery-group group fix/1966-engine-reads-asked-again merged: every member is merged
mesh/check checking c9ffed70421a on f5d76e84ca71 | novox/mesh-contracts#4 in group fix/1966-engine-reads-asked-again
mesh/delivery merged: source.main moved to novox/mesh-contracts@f5d76e84ca71
mesh/delivery-group group fix/1966-engine-reads-asked-again merged: every member is merged
mesh/check checking c9ffed70421a on f5d76e84ca71 | novox/mesh-contracts#4 in group fix/1966-engine-reads-asked-again
An event of manifest-reads-changed heard while an ask of the same node ran was dropped without a line, so a restated engine could stay read at its first statement (ace, 2026-11-10 07:36). Every reader goes through Keeper.AskNode, so the assigner, the builder and the indexer are fixed at once (ADR 0668 §4: the caller hears it and calls again; ADR 0634 §1: the seat's client). - AskNode: an ask while one runs is asked once more after it, one follow-up however many came, said in one line (1966/AC170-1, AC170-3). The asks of one node so run one after another, and take keeps the last answer, as before (no rule on the engine's clock: review round 1). - Statement.EventAt (event-at): when the node's event was last heard, read by the parts' engine-reads tools; forgotten with the node's key. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -95,6 +96,41 @@ func TestTheAnswerAndTheEventAreJudged(t *testing.T) {
|
||||
if got := u.Unanswered([]string{"ace"}, heard, 5*time.Minute, clock); len(got) != 0 || !u.Statements()["ace"].UnansweredSince.IsZero() {
|
||||
t.Errorf("an answered node is unanswered: %v", got)
|
||||
}
|
||||
// Issue 1966 note 170, 1966/AC170-1 and AC170-3: an event of a node heard while an ask of it runs is not dropped: the
|
||||
// node is asked once more after that ask ends (one follow-up however many came), said in one line, and the newer
|
||||
// statement is kept; the event's time is shown beside the statement (event-at).
|
||||
const stated26, restated = "2026-11-10T07:36:26Z", "2026-11-10T07:36:30Z"
|
||||
var lines []string
|
||||
eclock := time.Date(2026, 11, 10, 7, 36, 26, 0, time.UTC)
|
||||
asks := 0
|
||||
var e *Keeper
|
||||
e = &Keeper{Sync: true, Now: func() time.Time { return eclock },
|
||||
Logf: func(f string, a ...any) { lines = append(lines, fmt.Sprintf(f, a...)) },
|
||||
Put: func(_ string, raw []byte) error { _, err := Decode(raw); return err },
|
||||
Call: func(node, _ string, _ any) (json.RawMessage, error) {
|
||||
asks++
|
||||
stated := restated
|
||||
if asks == 1 {
|
||||
eclock = eclock.Add(4 * time.Second)
|
||||
e.AskNode(node, Event)
|
||||
e.AskNode(node, Event)
|
||||
stated = stated26
|
||||
} else if got := e.Statements()[node].StatedAt.Format(time.RFC3339); got != stated26 {
|
||||
t.Errorf("the second ask began before the first was taken in: held %s", got)
|
||||
}
|
||||
return json.RawMessage(`{"v":1,"node":"` + node + `","manifest-reads":["a.b"],"stated-at":"` + stated + `"}`), nil
|
||||
}}
|
||||
e.AskNode("ace", Event)
|
||||
held := e.Statements()["ace"]
|
||||
if asks != 2 || held.StatedAt.Format(time.RFC3339) != restated || e.Asking("ace") {
|
||||
t.Errorf("an event during an ask: asked %d times, held stated at %s, asking %v", asks, held.StatedAt, e.Asking("ace"))
|
||||
}
|
||||
if again := strings.Count(strings.Join(lines, "\n"), "asked once more"); again != 1 {
|
||||
t.Errorf("the ask kept for later is said in %d lines, not one: %q", again, lines)
|
||||
}
|
||||
if !held.EventAt.Equal(eclock) || !strings.Contains(string(must(Encode(held))), `"event-at":"2026-11-10T07:36:30Z"`) {
|
||||
t.Errorf("the event's time is not shown: %+v", held)
|
||||
}
|
||||
if Verb != "manifest-reads" || Event != "manifest-reads-changed" || Consumes != "node-engine.manifest-reads-changed" || Invoke != "seat:node-engine.manifest-reads" {
|
||||
t.Errorf("the names are %s %s %s %s", Verb, Event, Consumes, Invoke)
|
||||
}
|
||||
@@ -167,3 +203,10 @@ func TestReadsCallsSayAnUnansweredNodeHeard(t *testing.T) {
|
||||
t.Errorf("the bound's node is %q %v", n, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func must(b []byte, err error) []byte {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user