mesh/merge-gate fail: builds mesh-issues → novox; no bus step; a manifest the change touches fails the module check: modules/mesh-issues/module.json: this …
mesh/repo-check fail: its merge-check.sh failed: files asking for a setting without saying whether it is trusted: 25
mesh/delivery superseded: a newer head of the same pull request
Every write appends one operation at the issue's last sequence before the bucket is written; a refused append writes nothing. The start carries what the bucket holds, refolds what is behind and indexes every issue in memory, so list answers from memory with a ranked search and a before cursor.
381 lines
14 KiB
Go
381 lines
14 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
)
|
|
|
|
// The import of slice 3 (to-be 53 §8) over the real reports in testdata/hq, against the tracker kept in
|
|
// this process: it creates each issue at its own number, touches no counter, never overwrites, and run
|
|
// again changes nothing and says so.
|
|
|
|
// sampleRecords is the record played from testdata/hq, at a commit the test names.
|
|
type sampleRecords struct {
|
|
dirRecords
|
|
commit string
|
|
// moveTo, when set, is the commit every read after the first answers: the record moving mid-import.
|
|
moveTo string
|
|
mu sync.Mutex
|
|
reads int
|
|
}
|
|
|
|
func newSample(t *testing.T) *sampleRecords {
|
|
t.Helper()
|
|
return &sampleRecords{dirRecords: dirRecords{dir: filepath.Join("testdata", "hq")}, commit: "1ea678c6bd48f62a45db3b5e4b21b9c46867d31a"}
|
|
}
|
|
|
|
func (s *sampleRecords) Standing() (string, error) { return s.commit, nil }
|
|
func (s *sampleRecords) Read(path string) (string, string, error) {
|
|
content, _, err := s.dirRecords.Read(path)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
s.reads++
|
|
if s.moveTo != "" && s.reads > 1 {
|
|
return content, s.moveTo, nil
|
|
}
|
|
return content, s.commit, nil
|
|
}
|
|
|
|
var sampleNumbers = []uint64{7, 43, 80, 150, 236, 345, 362}
|
|
|
|
func TestTheImportCreatesEveryFolderAtItsOwnNumberAndLeavesTheCounterAlone(t *testing.T) {
|
|
h := newHarness(t)
|
|
rec := newSample(t)
|
|
out, err := h.Import(ImportAsk{Commit: "1ea678c"}, rec)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if out.Folders != 7 || out.Created != 7 || out.Filled+out.Same+out.Differs+out.Refused != 0 || out.DryRun {
|
|
t.Fatalf("%+v", out)
|
|
}
|
|
if !reflect.DeepEqual(out.ByStatus, map[string]int{StatusResolved: 5, StatusDiagnosing: 1, StatusWontfix: 1}) {
|
|
t.Fatalf("by status %v", out.ByStatus)
|
|
}
|
|
if out.Announced != 7 || len(h.said()) != 7 || h.said()[0].Event != EventImported {
|
|
t.Fatalf("announced %d, said %v", out.Announced, h.said())
|
|
}
|
|
for i, n := range sampleNumbers {
|
|
if out.Rows[i].Number != n || out.Rows[i].Action != ActionCreate {
|
|
t.Fatalf("row %d: %+v", i, out.Rows[i])
|
|
}
|
|
}
|
|
if h.mem.counter(t) != 0 {
|
|
t.Fatalf("the import moved the counter to %d", h.mem.counter(t))
|
|
}
|
|
// A report names only issues that exist: 362's report names issues the sample does not hold, which are
|
|
// said and not linked; its links to decisions stay.
|
|
if is, _ := h.Get(362); len(is.Links) == 0 {
|
|
t.Fatalf("362 has no links: %+v", is.Links)
|
|
}
|
|
for _, l := range (func() *Issue { is, _ := h.Get(362); return is })().Links {
|
|
if l.Kind == LinkRelatesTo {
|
|
n, _ := numberOfKey(l.Target)
|
|
if _, err := h.Get(n); err != nil {
|
|
t.Fatalf("362 links issue %d, which does not exist", n)
|
|
}
|
|
}
|
|
}
|
|
// The acceptance: get shows what the folder said, and who/when is the import, with the commit.
|
|
is, err := h.Get(362)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if is.Status != StatusResolved || is.Record != "04-ISSUES/362-two-merges-seconds-apart-left-their-delivery-waiting-for-a-person" ||
|
|
len(is.LocatedIn) != 2 || is.OpenedBy != ImportedBy || is.Opened.Format("2006-01-02") != "2026-10-10" ||
|
|
is.Imported == nil || is.Imported.Commit != rec.commit || is.Imported.From != is.Record+"/00-report.md" ||
|
|
is.Imported.By != Unproven || is.Imported.At != h.now() || !strings.HasPrefix(is.Report, "## Symptom") {
|
|
t.Fatalf("362 kept as %+v", is)
|
|
}
|
|
if is, _ := h.Get(362); is.Replay != "R362" || !reflect.DeepEqual(is.FixedBy, []string{"mesh-catalog#171, mesh-controller#197"}) {
|
|
t.Fatalf("362 kept as %+v", is)
|
|
}
|
|
if is, _ := h.Get(236); is.Replay != "R236" || is.Closed != nil {
|
|
t.Fatalf("236 kept as %+v", is)
|
|
}
|
|
if is, _ := h.Get(43); is.Closed == nil || is.Closed.Format("2006-01-02") != "2026-09-12" {
|
|
t.Fatalf("43 kept as %+v", is)
|
|
}
|
|
// The owners hq named that the registered list does not know are kept, and marked.
|
|
if is, _ := h.Get(7); !reflect.DeepEqual(is.LocatedInUnverified, []string{"hal (the instance)"}) || len(is.LocatedIn) != 3 {
|
|
t.Fatalf("7 kept as %+v", is)
|
|
}
|
|
if !reflect.DeepEqual(out.UnverifiedOwners["hal (the instance)"], []uint64{7}) {
|
|
t.Fatalf("unverified %v", out.UnverifiedOwners)
|
|
}
|
|
// The next open takes the number after the setting, as if nothing had been imported below it.
|
|
if got := h.open(t, "after the import"); got != 365 {
|
|
t.Fatalf("opened %d after the import, want 365", got)
|
|
}
|
|
l, _ := h.List(ListAsk{Status: StatusResolved})
|
|
if l.Matched != 5 || l.Rows[0].Number != 362 {
|
|
t.Fatalf("listed %+v", l)
|
|
}
|
|
}
|
|
|
|
func TestASecondImportChangesNothingAndSaysSo(t *testing.T) {
|
|
h := newHarness(t)
|
|
if _, err := h.Import(ImportAsk{}, newSample(t)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
before := h.mem.snapshot(IssuesState)
|
|
said := len(h.said())
|
|
out, err := h.Import(ImportAsk{}, newSample(t))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if out.Created != 0 || out.Filled != 0 || out.Same != 7 || out.Differs != 0 || out.Refused != 0 || !strings.Contains(out.Said, "Nothing changed") {
|
|
t.Fatalf("%+v", out)
|
|
}
|
|
if !reflect.DeepEqual(h.mem.snapshot(IssuesState), before) || len(h.said()) != said {
|
|
t.Fatal("a second import changed an issue or said an event")
|
|
}
|
|
}
|
|
|
|
func TestAnIssueTheTrackerHoldsDifferentlyIsNeverOverwritten(t *testing.T) {
|
|
h := newHarness(t)
|
|
if _, err := h.Import(ImportAsk{}, newSample(t)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// The tracker is the truth once imported: 345 moves on, 80 takes a note.
|
|
if _, err := h.Move(MoveAsk{Number: 345, Status: StatusLocated, LocatedIn: []string{"mesh-catalog modules/mesh-issues"}}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := h.Note(80, "a note"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
before := h.mem.snapshot(IssuesState)
|
|
out, err := h.Import(ImportAsk{}, newSample(t))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if out.Differs != 1 || out.Same != 6 || out.Created != 0 {
|
|
t.Fatalf("%+v", out)
|
|
}
|
|
var row ImportRow
|
|
for _, r := range out.Rows {
|
|
if r.Number == 345 {
|
|
row = r
|
|
}
|
|
}
|
|
if row.Action != ActionDiffers || !strings.Contains(row.Why, "status, located-in") || !strings.Contains(row.Why, "the tracker is the truth") {
|
|
t.Fatalf("row %+v", row)
|
|
}
|
|
if !reflect.DeepEqual(h.mem.snapshot(IssuesState), before) {
|
|
t.Fatal("the import changed an issue the tracker holds")
|
|
}
|
|
}
|
|
|
|
func TestAFolderBackedIssueTheTrackerOpenedTakesItsStatusFromItsFolderOnce(t *testing.T) {
|
|
h := newHarness(t)
|
|
// Before slice 3, an issue opened with a folder had no status in the tracker; its folder kept it.
|
|
h.mem.put(IssuesState, "362", Issue{Number: 362, Title: "opened through the tracker", Report: "r", OpenedBy: Unproven,
|
|
Record: "04-ISSUES/362-two-merges-seconds-apart-left-their-delivery-waiting-for-a-person", Opening: "x",
|
|
Notes: []Note{{By: Unproven, Text: "kept"}}})
|
|
h.settings.FirstNumber = 300 // so 362 is a number the tracker gave
|
|
out, err := h.Import(ImportAsk{}, newSample(t))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// 345 is past the first number too, with no issue in the tracker: refused, as a folder made without it.
|
|
if out.Filled != 1 || out.Created != 5 || out.Refused != 1 {
|
|
t.Fatalf("%+v", out)
|
|
}
|
|
is, _ := h.Get(362)
|
|
if is.Status != StatusResolved || is.Title != "opened through the tracker" || is.Report != "r" || len(is.Notes) != 1 ||
|
|
is.OpenedBy != Unproven || is.Imported == nil || len(is.LocatedIn) != 2 || len(is.Links) == 0 {
|
|
t.Fatalf("362 filled as %+v", is)
|
|
}
|
|
if !strings.Contains(strings.Join(out.Odd, "\n"), "362: the tracker's title") {
|
|
t.Fatalf("odd %v", out.Odd)
|
|
}
|
|
out, _ = h.Import(ImportAsk{}, newSample(t))
|
|
if out.Filled != 0 || out.Same != 6 || out.Refused != 1 {
|
|
t.Fatalf("second: %+v", out)
|
|
}
|
|
}
|
|
|
|
func TestATrackerIssueWithoutAStatusAndWithoutAFolderBecomesOpen(t *testing.T) {
|
|
h := newHarness(t)
|
|
h.mem.put(IssuesState, "375", Issue{Number: 375, Title: "t", Report: "r", OpenedBy: Unproven, Record: "04-ISSUES/375-not-merged", Opening: "x"})
|
|
out, err := h.Import(ImportAsk{DryRun: true}, newSample(t))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if out.Filled != 1 || out.Rows[len(out.Rows)-1].Action != ActionWould+ActionFill || out.Rows[len(out.Rows)-1].Number != 375 {
|
|
t.Fatalf("dry: %+v", out)
|
|
}
|
|
if is, _ := h.Get(375); is.Status != "" {
|
|
t.Fatal("a dry run wrote a status")
|
|
}
|
|
if _, err := h.Import(ImportAsk{}, newSample(t)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if is, _ := h.Get(375); is.Status != StatusOpen || is.Imported == nil || is.Imported.From != "" {
|
|
t.Fatalf("375 as %+v", is)
|
|
}
|
|
}
|
|
|
|
func TestADryRunReadsEverythingAndWritesNothing(t *testing.T) {
|
|
h := newHarness(t)
|
|
out, err := h.Import(ImportAsk{DryRun: true}, newSample(t))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !out.DryRun || out.Created != 7 || out.Rows[0].Action != ActionWould+ActionCreate || !strings.Contains(out.Said, "would create 7") ||
|
|
!strings.Contains(out.Said, "Nothing was written") {
|
|
t.Fatalf("%+v", out)
|
|
}
|
|
if keys, _ := h.mem.Keys(IssuesState); len(keys) != 0 || len(h.said()) != 0 || h.mem.counter(t) != 0 {
|
|
t.Fatal("a dry run wrote")
|
|
}
|
|
}
|
|
|
|
func TestTheImportRefusesWhatItMustNotWrite(t *testing.T) {
|
|
// A folder numbered past the counter: the counter's number, not a folder's to take. Nothing is imported.
|
|
h := newHarness(t)
|
|
h.mem.put(CountersState, IssueCounter, 100)
|
|
if _, err := h.Import(ImportAsk{}, newSample(t)); err == nil || !strings.Contains(err.Error(), "past the counter") {
|
|
t.Fatalf("past the counter: %v", err)
|
|
}
|
|
if keys, _ := h.mem.Keys(IssuesState); len(keys) != 0 {
|
|
t.Fatal("imported past the counter")
|
|
}
|
|
|
|
// A folder at or past the first number the tracker holds no issue for: made without the tracker.
|
|
h = newHarness(t)
|
|
h.settings.FirstNumber = 300
|
|
out, err := h.Import(ImportAsk{}, newSample(t))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if out.Refused != 2 || out.Created != 5 {
|
|
t.Fatalf("%+v", out)
|
|
}
|
|
for _, r := range out.Rows {
|
|
if (r.Number == 345 || r.Number == 362) && (r.Action != ActionRefused || !strings.Contains(r.Why, "made without it")) {
|
|
t.Fatalf("row %+v", r)
|
|
}
|
|
}
|
|
|
|
// The record standing elsewhere than asked.
|
|
h = newHarness(t)
|
|
if _, err := h.Import(ImportAsk{Commit: "abcdef0"}, newSample(t)); err == nil || !strings.Contains(err.Error(), "stands at hq commit 1ea678c6bd48") {
|
|
t.Fatalf("another commit: %v", err)
|
|
}
|
|
if _, err := h.Import(ImportAsk{Commit: "main"}, newSample(t)); err == nil || !strings.Contains(err.Error(), "hex digits") {
|
|
t.Fatalf("a commit that is no commit: %v", err)
|
|
}
|
|
|
|
// A status-less issue the tracker holds under another folder's name is left alone and said.
|
|
h = newHarness(t)
|
|
h.settings.FirstNumber = 300
|
|
h.mem.put(IssuesState, "345", Issue{Number: 345, Title: "t", Report: "r", OpenedBy: Unproven, Record: "04-ISSUES/345-another-name", Opening: "x"})
|
|
out, err = h.Import(ImportAsk{}, newSample(t))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, r := range out.Rows {
|
|
if r.Number == 345 && (r.Action != ActionDiffers || !strings.Contains(r.Why, "not its own")) {
|
|
t.Fatalf("row %+v", r)
|
|
}
|
|
}
|
|
if is, _ := h.Get(345); is.Status != "" {
|
|
t.Fatal("a status was taken from a folder that is not the issue's")
|
|
}
|
|
|
|
// The record moving while it is read.
|
|
h = newHarness(t)
|
|
rec := newSample(t)
|
|
rec.moveTo = "ffffffffffff"
|
|
if _, err := h.Import(ImportAsk{}, rec); err == nil || !strings.Contains(err.Error(), "moved while it was read") {
|
|
t.Fatalf("a moving record: %v", err)
|
|
}
|
|
if keys, _ := h.mem.Keys(IssuesState); len(keys) != 0 {
|
|
t.Fatal("imported from a moving record")
|
|
}
|
|
|
|
// An issue that appears under the number while the import runs is never overwritten.
|
|
h = newHarness(t)
|
|
h.log.failNext = createAttempts
|
|
out, err = h.Import(ImportAsk{}, newSample(t))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if out.Refused != 1 || out.Created != 6 || !strings.Contains(out.Rows[0].Why, "could not be written") {
|
|
t.Fatalf("%+v", out)
|
|
}
|
|
}
|
|
|
|
func TestAReportThatCannotBeReadIsRefusedAloneAndSaidWhy(t *testing.T) {
|
|
dir := t.TempDir()
|
|
write := func(folder, text string) {
|
|
if err := os.MkdirAll(filepath.Join(dir, IssuesFolder, folder), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(dir, IssuesFolder, folder, "00-report.md"), []byte(text), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
write("001-good", "---\nstatus: open\nopened: 2026-10-01\nlocated-in: [mesh-controller]\n---\n# 1. Good\n\nbody\n")
|
|
write("002-sixth-status", "---\nstatus: fixed\n---\n# 2. t\n\nbody\n")
|
|
write("003-no-title", "---\nstatus: open\n---\nbody\n")
|
|
write("004-a-secret", "---\nstatus: open\n---\n# 4. t\n\nthe key is sk-ant-api03-abcdefghijklmnopqrstuvwxyz\n")
|
|
write("005-twice", "---\nstatus: open\n---\n# 5. t\n\nbody\n")
|
|
write("005-again", "---\nstatus: open\n---\n# 5. t\n\nbody\n")
|
|
write("006-no-report", "")
|
|
if err := os.Remove(filepath.Join(dir, IssuesFolder, "006-no-report", "00-report.md")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.MkdirAll(filepath.Join(dir, IssuesFolder, "README"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
h := newHarness(t)
|
|
out, err := h.Import(ImportAsk{}, dirRecords{dir: dir})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if out.Folders != 7 || out.Created != 1 || out.Refused != 6 {
|
|
t.Fatalf("%+v", out)
|
|
}
|
|
why := map[uint64]string{}
|
|
for _, r := range out.Rows {
|
|
why[r.Number] = r.Why
|
|
}
|
|
for n, want := range map[uint64]string{2: "not one of", 3: "no title", 4: "an Anthropic key", 5: "two folders are numbered 5", 6: "could not be read"} {
|
|
if !strings.Contains(why[n], want) {
|
|
t.Errorf("%d: %q lacks %q", n, why[n], want)
|
|
}
|
|
}
|
|
if !strings.Contains(strings.Join(out.Odd, "\n"), "README is not an issue folder") {
|
|
t.Fatalf("odd %v", out.Odd)
|
|
}
|
|
if is, err := h.Get(1); err != nil || is.Status != StatusOpen || is.Title != "Good" {
|
|
t.Fatalf("1: %+v %v", is, err)
|
|
}
|
|
}
|
|
|
|
func TestTheImportVerbReadsItsArgumentsStrictly(t *testing.T) {
|
|
h := newHarness(t)
|
|
h.Records = newSample(t)
|
|
if _, err := h.call(t, "import", map[string]any{"path": "/tmp"}); err == nil || !strings.Contains(err.Error(), "does not take path") {
|
|
t.Fatalf("an unknown argument: %v", err)
|
|
}
|
|
if _, err := h.call(t, "import", map[string]any{"dry-run": "yes"}); err == nil {
|
|
t.Fatal("dry-run as text")
|
|
}
|
|
out, err := h.call(t, "import", map[string]any{"dry-run": true, "commit": "1ea678c"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := out.(*Imported); !got.DryRun || got.Created != 7 {
|
|
t.Fatalf("%+v", got)
|
|
}
|
|
}
|