From 49736bd2b6ed211d7b9ad54157854006456b16ab Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Wed, 7 Oct 2026 23:16:50 +0200 Subject: [PATCH] Test that a default written out loads in every position, null outside a column is refused, an alternation reads a field once, and the CLI's lone reference refuses an empty segment --- cmd/fejkdata/lone_test.go | 25 +++++++++++++++---------- cmd/fejkdata/main_test.go | 4 ++-- datatype_test.go | 4 +++- loading_test.go | 10 +++++----- name_test.go | 6 ++++++ spellings_test.go | 22 +++++++++++++++++----- struct_test.go | 18 ++++++++++++++---- 7 files changed, 62 insertions(+), 27 deletions(-) diff --git a/cmd/fejkdata/lone_test.go b/cmd/fejkdata/lone_test.go index eacbb2d..b08a0aa 100644 --- a/cmd/fejkdata/lone_test.go +++ b/cmd/fejkdata/lone_test.go @@ -22,21 +22,26 @@ func grammarLoneReference(text string) (string, bool) { func TestLoneReferenceAgreesWithTheGrammar(t *testing.T) { for _, text := range []string{ - "{/sv_SE.person}", + " {/x}", + "x", + "{.x}", + "{/.x}", + "{//x}", + "{/a...b}", + "{/a..b}", + "{/a..}", + "{/a.[x]}", + "{/a.}", + "{/a|/b}", + "{/a}{/b}", "{/misc.currency[US Dollar (Next day)]}", + "{/sv_SE.person}", "{/t[a as b].c}", "{/t[f(x)]}", - "{/a}{/b}", + "{/x as p}", + "{/x(1)}", "{/x}}", "{/}", - "{/.x}", - " {/x}", - "{/a|/b}", - "{/x as p}", - "{//x}", - "{/x(1)}", - "{.x}", - "x", } { path, lone := loneReference(text) want, wantLone := grammarLoneReference(text) diff --git a/cmd/fejkdata/main_test.go b/cmd/fejkdata/main_test.go index 975a8d0..9041765 100644 --- a/cmd/fejkdata/main_test.go +++ b/cmd/fejkdata/main_test.go @@ -495,8 +495,8 @@ func TestRunTemplateMisuse(t *testing.T) { "42": "number", "42\n": "number", "true\n": "boolean", - "null\n": "only null", - " null ": "only null", + "null\n": `write ""`, + " null ": `write ""`, "{//sv_SE.person}": "write {/sv_SE.person}", } { code, out, errb := runOut(stdin, "--seed", "1") diff --git a/datatype_test.go b/datatype_test.go index 74adee7..7bdc0c2 100644 --- a/datatype_test.go +++ b/datatype_test.go @@ -30,7 +30,9 @@ func TestDatatypeSitsOnlyInAColumn(t *testing.T) { `{"format":"{int(1,9)}","datatype":"integer"}`: "datatype only types a record column", `[{"format":"1","datatype":"integer"},"x"]`: "datatype only types a record column", `{"format":"{p}","p":{"format":"{n}","n":{"format":"1","datatype":"integer"}}}`: "datatype only types a record column", - `{"format":"{n}","repeat":2,"n":{"format":"1","datatype":"integer"}}`: "datatype only types a record column", + `{"format":"{n}","repeat":2,"n":{"format":"1","datatype":"integer"}}`: `so it has no effect here; drop "datatype"`, + `null`: `so write ""`, + `{"format":"{p}","p":{"format":"{x}","x":[null,"a"]}}`: `so write ""`, } { if _, err := resolved(t, src); err == nil || !strings.Contains(err.Error(), want) { t.Errorf("compile(%s) = %v, want an error containing %q", src, err, want) diff --git a/loading_test.go b/loading_test.go index 5746340..2854d3c 100644 --- a/loading_test.go +++ b/loading_test.go @@ -226,23 +226,23 @@ func TestNewErrors(t *testing.T) { }{ "separator without repeat": { map[string]string{"a": `{"format":"{x}","x":"1","separator":","}`}, - "has no effect without a repeat above 1", + `drop "separator"`, }, "separator with an explicit repeat of 1": { map[string]string{"a": `{"format":"{x}","x":"1","separator":","}`}, - "has no effect without a repeat above 1", + `drop "separator"`, }, "weight outside a choice": { map[string]string{"a": `{"format":"x","weight":5}`}, - "weight only skews a choice's items", + `drop "weight"`, }, "non-numeric weight outside a choice": { map[string]string{"a": `{"format":"x","weight":"bad"}`}, - "weight only skews a choice's items", + "takes a number, not a string", }, "weight used as a field": { map[string]string{"a": `{"format":"{name} {weight}kg","name":"Anvil","weight":["7"]}`}, - "can never be a field", + `a field cannot be named "weight"`, }, "an option name used as a token": { map[string]string{"a": `"{weight}"`}, diff --git a/name_test.go b/name_test.go index 29d397e..a02f9e7 100644 --- a/name_test.go +++ b/name_test.go @@ -192,6 +192,12 @@ func TestNameErrors(t *testing.T) { `no field "zz"`}, {"read by nothing", map[string]string{"word": `["a","b"]`, "card": `"{/word as w}"`}, `nothing reads name "w"`}, + {"one arm read twice in an alternation", map[string]string{"cat": `{"format":"{w|w}","w":["a","b"]}`, "card": `"{/cat as n}{n} {n.w}"`}, + ""}, + {"a field and a path into it in one alternation", map[string]string{"cat": `{"format":"{w|w.x}","w":{"format":"{x}","x":["a","b"]}}`, "card": `"{/cat as n}{n} {n.w}"`}, + ""}, + {"one field read in two alternations", map[string]string{"cat": `{"format":"{w|v}{w|v}","w":["a","b"],"v":["c","d"]}`, "card": `"{/cat as n}{n} {n.w}"`}, + `renders field "w" twice`}, {"read in a repeat", map[string]string{"nm.json": `{"format":"{first}","rows":"nm.tsv"}`, "nm.tsv": "first\tlast\nAda\tByron\nBo\tEk\n", "person": `{"format":"{first} {last}","first":"{/nm.first}","last":"{/nm.last}"}`, "card": `{"format":"{/person as p}{each}","each":{"format":"{p.first}","repeat":2}}`}, ""}, {"a misspelt name", map[string]string{"word": `["a","b"]`, "card": `"{/word as p}{p}{p}{pp}"`}, diff --git a/spellings_test.go b/spellings_test.go index 9c05ffa..1dc0fa0 100644 --- a/spellings_test.go +++ b/spellings_test.go @@ -29,8 +29,18 @@ func TestOneItemChoiceIsItsItem(t *testing.T) { if r, isTemplate := compiled(t, `[{"format":"","d":"x"}]`).(*template); !isTemplate || !r.isRecord { t.Error("a one-item choice of a record is no record, want the record it holds") } - if _, err := resolved(t, `[{"format":"a","weight":2}]`); err == nil || !strings.Contains(err.Error(), "no effect here") { - t.Errorf("a weight on a one-item choice's item = %v, want it refused as doing nothing", err) +} + +func TestAWeightWithNothingToDrawAgainstIsRejected(t *testing.T) { + for _, src := range []string{ + `{"format":"a","weight":2}`, + `{"format":"{x}","x":{"format":"a","weight":2}}`, + `[{"format":"a","weight":2}]`, + } { + _, err := resolved(t, src) + if err == nil || !strings.Contains(err.Error(), `drop "weight"`) || strings.Contains(err.Error(), "can never be a field") || strings.Contains(err.Error(), "only skews a choice's items") { + t.Errorf("compile(%s) = %v, want the weight refused as drawn against nothing, naming the drop", src, err) + } } } @@ -53,7 +63,7 @@ func TestARepeatedItemCountsAsWritten(t *testing.T) { func TestASeparatorWithoutARepeatIsRejected(t *testing.T) { for src, want := range map[string]string{ - `{"format":"{x}","x":"v","separator":","}`: "separator", + `{"format":"{x}","x":"v","separator":","}`: `drop "separator"`, } { if _, err := resolved(t, src); err == nil || !strings.Contains(err.Error(), want) { t.Errorf("compile(%s) = %v, want an error mentioning %s", src, err, want) @@ -69,8 +79,10 @@ func TestADefaultWrittenOutIsTheDefault(t *testing.T) { `{"format":"{x}","x":["a","b"],"repeat":1}`: `{"format":"{x}","x":["a","b"]}`, `{"format":"{x}","x":["a","b"],"repeat":2,"separator":""}`: `{"format":"{x}","x":["a","b"],"repeat":2}`, `{"format":"","n":{"format":"{x}","x":["1","2"],"datatype":"string"}}`: `{"format":"","n":{"format":"{x}","x":["1","2"]}}`, - `null`: `""`, - `{"format":"{p}","p":{"format":"{x}","x":[null,"a"]}}`: `{"format":"{p}","p":{"format":"{x}","x":["","a"]}}`, + `{"format":"a","weight":1}`: `"a"`, + `[{"format":"a","weight":1}]`: `"a"`, + `{"format":"{x}","x":{"format":"a","weight":1}}`: `{"format":"{x}","x":"a"}`, + `{"format":"{x}","x":["a","b"],"separator":""}`: `{"format":"{x}","x":["a","b"]}`, } { sameRenders(t, long, short) } diff --git a/struct_test.go b/struct_test.go index 6a06ec9..863e4c0 100644 --- a/struct_test.go +++ b/struct_test.go @@ -243,6 +243,16 @@ func TestFakeStructReadsAPathInEachSpelling(t *testing.T) { } } +func TestFakeStructTakesTheDatatypeItsGoTypeSets(t *testing.T) { + v := struct { + N int `fake:"{\"format\":\"{int(1,9)}\",\"datatype\":\"integer\"}"` + S string `fake:"{\"format\":\"x\",\"datatype\":\"string\"}"` + }{} + if err := structData(t).FakeStruct(&v); err != nil || v.N < 1 || v.N > 9 || v.S != "x" { + t.Errorf("FakeStruct = %+v, %v, want each tag's datatype taken as its Go type's", v, err) + } +} + func TestFakeStructErrors(t *testing.T) { f := structData(t) for _, c := range []struct { @@ -267,11 +277,11 @@ func TestFakeStructErrors(t *testing.T) { A structPlace `fake:"place"` }{}, ".A: a struct field fills from the tags on its own fields"}, {&struct { - A int `fake:"{\"format\":\"{int(1,9)}\",\"datatype\":\"integer\"}"` - }{}, `its Go type int sets the datatype; drop "datatype"`}, + A int `fake:"{\"format\":\"{int(1,9)}\",\"datatype\":\"number\"}"` + }{}, `its Go type int sets the datatype integer; drop "datatype"`}, {&struct { - A string `fake:"{\"format\":\"x\",\"datatype\":\"string\"}"` - }{}, `its Go type string sets the datatype; drop "datatype"`}, + A string `fake:"{\"format\":\"1\",\"datatype\":\"integer\"}"` + }{}, `its Go type string sets the datatype string; drop "datatype"`}, {&struct { A int `fake:"[null,\"{int(1,9)}\"]"` }{}, "can draw null, which int cannot hold; make it *int"},