Files
mesh-catalog/modules/mesh-knowledge/cmd/mesh-knowledge/sectionform_test.go
T
jschoubben bd9764ef9a CAT-K5: a section is a part with a plain title, never a file; a reference has one form
A new rule, section-form (31 now), refuses from the day the records move a section whose title is a file name, a number
prefix, a slash or over 200 bytes, a title twice in a record, and a text that holds a local path, a file and line, or a
link to a file. Only a section that is new or changed is judged, so a record imported with file-named sections keeps
them until its form-only version; write refuses at once.

Issue 1966.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 11:56:31 +01:00

64 lines
3.2 KiB
Go

package main
import (
"strings"
"testing"
)
// A section is a part with a title, never a file; a reference has one form (to-be 55 §2.2, AC49).
func TestASectionIsAPartWithATitleNeverAFile(t *testing.T) {
for _, c := range []struct {
name, title, text string
refused string // what the refusal says; empty when the section is taken
}{
{"a plain title", "How it is checked", "Each rule has a test; see ADR 0290 §7.", ""},
{"a title with a dot inside", "Phase 1.5 and the rest", "text", ""},
{"an extension", "overview.md", "text", "file extension"},
{"a number prefix", "01 findings", "text", ""},
{"a number prefix and dash", "01-findings", "text", "number prefix"},
{"a slash", "design/notes", "text", "slash"},
{"a title over 200 bytes", strings.Repeat("a", 201), "text", "at most 200"},
{"no title", " ", "text", "no title"},
{"a home folder", "Notes", "I read /home/someone/projects/x.txt on the machine.", "local path"},
{"the temporary folder", "Notes", "The output was in /var/tmp/run1.", "local path"},
{"a file and line", "Notes", "The bug is in kb.go:412 of the module.", "file and line"},
{"a relative link", "Notes", "See [the record](0290-a-decision-record.md).", "links to a file"},
{"a parent link", "Notes", "See [it](../01-RESEARCH/x).", "links to a file"},
{"an address", "Notes", "See [the forge](https://forge.example/x.md) and [below](#notes).", ""},
{"a version number", "Notes", "It is v1.2:3 in the table.", ""},
} {
got := strings.Join(sectionFormProblems(c.title, c.text), "; ")
if (c.refused == "") != (got == "") || !strings.Contains(got, c.refused) {
t.Errorf("%s: %q", c.name, got)
}
}
}
// The rule holds a record from the day the records move, and judges only a section that is new or changed: a record
// imported with file-named sections keeps them until its form-only version. A write refuses it at once.
func TestTheFormOfASectionIsHeldAtTheWriteAndOnlyOnWhatChanged(t *testing.T) {
w, _ := ledgerWorld(t)
r, _ := w.k.ReserveNumber("author@g14", KindResearch, "an effort")
key := Key{KindResearch, r.Number}
old := effortAsk(r.Number, StatusActive, Section{Name: "00-overview.md", Text: "An older effort, see [x](x.md)."})
w.accepted(t, "author@g14", old) // dated before the rule: taken, file-named as it was
next := old
next.Date = ""
next.Why = "a new version"
next.Sections = append(next.Sections, Section{Name: "01-findings.md", Text: "More."})
w.clock = w.clock.AddDate(0, 0, 30) // the day of the proposal is past the rule's first day
_, err := w.k.Propose("author@g14", next)
if err == nil || !strings.Contains(err.Error(), "- section-form:") || strings.Contains(err.Error(), "00-overview.md") {
t.Fatalf("a new file-named section, with the old one left alone: %v", err)
}
if _, err := w.k.Open("author@g14", ProposeAsk{Kind: KindResearch, Number: r.Number, Status: StatusActive}); err != nil {
t.Fatal(err)
}
if _, err := w.k.Write("author@g14", key, "02-more.md", "x"); err == nil || !strings.Contains(err.Error(), "nothing was written") {
t.Fatalf("a write of a file-named section: %v", err)
}
if _, err := w.k.Write("author@g14", key, "More", "see /tmp/x"); err == nil {
t.Fatal("a write of a local path")
}
}