From 8d0393713c0382a426138c6a9830810a382513cb Mon Sep 17 00:00:00 2001 From: M Date: Fri, 28 Aug 2026 00:17:27 +0200 Subject: [PATCH] Tests for options that cannot take effect, and entries that are not data --- edge_test.go | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ loading_test.go | 25 +++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/edge_test.go b/edge_test.go index e2c1d94..a3191a4 100644 --- a/edge_test.go +++ b/edge_test.go @@ -63,6 +63,72 @@ func TestNewErrors(t *testing.T) { if _, err := New([]string{writeData(t, map[string]string{"broken": `{ not json`})}); err == nil { t.Error("New(invalid JSON) = nil error") } + // An option that cannot take effect, and a category or folder no dot path can + // reach, are mistakes New must name rather than accept and ignore. + rejected := map[string]struct { + files map[string]string + want string + }{ + "separator without repeat": { + map[string]string{"a": `{"format":"{x}","x":["1"],"separator":","}`}, + "has no effect without a repeat above 1", + }, + "separator with an explicit repeat of 1": { + map[string]string{"a": `{"format":"{x}","x":["1"],"repeat":1,"separator":","}`}, + "has no effect without a repeat above 1", + }, + "weight outside a choice": { + map[string]string{"a": `{"format":"x","weight":5}`}, + "weight only skews a choice's items", + }, + "an option name used as a token": { + map[string]string{"a": `{"format":"{weight}"}`}, + `"weight" is an option, never a field`, + }, + "category name with a dot": { + map[string]string{"a.b": `["1"]`}, + `category "a.b" contains a dot`, + }, + "folder name with a dot": { + map[string]string{"a.b/cat": `["1"]`}, + `/a.b: folder "a.b" contains a dot`, + }, + } + for name, c := range rejected { + _, err := New([]string{writeData(t, c.files)}) + if err == nil { + t.Errorf("%s: New = nil error, want it rejected at load", name) + continue + } + if !strings.Contains(err.Error(), c.want) { + t.Errorf("%s: New = %v, want it to mention %q", name, err, c.want) + } + } + // Data that works today must keep working, and stay reachable. + accepted := map[string]struct { + files map[string]string + path string + want string + }{ + "option name as a field": {map[string]string{"a": `{"format":"{name} {Weight}kg","name":["Anvil"],"Weight":["7"]}`}, "a", "Anvil 7kg"}, + "format spelling as a field": {map[string]string{"a": `{"format":"{Format}","Format":["PDF"]}`}, "a", "PDF"}, + "dotted field via its token": {map[string]string{"a": `{"format":"[{a.b}]","a.b":["V"]}`}, "a", "[V]"}, + "hyphenated field": {map[string]string{"a": `{"format":"{x-y}","x-y":["1"]}`}, "a.x-y", "1"}, + "category named Format": {map[string]string{"Format": `["1"]`}, "Format", "1"}, + "folder named Repeat": {map[string]string{"Repeat/cat": `["1"]`}, "Repeat.cat", "1"}, + "field with a paren": {map[string]string{"a": `{"format":"{x}","x":["1"],"b(c":["2"]}`}, "a.b(c", "2"}, + "repeat without a separator": {map[string]string{"a": `{"format":"{x}","repeat":3,"x":["1"]}`}, "a", "111"}, + } + for name, c := range accepted { + f, err := New([]string{writeData(t, c.files)}) + if err != nil { + t.Errorf("%s: New = %v, want it accepted", name, err) + continue + } + if got, err := f.Fake(c.path); err != nil || got != c.want { + t.Errorf("%s: Fake(%q) = %q, %v, want %q", name, c.path, got, err, c.want) + } + } } // --- deep path navigation --- diff --git a/loading_test.go b/loading_test.go index 992ff04..f0fb5e1 100644 --- a/loading_test.go +++ b/loading_test.go @@ -5,6 +5,7 @@ import ( "path/filepath" "reflect" "slices" + "strings" "testing" ) @@ -156,3 +157,27 @@ func TestListedPathsAllRender(t *testing.T) { } } } + +// TestHiddenEntriesAreSkipped keeps a data directory usable when it is also a +// checkout or an editor workspace: a dot-prefixed entry is not data, and a folder +// carrying no JSON never contributed a namespace, so neither may fail the load. +func TestHiddenEntriesAreSkipped(t *testing.T) { + dir := writeData(t, map[string]string{ + "cat": `["V"]`, + ".git/HEAD": `["ignored"]`, + ".hidden": `["ignored"]`, + "empty.folder/doc": `["ignored"]`, + }) + if err := os.Rename(filepath.Join(dir, "empty.folder", "doc.json"), filepath.Join(dir, "empty.folder", "doc.txt")); err != nil { + t.Fatal(err) + } + f := newFakes(t, dir, WithSeed(1)) + if got := fake(t, f, "cat"); got != "V" { + t.Fatalf("cat = %q, want V", got) + } + for _, p := range f.List() { + if strings.HasPrefix(p, ".") || strings.Contains(p, "empty.folder") { + t.Errorf("List() advertises %q, which is not data", p) + } + } +}