package checks import ( "fmt" "io/fs" "regexp" "strings" "testing" "testing/fstest" ) // The reading order of the decision records (index.py until ADR 0527): its topics are written in // 02-DECISIONS/README.md, its records are not. The knowledge base reads these lines and refuses a record whose // topic is not one of them, so the form is a contract; a topic's records are `knowledge-base.holds` with that // topic, never stored here (ADR 0248, issue 297). const readmeOfDecisions = "02-DECISIONS/README.md" // A topic line: `1. **Label** — `topic: name`. ...` var topicLineRx = regexp.MustCompile(`(?m)^\d+\.\s+\*\*([^*]+)\*\*\s+—\s+` + "`topic:\\s*([^`]+)`") // What a stored list looks like: the old markers, or a line of the list index.py used to print. var storedLists = []struct { rx *regexp.Regexp what string }{ {regexp.MustCompile(``), "an index marker"}, {regexp.MustCompile(`(?m)^- \*\*\d{4}\*\* — \[`), "a line of a generated list"}, } func readingOrder(text string) []string { var topics []string for _, m := range topicLineRx.FindAllStringSubmatch(text, -1) { topics = append(topics, strings.TrimSpace(m[2])) } return topics } func checkIndex(fsys fs.FS) []string { text := readFile(fsys, readmeOfDecisions) topics := readingOrder(text) var out []string if len(topics) == 0 { out = append(out, "index: "+readmeOfDecisions+" writes no reading order: no line of the form '1. **Label** — `topic: name`. ...'; "+ "the knowledge base refuses every record without it") } seen := map[string]bool{} for _, topic := range topics { if seen[topic] { out = append(out, fmt.Sprintf("index: %s names a topic twice in its reading order: %s", readmeOfDecisions, topic)) break } seen[topic] = true } for _, s := range storedLists { if s.rx.MatchString(text) { out = append(out, fmt.Sprintf("index: %s stores a list of records (%s). The records live in the knowledge base, and a topic's list is "+ "`knowledge-base.holds` with that topic", readmeOfDecisions, s.what)) } } return out } func TestIndexReadingOrderIsWrittenAndStoresNoList(t *testing.T) { expectNone(t, checkIndex(rootFS())) if n := len(readingOrder(readFile(rootFS(), readmeOfDecisions))); n == 0 { t.Error("the reading order has no topic") } } const sound = "1. **One** — `topic: one`. x\n2. **Two** — `topic: two`. y\n" func TestIndexCatchesAMissingReadingOrder(t *testing.T) { expectProblem(t, checkIndex(fstest.MapFS{readmeOfDecisions: doc("nothing written here\n")}), "writes no reading order") } func TestIndexCatchesADuplicateTopic(t *testing.T) { broken := fstest.MapFS{readmeOfDecisions: doc(sound + "3. **Again** — `topic: one`. z\n")} expectProblem(t, checkIndex(broken), "names a topic twice") } func TestIndexCatchesAStoredList(t *testing.T) { for what, stored := range map[string]string{ "an index marker": "\n", "a line of a generated list": "- **0001** — [a title](x.md)\n", } { expectProblem(t, checkIndex(fstest.MapFS{readmeOfDecisions: doc(sound + stored)}), what) } } func TestIndexAcceptsASoundReadme(t *testing.T) { expectNone(t, checkIndex(fstest.MapFS{readmeOfDecisions: doc(sound)})) }