mesh/merge-gate pass: builds mesh-issues, mesh-knowledge, records → novox; no bus step; every machine composes with the change as it did without (4 of 4 co…
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery delivered
404 lines
14 KiB
Go
404 lines
14 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
)
|
|
|
|
// The tracker hands out decision and research numbers too (novox/hq ADR 0275 rule 8, ADR 0290 rule 5, issue
|
|
// 424): one counter per sequence, advanced like the issue counter, started above every number the knowledge base
|
|
// holds (hq issue 488) and the operator's floor, and a reserved number is spent forever.
|
|
|
|
// held is the knowledge base as reserve asks it (knowledge_highest, hq issue 488): the highest number it holds
|
|
// of each kind, or an error.
|
|
type held struct {
|
|
mu sync.Mutex
|
|
highest map[string]uint64
|
|
err error
|
|
asks int
|
|
}
|
|
|
|
func (k *held) Highest() (map[string]uint64, error) {
|
|
k.mu.Lock()
|
|
defer k.mu.Unlock()
|
|
k.asks++
|
|
if k.err != nil {
|
|
return nil, k.err
|
|
}
|
|
out := map[string]uint64{}
|
|
for kind, n := range k.highest {
|
|
out[kind] = n
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (k *held) set(kind string, n uint64) {
|
|
k.mu.Lock()
|
|
defer k.mu.Unlock()
|
|
k.highest[kind] = n
|
|
}
|
|
|
|
// unread is hq's folders, which reserve never reads for a number: the records leave hq (hq issue 426).
|
|
type unread struct{ lists int }
|
|
|
|
func (u *unread) Standing() (string, error) { return "1ea678c", nil }
|
|
func (u *unread) Read(string) (string, string, error) { return "", "", errors.New("not read") }
|
|
func (u *unread) List(string) ([]string, []string, error) {
|
|
u.lists++
|
|
return nil, nil, errors.New("reserve read hq's folders")
|
|
}
|
|
|
|
// heldToday is the knowledge base as it stands: decisions to 0291, research to 045.
|
|
func heldToday() *held {
|
|
return &held{highest: map[string]uint64{KindDecision: 291, KindResearch: 45}}
|
|
}
|
|
|
|
func reserveHarness(t *testing.T) (*harness, *held) {
|
|
t.Helper()
|
|
h := newHarness(t)
|
|
k := heldToday()
|
|
hq := &unread{}
|
|
h.Records = hq
|
|
h.Highest = k.Highest
|
|
t.Cleanup(func() {
|
|
if hq.lists != 0 {
|
|
t.Errorf("reserve read hq's folders %d time(s)", hq.lists)
|
|
}
|
|
})
|
|
return h, k
|
|
}
|
|
|
|
func (h *harness) reserve(t *testing.T, kind string) uint64 {
|
|
t.Helper()
|
|
out, err := h.Reserve(ReserveAsk{Kind: kind})
|
|
if err != nil {
|
|
t.Fatalf("reserve %s: %v", kind, err)
|
|
}
|
|
return out.Number
|
|
}
|
|
|
|
func (m *memory) counterOf(t *testing.T, key string) uint64 {
|
|
t.Helper()
|
|
e, _ := m.Get(CountersState, key)
|
|
if e == nil {
|
|
return 0
|
|
}
|
|
var n uint64
|
|
if err := json.Unmarshal(e.Value, &n); err != nil {
|
|
t.Fatalf("the counter %s holds %s", key, e.Value)
|
|
}
|
|
return n
|
|
}
|
|
|
|
func TestTheFirstReservedNumberIsAboveTheHighestTheKnowledgeBaseHolds(t *testing.T) {
|
|
h, _ := reserveHarness(t)
|
|
if got := h.reserve(t, KindDecision); got != 292 {
|
|
t.Fatalf("the first decision number is %d, want 292: one above 0291", got)
|
|
}
|
|
if got := h.reserve(t, KindResearch); got != 46 {
|
|
t.Fatalf("the first research number is %d, want 46: one above 045", got)
|
|
}
|
|
if got := h.reserve(t, KindDecision); got != 293 {
|
|
t.Fatalf("the second decision number is %d, want 293", got)
|
|
}
|
|
if h.mem.counter(t) != 0 {
|
|
t.Fatal("a reservation moved the issue counter")
|
|
}
|
|
}
|
|
|
|
// An open branch the tracker cannot read holds a higher number: the operator's floor says it.
|
|
func TestTheCounterStartsAboveTheFloorWhenItIsHigher(t *testing.T) {
|
|
h, _ := reserveHarness(t)
|
|
h.settings.DecisionFloor = 293
|
|
h.settings.ResearchFloor = 40 // below what the record holds: the record wins
|
|
if got := h.reserve(t, KindDecision); got != 294 {
|
|
t.Fatalf("got %d, want 294: one above the floor 293", got)
|
|
}
|
|
if got := h.reserve(t, KindResearch); got != 46 {
|
|
t.Fatalf("got %d, want 46: the floor 40 is below 045", got)
|
|
}
|
|
}
|
|
|
|
func TestTheCounterNeverStartsBelowAReservationAlreadyKept(t *testing.T) {
|
|
h, _ := reserveHarness(t)
|
|
h.mem.put(ReservationsState, ReservationKey(KindDecision, 310), Reservation{Kind: KindDecision, Number: 310})
|
|
if got := h.reserve(t, KindDecision); got != 311 {
|
|
t.Fatalf("got %d, want 311: the counter started below a reservation kept", got)
|
|
}
|
|
}
|
|
|
|
// A number the knowledge base holds above the counter — imported, or taken by hand while sessions still did —
|
|
// raises it: no number at or below one held is handed out.
|
|
func TestARecordAboveTheCounterRaisesIt(t *testing.T) {
|
|
h, k := reserveHarness(t)
|
|
h.reserve(t, KindDecision) // 292, the counter started
|
|
k.set(KindDecision, 300)
|
|
if got := h.reserve(t, KindDecision); got != 301 {
|
|
t.Fatalf("got %d, want 301: one above 0300", got)
|
|
}
|
|
}
|
|
|
|
func TestTheCounterStartsOnlyOnceAndTheFloorIsThenNotRead(t *testing.T) {
|
|
h, _ := reserveHarness(t)
|
|
h.reserve(t, KindDecision) // 292
|
|
h.settings.DecisionFloor = 1
|
|
if got := h.reserve(t, KindDecision); got != 293 {
|
|
t.Fatalf("got %d, want 293", got)
|
|
}
|
|
}
|
|
|
|
func TestManyConcurrentReservationsGetDistinctConsecutiveNumbers(t *testing.T) {
|
|
h, _ := reserveHarness(t)
|
|
const each = 40
|
|
var mu sync.Mutex
|
|
got := map[string][]uint64{}
|
|
var wg sync.WaitGroup
|
|
for i := 0; i < 2*each; i++ {
|
|
kind := KindDecision
|
|
if i%2 == 1 {
|
|
kind = KindResearch
|
|
}
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
out, err := h.Reserve(ReserveAsk{Kind: kind, For: "concurrent"})
|
|
if err != nil {
|
|
t.Errorf("reserve %s: %v", kind, err)
|
|
return
|
|
}
|
|
mu.Lock()
|
|
got[kind] = append(got[kind], out.Number)
|
|
mu.Unlock()
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
for kind, first := range map[string]uint64{KindDecision: 292, KindResearch: 46} {
|
|
ns := got[kind]
|
|
sort.Slice(ns, func(i, j int) bool { return ns[i] < ns[j] })
|
|
if len(ns) != each {
|
|
t.Fatalf("%s: %d numbers, want %d", kind, len(ns), each)
|
|
}
|
|
for i, n := range ns {
|
|
if n != first+uint64(i) {
|
|
t.Fatalf("%s: numbers %v are not %d.. distinct and consecutive", kind, ns, first)
|
|
}
|
|
if _, err := h.mem.Get(ReservationsState, ReservationKey(kind, n)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if c := h.mem.counterOf(t, kind); c != first+each-1 {
|
|
t.Fatalf("%s counter at %d, want %d", kind, c, first+each-1)
|
|
}
|
|
}
|
|
if keys, _ := h.mem.Keys(ReservationsState); len(keys) != 2*each {
|
|
t.Fatalf("%d reservations kept, want %d", len(keys), 2*each)
|
|
}
|
|
}
|
|
|
|
func TestARefusedReservationSpendsNothing(t *testing.T) {
|
|
h, k := reserveHarness(t)
|
|
refused := []ReserveAsk{
|
|
{Kind: ""},
|
|
{Kind: "issue"},
|
|
{Kind: "Decision"},
|
|
{Kind: "design"},
|
|
{Kind: KindDecision, For: "two\nlines"},
|
|
{Kind: KindDecision, For: strings.Repeat("x", MostTitle+1)},
|
|
{Kind: KindResearch, For: "the key is sk-ant-api03-abcdefghijklmnopqrstuvwxyz"},
|
|
{Kind: KindResearch, For: "t\x1b[31m"},
|
|
}
|
|
for i, a := range refused {
|
|
if out, err := h.Reserve(a); err == nil {
|
|
t.Errorf("case %d (%+v) was reserved: %+v", i, a, out)
|
|
}
|
|
}
|
|
keys, _ := h.mem.Keys(CountersState)
|
|
kept, _ := h.mem.Keys(ReservationsState)
|
|
if len(keys) != 0 || len(kept) != 0 || len(h.said()) != 0 || k.asks != 0 {
|
|
t.Fatalf("a refused reservation left counters %v, reservations %v, events %v, knowledge base asks %d", keys, kept, h.said(), k.asks)
|
|
}
|
|
if got := h.reserve(t, KindDecision); got != 292 {
|
|
t.Fatalf("the first valid reservation got %d, want 292", got)
|
|
}
|
|
}
|
|
|
|
// Before its counter starts, a sequence must know what the knowledge base holds, or have the operator's floor:
|
|
// with neither, nothing is handed out and nothing is spent. Once started, the counter alone is enough, and says it
|
|
// was not checked.
|
|
func TestAKnowledgeBaseThatDoesNotAnswerRefusesTheFirstNumberAndNotTheNext(t *testing.T) {
|
|
h, k := reserveHarness(t)
|
|
k.err = errors.New("nothing answers mesh-knowledge.knowledge_highest")
|
|
if _, err := h.Reserve(ReserveAsk{Kind: KindDecision}); err == nil || !strings.Contains(err.Error(), "knowledge_highest") {
|
|
t.Fatalf("a first reservation with the knowledge base silent: %v", err)
|
|
}
|
|
if h.mem.counterOf(t, KindDecision) != 0 || len(h.said()) != 0 {
|
|
t.Fatal("a refused first reservation started the counter or said something")
|
|
}
|
|
if keys, _ := h.mem.Keys(ReservationsState); len(keys) != 0 {
|
|
t.Fatal("a refused first reservation spent a number")
|
|
}
|
|
k.err = nil
|
|
h.reserve(t, KindDecision) // 292
|
|
k.err = errors.New("nothing answers mesh-knowledge.knowledge_highest")
|
|
out, err := h.Reserve(ReserveAsk{Kind: KindDecision})
|
|
if err != nil || out.Number != 293 {
|
|
t.Fatalf("got %+v (%v), want 293 from the started counter", out, err)
|
|
}
|
|
// Failure is loud: the answer says the number was not checked against the knowledge base.
|
|
if !strings.Contains(out.Warning, "knowledge_highest") || !strings.Contains(out.Warning, "unchecked") {
|
|
t.Fatalf("a number unchecked against the knowledge base answered warning %q", out.Warning)
|
|
}
|
|
k.err = nil
|
|
if out, err := h.Reserve(ReserveAsk{Kind: KindDecision}); err != nil || out.Warning != "" {
|
|
t.Fatalf("a reserve with the knowledge base read answered %+v (%v): no warning expected", out, err)
|
|
}
|
|
}
|
|
|
|
// A counter not started never starts without the knowledge base's answer, floor or no floor: a floor alone
|
|
// cannot say a number above it is free. Nothing is spent. With the answer, the floor still raises the start.
|
|
func TestAFloorDoesNotStartACounterTheKnowledgeBaseDoesNotAnswerFor(t *testing.T) {
|
|
h, k := reserveHarness(t)
|
|
k.err = errors.New("the knowledge base holds no record yet")
|
|
h.settings.ResearchFloor = 47
|
|
if out, err := h.Reserve(ReserveAsk{Kind: KindResearch}); err == nil || !strings.Contains(err.Error(), "the knowledge base holds no record yet") {
|
|
t.Fatalf("a research number from the floor alone: %+v %v", out, err)
|
|
}
|
|
if keys, _ := h.mem.Keys(ReservationsState); len(keys) != 0 || h.mem.counterOf(t, KindResearch) != 0 {
|
|
t.Fatal("a refused first reservation spent a number or started the counter")
|
|
}
|
|
k.err = nil
|
|
if got := h.reserve(t, KindResearch); got != 48 {
|
|
t.Fatalf("got %d, want 48: one above the floor 47, above the 045 held", got)
|
|
}
|
|
}
|
|
|
|
// A knowledge base that holds no record of a kind answers nothing for it: a 0 would start the counter at 1.
|
|
func TestAKindTheKnowledgeBaseHoldsNoneOfIsNoAnswer(t *testing.T) {
|
|
h, k := reserveHarness(t)
|
|
k.set(KindResearch, 0)
|
|
if _, err := h.Reserve(ReserveAsk{Kind: KindResearch}); err == nil || !strings.Contains(err.Error(), "holds no research") {
|
|
t.Fatalf("a research number from a knowledge base that holds none: %v", err)
|
|
}
|
|
if got := h.reserve(t, KindDecision); got != 292 {
|
|
t.Fatalf("got %d", got)
|
|
}
|
|
}
|
|
|
|
func TestAReservationIsKeptWithWhoWhenAndWhatFor(t *testing.T) {
|
|
h, _ := reserveHarness(t)
|
|
out, err := h.Reserve(ReserveAsk{Kind: KindDecision, For: " the knowledge base holds decisions "})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if out.Kind != KindDecision || out.Number != 292 || out.Prefix != "0292" || out.By != Unproven ||
|
|
out.For != "the knowledge base holds decisions" || !out.Announced {
|
|
t.Fatalf("answered %+v", out)
|
|
}
|
|
e, err := h.mem.Get(ReservationsState, "decision-0292")
|
|
if err != nil || e == nil {
|
|
t.Fatalf("the reservation is not kept: %v", err)
|
|
}
|
|
var kept Reservation
|
|
if err := json.Unmarshal(e.Value, &kept); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if kept.Kind != KindDecision || kept.Number != 292 || kept.By != Unproven || kept.At.IsZero() ||
|
|
kept.For != "the knowledge base holds decisions" {
|
|
t.Fatalf("kept %+v", kept)
|
|
}
|
|
ev := h.said()
|
|
if len(ev) != 1 || ev[0].Event != EventReserved || ev[0].Body["kind"] != KindDecision ||
|
|
ev[0].Body["number"] != float64(292) || ev[0].Body["by"] != Unproven || ev[0].Body["for"] != kept.For {
|
|
t.Fatalf("said %+v", ev)
|
|
}
|
|
r, err := h.Reserve(ReserveAsk{Kind: KindResearch})
|
|
if err != nil || r.Prefix != "046" {
|
|
t.Fatalf("a research number's prefix: %+v, %v", r, err)
|
|
}
|
|
}
|
|
|
|
func TestAReservationWhoseEventCannotBeSaidIsKeptAndSaysSo(t *testing.T) {
|
|
h, _ := reserveHarness(t)
|
|
h.emitErr = errors.New("the bus is away")
|
|
out, err := h.Reserve(ReserveAsk{Kind: KindResearch})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if out.Announced || out.Warning == "" || out.Number != 46 {
|
|
t.Fatalf("answered %+v", out)
|
|
}
|
|
}
|
|
|
|
// A reservation whose write cannot be confirmed is refused, and the number stays spent.
|
|
func TestANumberWhoseReservationIsNotKeptIsSaidAndNeverHandedOutAgain(t *testing.T) {
|
|
h, _ := reserveHarness(t)
|
|
h.mem.fail[ReservationsState] = errors.New("the bus did not answer")
|
|
if got := h.reserve(t, KindDecision); got != 292 {
|
|
t.Fatalf("one failure is tried again: got %d", got)
|
|
}
|
|
failing := &failingReservations{memory: h.mem, fail: 1000}
|
|
h.Store = failing
|
|
_, err := h.Reserve(ReserveAsk{Kind: KindDecision})
|
|
if err == nil || !strings.Contains(err.Error(), "293") || !strings.Contains(err.Error(), "spent") {
|
|
t.Fatalf("a reservation never kept: %v", err)
|
|
}
|
|
h.Store = h.mem
|
|
if got := h.reserve(t, KindDecision); got != 294 {
|
|
t.Fatalf("got %d, want 294: a number is never handed out twice", got)
|
|
}
|
|
}
|
|
|
|
type failingReservations struct {
|
|
*memory
|
|
fail int
|
|
}
|
|
|
|
func (f *failingReservations) Create(state, key string, value any) (uint64, error) {
|
|
if state == ReservationsState && f.fail > 0 {
|
|
f.fail--
|
|
return 0, errors.New("the bus did not answer")
|
|
}
|
|
return f.memory.Create(state, key, value)
|
|
}
|
|
|
|
func TestTheReserveVerbReadsItsArgumentsStrictlyAndAnswersTheContract(t *testing.T) {
|
|
h, _ := reserveHarness(t)
|
|
for _, args := range []map[string]any{
|
|
{},
|
|
{"kind": 5},
|
|
{"kind": KindDecision, "for": 7},
|
|
{"kind": KindDecision, "by": "operator"},
|
|
{"kind": KindDecision, "number": 400},
|
|
} {
|
|
if out, err := h.call(t, "reserve", args); err == nil {
|
|
t.Errorf("reserve %v was taken: %+v", args, out)
|
|
}
|
|
}
|
|
out, err := h.call(t, "reserve", map[string]any{"kind": "research", "for": "a spike"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
raw, _ := json.Marshal(out)
|
|
var answer map[string]any
|
|
_ = json.Unmarshal(raw, &answer)
|
|
if answer["kind"] != "research" || answer["number"] != float64(46) || strings.Contains(string(raw), "operator") {
|
|
t.Fatalf("answered %s", raw)
|
|
}
|
|
}
|
|
|
|
func TestAReservationKeyNamesTheKindAndTheRecordsPrefix(t *testing.T) {
|
|
for _, c := range []struct {
|
|
kind string
|
|
n uint64
|
|
want string
|
|
}{{KindDecision, 292, "decision-0292"}, {KindResearch, 46, "research-046"}, {KindDecision, 10000, "decision-10000"}} {
|
|
if got := ReservationKey(c.kind, c.n); got != c.want {
|
|
t.Errorf("%s %d: %s, want %s", c.kind, c.n, got, c.want)
|
|
}
|
|
}
|
|
}
|