94 lines
3.2 KiB
Go
94 lines
3.2 KiB
Go
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(`<!--\s*index:(start|end)\s*-->`), "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": "<!-- index:start -->\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)}))
|
|
}
|