From 25a307792534aea3982e95cc59f2268b4506d75e Mon Sep 17 00:00:00 2001 From: M Date: Mon, 31 Aug 2026 13:41:09 +0200 Subject: [PATCH] Say what a dot-prefixed data entry actually does MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two cases claimed to pin that a category or folder named with the reference prefix is rejected. They passed for the wrong reason: the entry is skipped as hidden, which left the directory empty, so New failed with "no .json data found" instead. Beside a valid sibling both load fine and are simply absent. Replaced with a test of the real rule — a leading dot means hidden, which is what lets a data directory be a checkout — and that covers a ".." name too. Co-Authored-By: Claude Opus 5 --- reference_test.go | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/reference_test.go b/reference_test.go index c08e82b..3ddf688 100644 --- a/reference_test.go +++ b/reference_test.go @@ -104,9 +104,7 @@ func TestReferenceErrors(t *testing.T) { "mutual cycle across two folders": {"en_US/a": `{"format":"{..sv_SE.b}"}`, "sv_SE/b": `{"format":"{..en_US.a}"}`}, // ".." is reserved for bound references, so an authored key using it would // name a node nothing can reach and nothing would validate. - "field key using the reference prefix": {"cat": `{"format":"hi","..x":{"format":"{..nope}"}}`}, - "category name using the reference prefix": {"sv_SE/..bad": `{"format":"{..nope}"}`}, - "folder name using the reference prefix": {"sv_SE/..y/cat": `{"format":"{..nope}"}`}, + "field key using the reference prefix": {"cat": `{"format":"hi","..x":{"format":"{..nope}"}}`}, } for name, files := range cases { if _, err := New([]string{writeData(t, files)}); err == nil { @@ -115,6 +113,22 @@ func TestReferenceErrors(t *testing.T) { } } +// TestDotPrefixedDataEntriesAreSkipped pins what a leading dot means on disk: the +// entry is hidden, not data, so a data directory can also be a checkout. A name +// starting with the reference prefix is covered by that same rule, since ".." +// starts with "." — it is skipped, not rejected. +func TestDotPrefixedDataEntriesAreSkipped(t *testing.T) { + f := newFakes(t, writeData(t, map[string]string{ + "sv_SE/ok": `["fine"]`, + "sv_SE/..bad": `{"format":"{..nope}"}`, + "sv_SE/..y/ct": `{"format":"{..nope}"}`, + ".git/config": `["not data"]`, + }), WithSeed(1)) + if got := f.List(); len(got) != 1 || got[0] != "sv_SE.ok" { + t.Fatalf("List() = %v, want only sv_SE.ok", got) + } +} + // TestReferenceFromUnrenderedFieldTerminates guards the cycle check against // over-rejecting: a field the format never renders may point back at its own // category, which terminates, and stays renderable by path.