cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

main
012 / internal / config / docs_test.go
docs_test.go 2.7K
1package config23import (4 "io/fs"5 "os"6 "path/filepath"7 "regexp"8 "slices"9 "strings"10 "testing"11)1213var (14 // inlineSetting is a setting quoted in prose: `theme = Dracula`.15 inlineSetting = regexp.MustCompile("`([a-z][a-z0-9-]*) = [^`]*`")16 // fileSetting is a line of a config example.17 fileSetting = regexp.MustCompile(`^([a-z][a-z0-9-]*)\s*=`)18 // envVar is one of 012's own variables, or the JEV service's.19 envVar = regexp.MustCompile(`\b(?:O12|TYPESAFE)_[A-Z_]+\b`)20)2122// notOptions are variables the docs name that aren't options: the JEV key23// never goes through the config (see docs/formulas/jev.md).24var notOptions = []string{"TYPESAFE_API_KEY"}2526// TestDocsNameOptions checks the settings and variables the docs name are27// options. A config example is a fenced block whose first line is a28// comment naming the file, `# ~/.config/012/config`; other blocks (theme29// files, shell) aren't read.30func TestDocsNameOptions(t *testing.T) {31 var paths []string32 err := filepath.WalkDir("../../docs", func(p string, d fs.DirEntry, err error) error {33 if err == nil && !d.IsDir() && filepath.Ext(p) == ".md" {34 paths = append(paths, p)35 }36 return err37 })38 if err != nil {39 t.Fatal(err)40 }41 var env []string42 for _, o := range Options {43 env = append(env, o.Env...)44 }45 for _, p := range append(paths, "../../README.md") {46 data, err := os.ReadFile(p)47 if err != nil {48 t.Fatal(err)49 }50 checkDocOptions(t, strings.TrimPrefix(p, "../../"), string(data), env)51 }52}5354// checkDocOptions reports settings and variables doc names that aren't55// options.56func checkDocOptions(t *testing.T, name, doc string, env []string) {57 t.Helper()58 check := func(key string) {59 if _, ok := Lookup(key); !ok && key != "key" {60 t.Errorf("%s names setting %q, which isn't an option", name, key)61 }62 }63 for _, m := range inlineSetting.FindAllStringSubmatch(doc, -1) {64 check(m[1])65 }66 for _, block := range configBlocks(doc) {67 for _, l := range block {68 if m := fileSetting.FindStringSubmatch(l); m != nil {69 check(m[1])70 }71 }72 }73 for _, v := range envVar.FindAllString(doc, -1) {74 if !slices.Contains(env, v) && !slices.Contains(notOptions, v) {75 t.Errorf("%s names %s, which no option reads", name, v)76 }77 }78}7980// configBlocks returns the lines of the fenced blocks that are config81// examples.82func configBlocks(doc string) [][]string {83 var out [][]string84 var cur []string85 in := false86 for l := range strings.SplitSeq(doc, "\n") {87 if strings.HasPrefix(strings.TrimSpace(l), "```") {88 if in && len(cur) > 0 && strings.HasPrefix(cur[0], "#") && strings.Contains(cur[0], "012/config") {89 out = append(out, cur)90 }91 in, cur = !in, nil92 continue93 }94 if in {95 cur = append(cur, strings.TrimSpace(l))96 }97 }98 return out99}