From ba2ae41ffe6743415d5395a735ae1809235c2023 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Wed, 7 Oct 2026 23:58:22 +0200 Subject: [PATCH] Test that a calc over digits(0) or a repeat of 0 is refused, that an option of the wrong kind names its JSON kind, and that the root scope is the category --- calc_test.go | 15 ++++++++++++--- datatype_test.go | 2 +- imports_test.go | 5 +++-- name_test.go | 11 +++++++++++ node_test.go | 11 +++++++++++ struct_test.go | 3 +++ 6 files changed, 41 insertions(+), 6 deletions(-) diff --git a/calc_test.go b/calc_test.go index a544daf..90ad27b 100644 --- a/calc_test.go +++ b/calc_test.go @@ -309,6 +309,15 @@ func TestCalcRefusesABadOperand(t *testing.T) { } } +func TestATypedColumnRefusesACalcOverNoDigits(t *testing.T) { + _, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{ + "row": `{"format":"","x":{"format":"{calc(d + 1)}","d":"{digits(0)}","datatype":"integer"}}`, + }))) + if err == nil || !strings.Contains(err.Error(), "{digits(0)} prints nothing, which is no number") { + t.Fatalf("New = %v, want a calc over {digits(0)} refused in a typed column", err) + } +} + func TestATypedColumnProvesACalcOverAName(t *testing.T) { dir := writeData(t, map[string]string{ "n": `"{int(1,9)}"`, @@ -334,8 +343,8 @@ func TestATypedColumnProvesACalcOverAName(t *testing.T) { } } -func TestARepeatOfZeroIsNoConstant(t *testing.T) { - if got := mustRender(t, engine(1), `{"format":"{calc(x / y)}","x":"1","y":{"format":"0","repeat":0}}`); got != "NaN" { - t.Errorf("a divisor rendering nothing = %q, want NaN", got) +func TestARepeatOfZeroIsNeverANumber(t *testing.T) { + if _, err := resolved(t, `{"format":"{calc(x / y)}","x":"1","y":{"format":"0","repeat":0}}`); err == nil || !strings.Contains(err.Error(), `operand "y" is never a number: it renders ""`) { + t.Errorf("a calc over a repeat of 0 = %v, want the operand refused as never a number", err) } } diff --git a/datatype_test.go b/datatype_test.go index 2d4c149..bcc4d7a 100644 --- a/datatype_test.go +++ b/datatype_test.go @@ -26,7 +26,7 @@ func TestDatatypeSitsOnlyInAColumn(t *testing.T) { } for src, want := range map[string]string{ `{"format":"","n":{"format":"1","datatype":"int"}}`: `datatype takes "string", "integer", "number" or "boolean", got "int"`, - `{"format":"","n":{"format":"1","datatype":1}}`: "datatype must be a string", + `{"format":"","n":{"format":"1","datatype":1}}`: "datatype must be a string, not a number", } { 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/imports_test.go b/imports_test.go index 854d118..b9a4c39 100644 --- a/imports_test.go +++ b/imports_test.go @@ -29,14 +29,15 @@ func TestPackageImports(t *testing.T) { } checked := 0 allowed := map[string][]string{ - ".": {"internal/builtinfunc", "internal/datafiles", "internal/datatype", "internal/drawstate", "internal/grammar", "internal/invariant", "internal/proven", "internal/rows"}, + ".": {"internal/builtinfunc", "internal/datafiles", "internal/datatype", "internal/drawstate", "internal/grammar", "internal/invariant", "internal/jsonvalue", "internal/proven", "internal/rows"}, "cmd/fejkdata": {"."}, "internal/builtinfunc": {"internal/datatype", "internal/drawstate", "internal/grammar", "internal/invariant", "internal/proven"}, - "internal/datafiles": {"internal/grammar"}, + "internal/datafiles": {"internal/grammar", "internal/jsonvalue"}, "internal/datatype": nil, "internal/drawstate": nil, "internal/grammar": nil, "internal/invariant": nil, + "internal/jsonvalue": nil, "internal/proven": {"internal/datatype", "internal/grammar", "internal/invariant"}, "internal/rows": {"internal/drawstate", "internal/grammar", "internal/invariant"}, } diff --git a/name_test.go b/name_test.go index fa120ca..4245053 100644 --- a/name_test.go +++ b/name_test.go @@ -1,6 +1,7 @@ package fejkdata import ( + "fmt" "strings" "testing" ) @@ -182,6 +183,16 @@ func TestAChoiceItemKeepsTheNamesItBinds(t *testing.T) { } } +func TestTheRootScopeIsTheCategory(t *testing.T) { + root := compiled(t, `{"format":"{x as n}{n}{y}","x":"a","y":{"format":"{z as m}{m}","z":"b","repeat":2}}`).(*template) + if k := fmt.Sprint(root.nameScope.kind()); k != "category" { + t.Errorf("root scope kind = %q, want category", k) + } + if k := fmt.Sprint(root.fields["y"].(*template).nameScope.kind()); k != "repeat" { + t.Errorf("repeat scope kind = %q, want repeat", k) + } +} + func TestNameErrors(t *testing.T) { cases := []struct { name string diff --git a/node_test.go b/node_test.go index c7a7bbf..a7e32ad 100644 --- a/node_test.go +++ b/node_test.go @@ -2,9 +2,20 @@ package fejkdata import ( "regexp" + "strings" "testing" ) +func TestAnOptionOfTheWrongKindIsNamedByItsJSONKind(t *testing.T) { + if _, err := resolved(t, `{"format":"x","repeat":2,"separator":5}`); err == nil || !strings.Contains(err.Error(), "separator must be a string, not a number") { + t.Errorf("separator 5 = %v, want the kind named", err) + } + _, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv","key":5}`, "t.tsv": "a\nx\n"}))) + if err == nil || !strings.Contains(err.Error(), "key must be a string, not a number") { + t.Errorf("key 5 = %v, want the kind named", err) + } +} + func TestWeightSkewsDistribution(t *testing.T) { f := engine(9) heavy := 0 diff --git a/struct_test.go b/struct_test.go index 92c4d17..085013b 100644 --- a/struct_test.go +++ b/struct_test.go @@ -309,6 +309,9 @@ func TestFakeStructErrors(t *testing.T) { {&struct { A int `fake:"person.first"` }{}, `"Ada" is not an integer`}, + {&struct { + A int `fake:"{\"format\":\"{calc(d + 1)}\",\"d\":\"{digits(0)}\"}"` + }{}, "{digits(0)} prints nothing, which is no number"}, {&struct { A int8 `fake:"{int(0,300)}"` }{}, `"{int(0,300)}" is not proven within int8; narrow it to that range, or make the field int64`},