mesh/merge-gate pass: builds new: modules/mesh-knowledge, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compo…
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 superseded: a newer head of the same pull request
An agent learns what a decision says only by reading whole records. This module holds every decision record and research effort whole in a database in the store, with clauses that carry their own status and source, typed links read from both ends, and every version. It answers ask, get and holds within a stated bound, and takes a record through reserve, propose (refused on any of the 29 per-record rules), review by someone other than the author, and acceptance, on the operator's proven warrant when the record needs it. The 4 whole-set rules make the module unhealthy when they fail. The import reads hq at a commit through the records module, never overwrites, and changes nothing when run again. Prose decisions get drafted clauses that a second judgement must confirm before they are served.
183 lines
6.6 KiB
Go
183 lines
6.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// to-be 55 §10: "The import changes nothing on a second run, and loses nothing — the module's test over a fixture
|
|
// repository." The fixture is real hq records copied at the commit testdata/hq/COMMIT names.
|
|
|
|
func TestTheImportWritesEveryRecordWholeAndASecondRunChangesNothing(t *testing.T) {
|
|
eachStore(t, func(t *testing.T, s Store) {
|
|
w := newWorld(t, s, fixture())
|
|
ctx := context.Background()
|
|
dry, err := w.k.Import("import@test", ImportAsk{DryRun: true}, fixture())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if all, _ := s.All(ctx); len(all) != 0 || dry.Created != 11 || !strings.Contains(dry.Said, "a dry run") {
|
|
t.Fatalf("the dry run wrote %d records, or would create %d: %s", len(all), dry.Created, dry.Said)
|
|
}
|
|
first, err := w.k.Import("import@test", ImportAsk{}, fixture())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if first.Created != 11 || first.Refused != 0 || first.Held != 0 || first.ToDraft != 5 {
|
|
t.Fatalf("first run: %+v", first)
|
|
}
|
|
if len(w.said.of(EventImported)) != 11 {
|
|
t.Fatalf("%d imported events", len(w.said.of(EventImported)))
|
|
}
|
|
// Loses nothing: every file's record, whole.
|
|
commit := strings.TrimSpace(readFile(t, "testdata/hq/COMMIT"))
|
|
entries, _ := os.ReadDir("testdata/hq/02-DECISIONS")
|
|
for _, f := range entries {
|
|
if !adrFile.MatchString(f.Name()) {
|
|
continue
|
|
}
|
|
text := readFile(t, filepath.Join("testdata/hq/02-DECISIONS", f.Name()))
|
|
p, err := ParseDecision(f.Name(), text)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
e := mustGet(t, s, p.Record.Key())
|
|
r := e.Current
|
|
if r.Title != p.Record.Title || r.Status != p.Record.Status || r.Topic != p.Record.Topic || r.Date != p.Record.Date ||
|
|
len(r.Sections) != len(p.Record.Sections) || len(r.Clauses) != len(p.Record.Clauses) || e.Versions != 1 ||
|
|
r.Source != "hq@"+commit+":02-DECISIONS/"+f.Name() {
|
|
t.Errorf("%s is not held whole: %+v", f.Name(), r)
|
|
}
|
|
for i, sec := range p.Record.Sections {
|
|
if r.Sections[i] != sec {
|
|
t.Errorf("%s's section %s differs", f.Name(), sec.Name)
|
|
}
|
|
}
|
|
// Every section of the file, read back in order, is the file's body after its heading.
|
|
if body := sectionsText(r); !strings.Contains(text, strings.TrimSpace(r.Sections[len(r.Sections)-1].Text)) || body == "" {
|
|
t.Errorf("%s's last section is not the file's", f.Name())
|
|
}
|
|
}
|
|
// 0276 writes nine numbered clauses with a bold lead: written and in force. 0240 writes prose: none yet,
|
|
// waiting to be drafted. 0127 is superseded by 0131.
|
|
r276 := mustGet(t, s, Key{KindDecision, 276}).Current
|
|
if len(r276.Clauses) != 9 || r276.Clauses[0].Source != SourceWritten || r276.Clauses[0].Status != ClauseInForce ||
|
|
!strings.HasPrefix(r276.Clauses[0].Text, "**A rolling merge window.**") {
|
|
t.Fatalf("0276's clauses: %+v", r276.Clauses)
|
|
}
|
|
if r := mustGet(t, s, Key{KindDecision, 240}).Current; len(r.Clauses) != 0 || r.Drafting == nil || r.Drafting.State != DraftWaiting {
|
|
t.Fatalf("0240: %+v", r.Drafting)
|
|
}
|
|
if r := mustGet(t, s, Key{KindDecision, 127}).Current; r.Status != StatusSuperseded {
|
|
t.Fatalf("0127: %s", r.Status)
|
|
}
|
|
// 0282 supersedes part of 0276, as hq wrote it: the link is held, its clause waiting to be judged.
|
|
r282 := mustGet(t, s, Key{KindDecision, 282}).Current
|
|
var changes *Link
|
|
for i, l := range r282.Links {
|
|
if l.Kind == LinkChanges && l.To.Number == 276 {
|
|
changes = &r282.Links[i]
|
|
}
|
|
}
|
|
if changes == nil || changes.Source != LinkPending || changes.To.Clause != 0 {
|
|
t.Fatalf("0282's change of 0276: %+v", r282.Links)
|
|
}
|
|
in, _ := s.Inbound(ctx, Key{KindDecision, 276})
|
|
if len(in) == 0 {
|
|
t.Fatal("0276 reads no link from its end")
|
|
}
|
|
// Research: 045 and 042, every document a section.
|
|
r45 := mustGet(t, s, Key{KindResearch, 45}).Current
|
|
if r45.Status != StatusGraduated || len(r45.Sections) != 2 || r45.Sections[0].Name != "00-overview.md" {
|
|
t.Fatalf("research 045: %s %v", r45.Status, r45.Sections)
|
|
}
|
|
// A second run changes nothing, and says so.
|
|
before := countVersions(t, s)
|
|
second, err := w.k.Import("import@test", ImportAsk{}, fixture())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if second.Created != 0 || second.Held != 11 || !strings.Contains(second.Said, "Nothing changed") || countVersions(t, s) != before {
|
|
t.Fatalf("second run: %+v", second)
|
|
}
|
|
if len(w.said.of(EventImported)) != 11 {
|
|
t.Fatal("the second run said an import")
|
|
}
|
|
})
|
|
}
|
|
|
|
// The import never overwrites: a record changed here keeps its change through a later import.
|
|
func TestTheImportNeverOverwrites(t *testing.T) {
|
|
w := imported(t, newMemStore())
|
|
ctx := context.Background()
|
|
e := mustGet(t, w.k.Store, Key{KindDecision, 11})
|
|
r := e.Current.Clone()
|
|
r.Title = "changed here"
|
|
if err := w.k.Store.Save(ctx, Version{By: "test", Why: "changed", Record: r}, true, false, e.Revision); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := w.k.Import("import@test", ImportAsk{}, fixture()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := mustGet(t, w.k.Store, Key{KindDecision, 11}).Current.Title; got != "changed here" {
|
|
t.Fatalf("overwritten: %q", got)
|
|
}
|
|
}
|
|
|
|
func TestTheImportRefusesACommitTheRecordDoesNotStandAt(t *testing.T) {
|
|
w := newWorld(t, newMemStore(), fixture())
|
|
if _, err := w.k.Import("import@test", ImportAsk{Commit: "0000000"}, fixture()); err == nil || !strings.Contains(err.Error(), "not 0000000") {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
commit := strings.TrimSpace(readFile(t, "testdata/hq/COMMIT"))
|
|
if out, err := w.k.Import("import@test", ImportAsk{Commit: commit[:9]}, fixture()); err != nil || out.Created != 11 {
|
|
t.Fatalf("%v %+v", err, out)
|
|
}
|
|
if _, err := w.k.Import("import@test", ImportAsk{Commit: "not-hex"}, fixture()); err == nil {
|
|
t.Fatal("a commit that is not one was taken")
|
|
}
|
|
}
|
|
|
|
// The whole of main at a recorded commit is imported without a refusal, and leaves the whole set consistent.
|
|
func TestTheWholeOfMainImports(t *testing.T) {
|
|
eachStore(t, func(t *testing.T, s Store) {
|
|
w := newWorld(t, s, hqMain(t))
|
|
out, err := w.k.Import("import@test", ImportAsk{}, hqMain(t))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if out.Refused != 0 || out.Created < 300 {
|
|
t.Fatalf("%s; rows %+v", out.Said, out.Rows)
|
|
}
|
|
if len(out.Findings) != 0 {
|
|
t.Fatalf("the whole set of main fails: %+v", out.Findings)
|
|
}
|
|
t.Log(out.Said)
|
|
})
|
|
}
|
|
|
|
func readFile(t *testing.T, path string) string {
|
|
t.Helper()
|
|
raw, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return string(raw)
|
|
}
|
|
|
|
func countVersions(t *testing.T, s Store) int {
|
|
t.Helper()
|
|
all, err := s.All(context.Background())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
n := 0
|
|
for _, e := range all {
|
|
n += e.Versions
|
|
}
|
|
return n
|
|
}
|