package main import ( "encoding/json" "errors" "fmt" "reflect" "strings" "sync" "testing" "time" stdio "git.novox.be/novox/mesh-sdk/go" ) func TestTheFirstIssueGetsTheSettingsNumberAndEachNextOneMore(t *testing.T) { h := newHarness(t) for want := uint64(365); want < 368; want++ { if got := h.open(t, "a symptom"); got != want { t.Fatalf("opened %d, want %d", got, want) } } is, err := h.Get(366) if err != nil { t.Fatal(err) } if is.Status != StatusOpen || is.Title != "a symptom" || is.Report != "seen: a symptom" || is.OpenedBy != Unproven || is.Opened.IsZero() || is.Closed != nil { t.Fatalf("kept %+v", is) } } func TestTheCounterNeverStartsBelowAnIssueAlreadyKept(t *testing.T) { h := newHarness(t) h.mem.put(IssuesState, "400", Issue{Number: 400, Title: "kept before", Status: StatusOpen, Opening: "x"}) if got := h.open(t, "after it"); got != 401 { t.Fatalf("opened %d, want 401: the counter started below an issue kept", got) } } func TestTheCounterIsStartedOnlyOnceAndTheSettingIsThenNotRead(t *testing.T) { h := newHarness(t) h.open(t, "first") h.settings.FirstNumber = 10 // a later setting cannot move a number if got := h.open(t, "second"); got != 366 { t.Fatalf("opened %d, want 366", got) } h2 := newHarness(t) h2.settings.FirstNumber = 0 if _, err := h2.Open(OpenAsk{Title: "t", Report: "r"}); err == nil || !strings.Contains(err.Error(), "first-number") { t.Fatalf("an unset first number: %v", err) } if h2.mem.counter(t) != 0 { t.Fatal("a counter was started without its first number") } } func TestARefusedOpenSpendsNoNumber(t *testing.T) { h := newHarness(t) refused := []OpenAsk{ {Title: "", Report: "r"}, {Title: "t", Report: " "}, {Title: "two\nlines", Report: "r"}, {Title: strings.Repeat("x", MostTitle+1), Report: "r"}, {Title: "t", Report: strings.Repeat("x", MostReport+1)}, {Title: "t", Report: "r", LocatedIn: []string{"somewhere-else"}}, {Title: "t", Report: "r", LocatedIn: []string{"mesh-controllerx"}}, {Title: "t", Report: "r", Record: "../escape"}, {Title: "t", Report: "r", Record: "04-ISSUES/365-x"}, {Title: "t", Report: "r", Record: "Upper"}, {Title: "t", Report: "the key is sk-ant-api03-abcdefghijklmnopqrstuvwxyz"}, {Title: "t\x1b[31m", Report: "r"}, {Title: "t‮", Report: "r"}, {Title: "t", Report: "r\x00"}, } for i, a := range refused { if _, err := h.Open(a); err == nil { t.Errorf("case %d (%+v) was opened", i, a) } } if h.mem.counter(t) != 0 || len(h.said()) != 0 { t.Fatalf("a refused open moved the counter to %d or said %v", h.mem.counter(t), h.said()) } if got := h.open(t, "valid"); got != 365 { t.Fatalf("the first valid open got %d, want 365", got) } } func TestAnIssuesKeyIsNeverOverwrittenAndTheFaultIsSaid(t *testing.T) { h := newHarness(t) h.open(t, "first") // 365, the counter started h.mem.put(IssuesState, "366", Issue{Number: 366, Title: "already here", Opening: "other"}) _, err := h.Open(OpenAsk{Title: "second", Report: "r"}) if err == nil || !strings.Contains(err.Error(), "already kept") || !strings.Contains(err.Error(), "366 stays unused") { t.Fatalf("open over a kept key: %v", err) } is, _ := h.Get(366) if is.Title != "already here" { t.Fatal("the kept issue was overwritten") } if got := h.open(t, "third"); got != 367 { t.Fatalf("the next open got %d, want 367", got) } } func TestANumberTakenAndNotWrittenIsSaidAndNeverHandedOutAgain(t *testing.T) { h := newHarness(t) h.open(t, "first") h.mem.fail[IssuesState] = errors.New("the bus did not answer") // One failure is tried again and succeeds: no gap. if got := h.open(t, "second"); got != 366 { t.Fatalf("got %d", got) } // Failing every time leaves a gap, said. failing := &failingStore{memory: h.mem, failCreates: createAttempts} h.Store = failing _, err := h.Open(OpenAsk{Title: "third", Report: "r"}) if err == nil || !strings.Contains(err.Error(), "may have been written") || !strings.Contains(err.Error(), "get 367") { t.Fatalf("a write that never landed: %v", err) } h.Store = h.mem if got := h.open(t, "fourth"); got != 368 { t.Fatalf("got %d, want 368: a number is never handed out twice", got) } } // failingStore fails the next creates of issues, as a bus that takes no write. type failingStore struct { *memory failCreates int } func (f *failingStore) Create(state, key string, value any) (uint64, error) { if state == IssuesState && f.failCreates > 0 { f.failCreates-- return 0, errors.New("the bus did not answer") } return f.memory.Create(state, key, value) } // lostAnswer writes the issue and then says it failed, as a bus whose answer was lost. type lostAnswer struct { *memory once bool } func (l *lostAnswer) Create(state, key string, value any) (uint64, error) { rev, err := l.memory.Create(state, key, value) if state == IssuesState && !l.once && err == nil { l.once = true return 0, errors.New("no answer in 30 s") } return rev, err } func TestAWriteWhoseAnswerWasLostIsKnownAsItsOwn(t *testing.T) { h := newHarness(t) h.Store = &lostAnswer{memory: h.mem} o, err := h.Open(OpenAsk{Title: "t", Report: "r"}) if err != nil || o.Number != 365 { t.Fatalf("%v %v", o, err) } } // lostEvery writes the issue on its first create and then answers every create with a failure. type lostEvery struct{ *memory } func (l lostEvery) Create(state, key string, value any) (uint64, error) { if state == IssuesState { _, _ = l.memory.Create(state, key, value) return 0, errors.New("no answer in 30 s") } return l.memory.Create(state, key, value) } // unreadable answers a taken key with a refusal and cannot read it back. type unreadable struct{ *memory } func (u unreadable) Create(state, key string, value any) (uint64, error) { if state == IssuesState { return 0, fmt.Errorf("%s: %w", key, stdio.ErrChanged) } return u.memory.Create(state, key, value) } func (u unreadable) Get(state, key string) (*Entry, error) { if state == IssuesState { return nil, errors.New("no answer in 30 s") } return u.memory.Get(state, key) } // An open whose write may have landed never says nothing was opened (the review of 2026-10-10). func TestAnOpenWhoseWriteMayHaveLandedNeverSaysNothingWasOpened(t *testing.T) { h := newHarness(t) h.Store = lostEvery{h.mem} if o, err := h.Open(OpenAsk{Title: "t", Report: "r"}); err != nil || o.Number != 365 { t.Fatalf("a write that landed, every answer lost: %v %v", o, err) } h.Store = unreadable{h.mem} _, err := h.Open(OpenAsk{Title: "t", Report: "r"}) if err == nil || strings.Contains(err.Error(), "nothing was opened") || !strings.Contains(err.Error(), "get 366") { t.Fatalf("a taken key that cannot be read back: %v", err) } } func TestOpenSaysAnEventWithWhatChangedAndWhoChangedIt(t *testing.T) { h := newHarness(t) o, err := h.Open(OpenAsk{Title: "a title", Report: "a report", LocatedIn: []string{"mesh-controller internal/broker"}}) if err != nil { t.Fatal(err) } if !o.Announced || o.By != Unproven || o.Status != StatusOpen || !strings.Contains(o.Said, "Opened issue 365") { t.Fatalf("answer %+v", o) } ev := h.said() if len(ev) != 1 || ev[0].Event != EventOpened || ev[0].Body["number"] != float64(365) || ev[0].Body["title"] != "a title" || ev[0].Body["by"] != Unproven || ev[0].Body["status"] != StatusOpen { t.Fatalf("events %+v", ev) } if _, carried := ev[0].Body["report"]; carried { t.Fatal("the opened event carries the whole report; it says what changed, and get has the rest") } } func TestAChangeWhoseEventCannotBeSaidIsKeptAndSaysSo(t *testing.T) { h := newHarness(t) h.emitErr = errors.New("the bus refused it") o, err := h.Open(OpenAsk{Title: "t", Report: "r"}) if err != nil { t.Fatal(err) } if o.Announced || !strings.Contains(o.Warning, "could not be said") { t.Fatalf("a change not announced was reported as announced: %+v", o) } if _, err := h.Get(o.Number); err != nil { t.Fatal("the change was not kept") } } func TestAnIssueWithAFolderIsNumberedTitledAndPointsAtItsFolder(t *testing.T) { h := newHarness(t) o, err := h.Open(OpenAsk{Title: "t", Report: "r", Record: "the-counter-skips-a-number"}) if err != nil { t.Fatal(err) } if o.Record != "04-ISSUES/365-the-counter-skips-a-number" || o.Status != StatusOpen { t.Fatalf("answer %+v", o) } // Since the import of slice 3, an issue with a folder is whole in the tracker: its status moves here. if _, err := h.Move(MoveAsk{Number: 365, Status: StatusDiagnosing, By: "fix-365@g14"}); err != nil { t.Fatalf("a move of an issue with a folder: %v", err) } if _, err := h.Note(365, "notes are taken"); err != nil { t.Fatal(err) } l, _ := h.List(ListAsk{}) if l.Rows[0].Status != StatusDiagnosing { t.Fatalf("listed %+v", l.Rows) } } // The cycle's rules at the write (to-be 53 §5, ADR 0283): one case per row, each refused case changing nothing. // A case starts from open, or, with from, from that status reached first (fixing by fix@g14). func TestTheCyclesRulesHoldAtTheWrite(t *testing.T) { const fx = StatusFixing cases := []struct { name string located []string // at open from string // the status it is moved to first; "" stays open ask MoveAsk refused string }{ {"an unknown status", nil, "", MoveAsk{Status: "closed"}, "not one of"}, {"located without located-in", nil, "", MoveAsk{Status: StatusLocated}, "without located-in"}, {"located with an unregistered owner", nil, "", MoveAsk{Status: StatusLocated, LocatedIn: []string{"elsewhere"}}, "no registered owner"}, {"located with located-in", nil, "", MoveAsk{Status: StatusLocated, LocatedIn: []string{"mesh-catalog modules/records"}}, ""}, {"located, named at open", []string{"hq"}, "", MoveAsk{Status: StatusLocated}, ""}, {"resolved without located-in", nil, fx, MoveAsk{Status: StatusResolved, FixedBy: []string{"hq#1"}}, "without located-in"}, {"resolved without fixed-by", []string{"hq"}, fx, MoveAsk{Status: StatusResolved}, "without fixed-by"}, {"resolved", []string{"hq"}, fx, MoveAsk{Status: StatusResolved, FixedBy: []string{"hq#280"}}, ""}, {"resolved from open", []string{"hq"}, "", MoveAsk{Status: StatusResolved, FixedBy: []string{"hq#280"}}, "only from fixing"}, {"resolved from located", []string{"hq"}, StatusLocated, MoveAsk{Status: StatusResolved, FixedBy: []string{"hq#280"}}, "only from fixing"}, {"resolved from diagnosing", []string{"hq"}, StatusDiagnosing, MoveAsk{Status: StatusResolved, FixedBy: []string{"hq#280"}}, "only from fixing"}, {"a core issue resolved without a replay", []string{"mesh-controller"}, fx, MoveAsk{Status: StatusResolved, FixedBy: []string{"mesh-controller#9"}}, "core issue"}, {"a core issue in the bus resolved without a replay", []string{"mesh-catalog modules/nats"}, fx, MoveAsk{Status: StatusResolved, FixedBy: []string{"mesh-catalog#9"}}, "core issue"}, {"a core issue resolved with a replay", []string{"mesh-tools node-tools/internal/bus"}, fx, MoveAsk{Status: StatusResolved, FixedBy: []string{"mesh-tools#25"}, Replay: "R361"}, ""}, {"a core issue resolved with replay-none", []string{"mesh-sdk"}, fx, MoveAsk{Status: StatusResolved, FixedBy: []string{"mesh-sdk#16"}, ReplayNone: "it needs two machines"}, ""}, {"a replay that names none", []string{"mesh-sdk"}, fx, MoveAsk{Status: StatusResolved, FixedBy: []string{"mesh-sdk#16"}, Replay: "yes"}, "names no replay"}, {"replay and replay-none", []string{"mesh-sdk"}, fx, MoveAsk{Status: StatusResolved, FixedBy: []string{"x"}, Replay: "R1", ReplayNone: "y"}, "not both"}, {"a replay on another status", []string{"mesh-sdk"}, "", MoveAsk{Status: StatusLocated, Replay: "R1"}, "what resolving"}, {"wontfix without a reason", nil, "", MoveAsk{Status: StatusWontfix}, "without a reason"}, {"wontfix", nil, "", MoveAsk{Status: StatusWontfix, Reason: "the module is retired"}, ""}, {"wontfix from fixing", nil, fx, MoveAsk{Status: StatusWontfix, Reason: "the module is retired"}, ""}, {"a reason on another status", nil, "", MoveAsk{Status: StatusDiagnosing, By: "a@g14", Reason: "x"}, "what wontfix needs"}, {"to the status it has", nil, "", MoveAsk{Status: StatusOpen}, "already open"}, {"diagnosing", nil, "", MoveAsk{Status: StatusDiagnosing, By: "fix-365@g14"}, ""}, {"diagnosing without by", nil, "", MoveAsk{Status: StatusDiagnosing}, "needs by"}, {"fixing", nil, "", MoveAsk{Status: StatusFixing, By: "Jochen@shanks"}, ""}, {"fixing without by", nil, "", MoveAsk{Status: StatusFixing}, "needs by"}, {"fixing with a by that names no node", nil, "", MoveAsk{Status: StatusFixing, By: "an agent"}, "who@node"}, {"fixing with a by that carries a secret", nil, "", MoveAsk{Status: StatusFixing, By: "sk-ant-api03-abcdefghijklmnopqrstuvwxyz@g14"}, "carries no secret"}, {"by on another status", []string{"hq"}, "", MoveAsk{Status: StatusLocated, By: "a@g14"}, "only diagnosing and fixing"}, {"take-over on another status", []string{"hq"}, "", MoveAsk{Status: StatusLocated, TakeOver: "x"}, "take-over is for"}, {"take-over with nobody to take from", nil, "", MoveAsk{Status: StatusFixing, By: "a@g14", TakeOver: "x"}, "nobody works on"}, {"an amended design that is not one", nil, "", MoveAsk{Status: StatusDiagnosing, By: "a@g14", AmendedDesign: []string{"../etc/passwd"}}, "not a design document"}, {"a take-over reason that carries a secret", nil, StatusDiagnosing, MoveAsk{Status: StatusFixing, By: "b@ace", TakeOver: "the key is sk-ant-api03-abcdefghijklmnopqrstuvwxyz"}, "carries no secret"}, {"a fixed-by of two lines", []string{"hq"}, fx, MoveAsk{Status: StatusResolved, FixedBy: []string{"a\nb"}}, "one line"}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { h := newHarness(t) n := h.open(t, "an issue", c.located...) from := StatusOpen switch c.from { case "": case StatusFixing: h.fixing(t, n) from = StatusFixing case StatusDiagnosing: if _, err := h.Move(MoveAsk{Number: n, Status: c.from, By: "fix@g14"}); err != nil { t.Fatal(err) } from = c.from default: if _, err := h.Move(MoveAsk{Number: n, Status: c.from}); err != nil { t.Fatal(err) } from = c.from } before, _ := h.mem.Get(IssuesState, keyOf(n)) events := len(h.said()) c.ask.Number = n m, err := h.Move(c.ask) if c.refused != "" { if err == nil || !strings.Contains(err.Error(), c.refused) { t.Fatalf("want refused with %q, got %v", c.refused, err) } after, _ := h.mem.Get(IssuesState, keyOf(n)) if after.Revision != before.Revision || string(after.Value) != string(before.Value) { t.Fatal("a refused move changed the issue") } if len(h.said()) != events { t.Fatalf("a refused move said %v", h.said()) } return } if err != nil { t.Fatalf("refused: %v", err) } is, _ := h.Get(n) if is.Status != c.ask.Status || m.From != from || m.To != c.ask.Status { t.Fatalf("moved %+v, kept %+v", m, is) } closed := c.ask.Status == StatusResolved || c.ask.Status == StatusWontfix if (is.Closed != nil) != closed { t.Fatalf("closed %v at %s", is.Closed, is.Status) } wantBy := Unproven if worked(c.ask.Status) { wantBy = strings.ToLower(c.ask.By) if is.Worker == nil || is.Worker.By != wantBy || is.Worker.Since.IsZero() { t.Fatalf("worker %+v", is.Worker) } } else if is.Worker != nil { t.Fatalf("a worker kept at %s: %+v", is.Status, is.Worker) } ev := h.said() if last := ev[len(ev)-1]; last.Event != EventMoved || last.Body["to"] != c.ask.Status || last.Body["from"] != from || last.Body["by"] != wantBy || last.Body["caller"] != Unproven { t.Fatalf("event %+v", last) } }) } } func TestReopeningClearsWhenItClosed(t *testing.T) { h := newHarness(t) n := h.open(t, "x", "hq") h.fixing(t, n) if _, err := h.Move(MoveAsk{Number: n, Status: StatusResolved, FixedBy: []string{"hq#1"}}); err != nil { t.Fatal(err) } if _, err := h.Move(MoveAsk{Number: n, Status: StatusOpen}); err != nil { t.Fatal(err) } is, _ := h.Get(n) if is.Closed != nil || is.Status != StatusOpen { t.Fatalf("%+v", is) } } func TestAMoveOfAnIssueThatDoesNotExistIsRefused(t *testing.T) { h := newHarness(t) if _, err := h.Move(MoveAsk{Number: 9, Status: StatusDiagnosing, By: "a@g14"}); !errors.Is(err, ErrNoIssue) { t.Fatalf("%v", err) } if _, err := h.Get(9); err == nil || !strings.Contains(err.Error(), "there is no issue 9") { t.Fatalf("%v", err) } } func TestNotesAreAppendedNeverEditedAndEachIsSaid(t *testing.T) { h := newHarness(t) n := h.open(t, "x") for _, text := range []string{"first finding", "second, over\ntwo lines"} { if _, err := h.Note(n, text); err != nil { t.Fatal(err) } } is, _ := h.Get(n) if len(is.Notes) != 2 || is.Notes[0].Text != "first finding" || is.Notes[1].Text != "second, over\ntwo lines" || is.Notes[0].By != Unproven { t.Fatalf("%+v", is.Notes) } ev := h.said() if ev[2].Event != EventNoted || ev[2].Body["text"] != "second, over\ntwo lines" || ev[2].Body["number"] != float64(n) { t.Fatalf("%+v", ev) } for _, bad := range []string{"", "\x07", "password=hunter2hunter2", strings.Repeat("x", MostNote+1)} { if _, err := h.Note(n, bad); err == nil { t.Errorf("note %q taken", bad) } } if _, err := h.Note(404, "x"); !errors.Is(err, ErrNoIssue) { t.Fatalf("a note on no issue: %v", err) } } func TestANoteThatWouldMakeTheIssueTooLargeIsRefusedAndNothingChanges(t *testing.T) { h := newHarness(t) n := h.open(t, "x") note := strings.Repeat("word ", MostNote/5-1) var err error for i := 0; i < 40 && err == nil; i++ { _, err = h.Note(n, note) } if err == nil || !strings.Contains(err.Error(), "256 KiB") { t.Fatalf("no refusal: %v", err) } e, _ := h.mem.Get(IssuesState, keyOf(n)) if len(e.Value) > MostIssueBytes { t.Fatalf("kept %d bytes", len(e.Value)) } } func TestConcurrentNotesAreAllKept(t *testing.T) { h := newHarness(t) h.Sleep = time.Sleep n := h.open(t, "x") var wg sync.WaitGroup for i := 0; i < 20; i++ { wg.Add(1) go func() { defer wg.Done() if _, err := h.Note(n, "a note"); err != nil { t.Error(err) } }() } wg.Wait() is, _ := h.Get(n) if len(is.Notes) != 20 { t.Fatalf("kept %d of 20 notes: a write over a stale read lost some", len(is.Notes)) } } func TestLinksAreTypedAddedOnceAndRemoved(t *testing.T) { h := newHarness(t) a, b := h.open(t, "a"), h.open(t, "b") good := []LinkAsk{ {Kind: LinkDuplicates, Target: keyOf(b)}, {Kind: LinkRelatesTo, Target: keyOf(b)}, {Kind: LinkPullRequest, Target: "mesh-controller#246"}, {Kind: LinkDecision, Target: "0275"}, {Kind: LinkDesign, Target: "03-DESIGN/01-to-be/53-the-issue-tracker.md"}, {Kind: LinkCondition, Target: "healer-wanted.ace.backup"}, {Kind: LinkDelivery, Target: "d-2026-10-10.3"}, } for _, l := range good { l.Number = a if _, err := h.Link(l); err != nil { t.Fatalf("%+v: %v", l, err) } } bad := []struct { l LinkAsk says string }{ {LinkAsk{Kind: "blocks", Target: "1"}, "not a kind"}, {LinkAsk{Kind: LinkDuplicates, Target: keyOf(a)}, "itself"}, {LinkAsk{Kind: LinkDuplicates, Target: "999"}, "there is no issue 999"}, {LinkAsk{Kind: LinkPullRequest, Target: "mesh-controller#246"}, "already has"}, {LinkAsk{Kind: LinkPullRequest, Target: "../x#1"}, "target is"}, {LinkAsk{Kind: LinkDecision, Target: "275"}, "four-digit"}, {LinkAsk{Kind: LinkDesign, Target: "03-DESIGN/../../etc/passwd"}, "design document"}, {LinkAsk{Kind: LinkCondition, Target: "a key\nwith a line"}, "condition"}, {LinkAsk{Kind: LinkCondition, Target: "token=abcdefgh123"}, "secret"}, {LinkAsk{Kind: LinkRelatesTo, Target: keyOf(b), Remove: true}, ""}, {LinkAsk{Kind: LinkRelatesTo, Target: keyOf(b), Remove: true}, "no relates-to link"}, } for _, c := range bad { c.l.Number = a _, err := h.Link(c.l) if c.says == "" { if err != nil { t.Fatalf("%+v: %v", c.l, err) } continue } if err == nil || !strings.Contains(err.Error(), c.says) { t.Fatalf("%+v: want %q, got %v", c.l, c.says, err) } } is, _ := h.Get(a) if len(is.Links) != len(good)-1 { t.Fatalf("kept %d links", len(is.Links)) } ev := h.said() last := ev[len(ev)-1] if last.Event != EventLinked || last.Body["removed"] != true || last.Body["kind"] != LinkRelatesTo || last.Body["by"] != Unproven { t.Fatalf("%+v", last) } } func TestListIsNewestFirstAndNarrows(t *testing.T) { h := newHarness(t) h.open(t, "the bus drops a message", "mesh-catalog modules/nats") h.open(t, "a check passes and did nothing", "mesh-controller internal/catalogue") n := h.open(t, "the console names a ghost") if _, err := h.Move(MoveAsk{Number: n, Status: StatusDiagnosing, By: "a@g14"}); err != nil { t.Fatal(err) } l, err := h.List(ListAsk{}) if err != nil { t.Fatal(err) } var got []uint64 for _, r := range l.Rows { got = append(got, r.Number) } if !reflect.DeepEqual(got, []uint64{367, 366, 365}) || l.Rows[1].Owner != "mesh-controller internal/catalogue" || l.Rows[0].Status != StatusDiagnosing { t.Fatalf("%+v", l) } for _, c := range []struct { ask ListAsk want []uint64 }{ {ListAsk{Status: StatusOpen}, []uint64{366, 365}}, {ListAsk{Owner: "mesh-controller"}, []uint64{366}}, {ListAsk{Owner: "mesh-catalog"}, []uint64{365}}, {ListAsk{Owner: "mesh-catalog modules/nats"}, []uint64{365}}, {ListAsk{Phrase: "GHOST"}, []uint64{367}}, {ListAsk{Phrase: "seen: a check"}, []uint64{366}}, {ListAsk{Limit: 1}, []uint64{367}}, {ListAsk{Status: StatusResolved}, nil}, } { l, err := h.List(c.ask) if err != nil { t.Fatal(err) } var got []uint64 for _, r := range l.Rows { got = append(got, r.Number) } if !reflect.DeepEqual(got, c.want) { t.Errorf("%+v: %v, want %v (%s)", c.ask, got, c.want, l.Said) } } if _, err := h.List(ListAsk{Status: "closed"}); err == nil { t.Fatal("an unknown status was listed") } } // The caller is the bus credential, never an argument (ADR 0275 §5): no verb takes one, so none can be // named, and what is recorded is never what an argument said. func TestNoArgumentNamesWhoActed(t *testing.T) { h := newHarness(t) for _, verb := range Verbs { for _, field := range []string{"by", "actor", "caller", "opened-by", "user"} { _, err := h.call(t, verb, map[string]any{field: "operator", "number": 1, "title": "t", "report": "r", "status": "open", "text": "x", "kind": LinkDecision, "target": "0001"}) if err == nil || !strings.Contains(err.Error(), "does not take "+field) && !strings.Contains(err.Error(), field) { t.Errorf("%s with %s: %v", verb, field, err) } } } out, err := h.call(t, "open", map[string]any{"title": "t", "report": "r"}) if err != nil { t.Fatal(err) } if out.(*Opened).By != Unproven { t.Fatal("open recorded a caller nothing proved") } is, _ := h.Get(out.(*Opened).Number) raw, _ := json.Marshal(is) if strings.Contains(string(raw), "operator") { t.Fatal("an argument's name reached the record") } } func TestTheVerbsReadTheirArgumentsStrictly(t *testing.T) { h := newHarness(t) n := h.open(t, "x") refusals := []struct { verb string args map[string]any }{ {"open", map[string]any{"title": 5, "report": "r"}}, {"open", map[string]any{"title": "t", "report": "r", "located-in": []any{1}}}, {"get", map[string]any{}}, {"get", map[string]any{"number": -1}}, {"get", map[string]any{"number": 1.5}}, {"get", map[string]any{"number": "../365"}}, {"get", map[string]any{"number": "0365"}}, {"move", map[string]any{"number": n, "status": true}}, {"note", map[string]any{"number": n}}, {"link", map[string]any{"number": n, "kind": LinkDecision, "target": "0275", "remove": "yes"}}, {"list", map[string]any{"limit": "all"}}, } for _, r := range refusals { if _, err := h.call(t, r.verb, r.args); err == nil { t.Errorf("%s %v was taken", r.verb, r.args) } } if out, err := h.call(t, "get", map[string]any{"number": keyOf(n)}); err != nil || out.(*View).Number != n { t.Fatalf("a number as text: %v", err) } if out, err := h.call(t, "open", map[string]any{"title": "t", "report": "r", "located-in": "hq"}); err != nil || !reflect.DeepEqual(out.(*Opened).LocatedIn, []string{"hq"}) { t.Fatalf("one owner as text: %v", err) } } func TestAnOwnersEntryIsTheRegisteredOneOrStartsWithIt(t *testing.T) { o := Owners{"mesh-catalog", "mesh-catalog modules/nats", "mesh-controller"} for entry, want := range map[string]string{ "mesh-catalog": "mesh-catalog", "mesh-catalog modules/nats": "mesh-catalog modules/nats", "mesh-catalog modules/nats/module.go": "mesh-catalog modules/nats", "mesh-catalog modules/does-not-exist": "", "mesh-catalog checks/words": "mesh-catalog", "mesh-catalog modules/nats (the gate)": "mesh-catalog modules/nats", "mesh-controller cmd/mesh-controller": "mesh-controller", "mesh-controllers": "", "hal": "", } { if got := o.owns(entry); got != want { t.Errorf("%q is owned by %q, want %q", entry, got, want) } } }