Link every test fragment before asserting on it

This commit is contained in:
2026-09-28 02:53:08 +02:00
committed by Lillem4n
parent e0969e69e7
commit e11ee9ec0e
9 changed files with 36 additions and 26 deletions
+6 -6
View File
@@ -142,7 +142,7 @@ func TestBuiltinArgLimits(t *testing.T) {
`{"format":"{x}","x":"1","repeat":2000000000}`, // repeat -> multi-GB join `{"format":"{x}","x":"1","repeat":2000000000}`, // repeat -> multi-GB join
} }
for _, tmpl := range bad { for _, tmpl := range bad {
if _, err := compile(parse(t, tmpl)); err == nil { if _, err := linked(t, tmpl); err == nil {
t.Errorf("compile(%s) = nil error, want a limit rejection", tmpl) t.Errorf("compile(%s) = nil error, want a limit rejection", tmpl)
} }
} }
@@ -226,12 +226,12 @@ func mustPanic(t *testing.T, name string, call func()) {
func TestClassBuiltinArgs(t *testing.T) { func TestClassBuiltinArgs(t *testing.T) {
for _, bad := range []string{`"{digits(0)}"`, `"{digits(2000000000)}"`, `"{upper(-1)}"`, `"{lower(x)}"`, `"{digits()}"`, `"{upper(1,2)}"`} { for _, bad := range []string{`"{digits(0)}"`, `"{digits(2000000000)}"`, `"{upper(-1)}"`, `"{lower(x)}"`, `"{digits()}"`, `"{upper(1,2)}"`} {
if _, err := compile(parse(t, bad)); err == nil { if _, err := linked(t, bad); err == nil {
t.Errorf("compile(%s) = nil error, want the arg rejected", bad) t.Errorf("compile(%s) = nil error, want the arg rejected", bad)
} }
} }
for _, ok := range []string{`"{digits(1048576)}"`, `"{upper(1)}"`, `"{lower(26)}"`} { for _, ok := range []string{`"{digits(1048576)}"`, `"{upper(1)}"`, `"{lower(26)}"`} {
if _, err := compile(parse(t, ok)); err != nil { if _, err := linked(t, ok); err != nil {
t.Errorf("compile(%s) = %v", ok, err) t.Errorf("compile(%s) = %v", ok, err)
} }
} }
@@ -313,7 +313,7 @@ func TestBuiltinDateArgs(t *testing.T) {
`"{time('x')}"`: "text", `"{time('x')}"`: "text",
`"{date(1990-01-01,1990-12-31,'15:04')}"`: "time('15:04')", `"{date(1990-01-01,1990-12-31,'15:04')}"`: "time('15:04')",
} { } {
_, err := compile(parse(t, tmpl)) _, err := linked(t, tmpl)
if err == nil || !strings.Contains(err.Error(), want) { if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("compile(%s) = %v, want an error mentioning %q", tmpl, err, want) t.Errorf("compile(%s) = %v, want an error mentioning %q", tmpl, err, want)
} }
@@ -427,7 +427,7 @@ func TestBuiltinCompileErrors(t *testing.T) {
`"{iban(US)}"`, // unsupported country `"{iban(US)}"`, // unsupported country
`"{seq(a,b)}"`, // seq takes at most one name `"{seq(a,b)}"`, // seq takes at most one name
} { } {
if _, err := compile(parse(t, bad)); err == nil { if _, err := linked(t, bad); err == nil {
t.Errorf("compile(%s) = nil error, want error", bad) t.Errorf("compile(%s) = nil error, want error", bad)
} }
} }
@@ -441,7 +441,7 @@ func TestArgErrorsNameTheSpelling(t *testing.T) {
`"{hex(99999999999999999999)}"`: "exceeds the maximum", `"{hex(99999999999999999999)}"`: "exceeds the maximum",
`"{int(007,9)}"`: "write 7", `"{int(007,9)}"`: "write 7",
} { } {
if _, err := compile(parse(t, src)); err == nil || !strings.Contains(err.Error(), want) { if _, err := linked(t, src); err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("compile(%s) = %v, want an error saying %q", src, err, want) t.Errorf("compile(%s) = %v, want an error saying %q", src, err, want)
} }
} }
+4 -4
View File
@@ -229,7 +229,7 @@ func TestCalcOverANeverNumericOperandIsRejected(t *testing.T) {
`{"format":"{calc(x * 2)}","x":["a","b"]}`: `"x"`, `{"format":"{calc(x * 2)}","x":["a","b"]}`: `"x"`,
`{"format":"{calc(x + y)}","x":"1","y":"{{2}}"}`: `"{2}"`, `{"format":"{calc(x + y)}","x":"1","y":"{{2}}"}`: `"{2}"`,
} { } {
_, err := compile(parse(t, src)) _, err := linked(t, src)
if err == nil || !strings.Contains(err.Error(), want) || !strings.Contains(err.Error(), "never a number") { if err == nil || !strings.Contains(err.Error(), want) || !strings.Contains(err.Error(), "never a number") {
t.Errorf("compile(%s) = %v, want the operand rejected naming %s", src, err, want) t.Errorf("compile(%s) = %v, want the operand rejected naming %s", src, err, want)
} }
@@ -240,7 +240,7 @@ func TestCalcOverANeverNumericOperandIsRejected(t *testing.T) {
`{"format":"{calc(x * 2)}","x":["1","abc"]}`, `{"format":"{calc(x * 2)}","x":["1","abc"]}`,
`{"format":"{calc(x * 2)}","x":"{digits(2)}"}`, `{"format":"{calc(x * 2)}","x":"{digits(2)}"}`,
} { } {
if _, err := compile(parse(t, ok)); err != nil { if _, err := linked(t, ok); err != nil {
t.Errorf("compile(%s) = %v, want it accepted", ok, err) t.Errorf("compile(%s) = %v, want it accepted", ok, err)
} }
} }
@@ -255,7 +255,7 @@ func TestCalcConstantZeroDivisorIsRejected(t *testing.T) {
`{"format":"{calc((a/0)+b)}","a":"1","b":"2"}`: "divides by 0", `{"format":"{calc((a/0)+b)}","a":"1","b":"2"}`: "divides by 0",
`{"format":"{calc(a/-0)}","a":"1"}`: "divides by -0", `{"format":"{calc(a/-0)}","a":"1"}`: "divides by -0",
} { } {
if _, err := compile(parse(t, src)); err == nil || !strings.Contains(err.Error(), want) { if _, err := linked(t, src); err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("compile(%s) = %v, want the constant zero divisor rejected naming %q", src, err, want) t.Errorf("compile(%s) = %v, want the constant zero divisor rejected naming %q", src, err, want)
} }
} }
@@ -284,7 +284,7 @@ func TestCalcCompileErrors(t *testing.T) {
`"{calc(1, x)}"`, // decimals arg not an integer `"{calc(1, x)}"`, // decimals arg not an integer
`"{calc(1, -1)}"`, // decimals negative `"{calc(1, -1)}"`, // decimals negative
} { } {
if _, err := compile(parse(t, bad)); err == nil { if _, err := linked(t, bad); err == nil {
t.Errorf("compile(%s) = nil error, want error", bad) t.Errorf("compile(%s) = nil error, want error", bad)
} }
} }
+2 -2
View File
@@ -18,7 +18,7 @@ func TestDatatypeAndNullSitOnlyInAColumn(t *testing.T) {
`{"format":"","age":[null,{"format":"{int(18,99)}","datatype":"integer","weight":9}]}`, `{"format":"","age":[null,{"format":"{int(18,99)}","datatype":"integer","weight":9}]}`,
`{"format":"","pick":[[null,"a"],"b"]}`, `{"format":"","pick":[[null,"a"],"b"]}`,
} { } {
if _, err := compile(parse(t, src)); err != nil { if _, err := linked(t, src); err != nil {
t.Errorf("compile(%s) = %v, want a column to take a datatype and null", src, err) t.Errorf("compile(%s) = %v, want a column to take a datatype and null", src, err)
} }
} }
@@ -32,7 +32,7 @@ func TestDatatypeAndNullSitOnlyInAColumn(t *testing.T) {
`null`: `so write ""`, `null`: `so write ""`,
`{"format":"{p}","p":{"format":"{x}","x":[null,"a"]}}`: `so write ""`, `{"format":"{p}","p":{"format":"{x}","x":[null,"a"]}}`: `so write ""`,
} { } {
if _, err := compile(parse(t, src)); err == nil || !strings.Contains(err.Error(), want) { if _, err := linked(t, src); err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("compile(%s) = %v, want an error containing %q", src, err, want) t.Errorf("compile(%s) = %v, want an error containing %q", src, err, want)
} }
} }
+12 -2
View File
@@ -113,10 +113,20 @@ func parse(t *testing.T, s string) any {
return v return v
} }
// compiled parses and compiles a JSON fragment into a node. // linked compiles a JSON fragment and links it against no data.
func compiled(t *testing.T, s string) node { func linked(t *testing.T, s string) (node, error) {
t.Helper() t.Helper()
n, err := compile(parse(t, s)) n, err := compile(parse(t, s))
if err != nil {
return nil, err
}
return n, linkNodeRefs(inlineScope(n, "template"), nil)
}
// compiled parses, compiles and links a JSON fragment into a node.
func compiled(t *testing.T, s string) node {
t.Helper()
n, err := linked(t, s)
if err != nil { if err != nil {
t.Fatalf("compile %q: %v", s, err) t.Fatalf("compile %q: %v", s, err)
} }
+3 -3
View File
@@ -21,7 +21,7 @@ func TestStringIsAFormat(t *testing.T) {
if got := mustRender(t, f, `"{digits(3)}"`); !regexp.MustCompile(`^[0-9]{3}$`).MatchString(got) { if got := mustRender(t, f, `"{digits(3)}"`); !regexp.MustCompile(`^[0-9]{3}$`).MatchString(got) {
t.Errorf(`render("{digits(3)}") = %q, want three digits`, got) t.Errorf(`render("{digits(3)}") = %q, want three digits`, got)
} }
if _, err := compile(parse(t, `"{x}"`)); err == nil || !strings.Contains(err.Error(), `no field "x"`) { if _, err := linked(t, `"{x}"`); err == nil || !strings.Contains(err.Error(), `no field "x"`) {
t.Errorf(`compile("{x}") = %v, want a no-field error`, err) t.Errorf(`compile("{x}") = %v, want a no-field error`, err)
} }
} }
@@ -51,7 +51,7 @@ func TestBraceEscapes(t *testing.T) {
`{"format":"{a{b}","a":"Q"}`: "'{'", `{"format":"{a{b}","a":"Q"}`: "'{'",
`"{x"`: "unterminated", `"{x"`: "unterminated",
} { } {
if _, err := compile(parse(t, src)); err == nil || !strings.Contains(err.Error(), want) { if _, err := linked(t, src); err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("compile(%s) = %v, want an error mentioning %s", src, err, want) t.Errorf("compile(%s) = %v, want an error mentioning %s", src, err, want)
} }
} }
@@ -126,7 +126,7 @@ func TestFormatCompileErrors(t *testing.T) {
`"{int(1)}"`, // wrong arity `"{int(1)}"`, // wrong arity
`"{float(1,2)}"`, // wrong arity `"{float(1,2)}"`, // wrong arity
} { } {
if _, err := compile(parse(t, bad)); err == nil { if _, err := linked(t, bad); err == nil {
t.Errorf("compile(%s) = nil error, want error", bad) t.Errorf("compile(%s) = nil error, want error", bad)
} }
} }
+2 -2
View File
@@ -743,7 +743,7 @@ func TestRepeatedBareTokenOfAHeldNameIsRejected(t *testing.T) {
`{"format":"{n} + {n} = {calc(n * 2)}","n":["1","2"]}`: "write {n} once", `{"format":"{n} + {n} = {calc(n * 2)}","n":["1","2"]}`: "write {n} once",
`{"format":"{p.a} {q} {q} {lowercase(q)}","p":{"format":"{a}","a":"1"},"q":["A","B"]}`: "write {q} once", `{"format":"{p.a} {q} {q} {lowercase(q)}","p":{"format":"{a}","a":"1"},"q":["A","B"]}`: "write {q} once",
} { } {
_, err := compile(parse(t, src)) _, err := linked(t, src)
if err == nil || !strings.Contains(err.Error(), want) || !strings.Contains(err.Error(), "holds") { if err == nil || !strings.Contains(err.Error(), want) || !strings.Contains(err.Error(), "holds") {
t.Errorf("compile(%s) = %v, want the repeated token rejected naming %s", src, err, want) t.Errorf("compile(%s) = %v, want the repeated token rejected naming %s", src, err, want)
} }
@@ -754,7 +754,7 @@ func TestRepeatedBareTokenOfAHeldNameIsRejected(t *testing.T) {
`{"format":"{net} x {qty} = {calc(net * qty, 2)}","net":["19.99","5.00"],"qty":["3","7"]}`, `{"format":"{net} x {qty} = {calc(net * qty, 2)}","net":["19.99","5.00"],"qty":["3","7"]}`,
`{"format":"{uppercase(w)} {uppercase(w)}","w":["a","b"]}`, `{"format":"{uppercase(w)} {uppercase(w)}","w":["a","b"]}`,
} { } {
if _, err := compile(parse(t, ok)); err != nil { if _, err := linked(t, ok); err != nil {
t.Errorf("compile(%s) = %v, want it accepted", ok, err) t.Errorf("compile(%s) = %v, want it accepted", ok, err)
} }
} }
+2 -2
View File
@@ -7,7 +7,7 @@ import (
) )
func TestWeightZeroIsRejected(t *testing.T) { func TestWeightZeroIsRejected(t *testing.T) {
_, err := compile(parse(t, `[{"format":"X","weight":0},"Y"]`)) _, err := linked(t, `[{"format":"X","weight":0},"Y"]`)
if err == nil || !strings.Contains(err.Error(), "weight 0") || !strings.Contains(err.Error(), "remove") { if err == nil || !strings.Contains(err.Error(), "weight 0") || !strings.Contains(err.Error(), "remove") {
t.Fatalf("compile(weight 0) = %v, want it rejected naming the fix", err) t.Fatalf("compile(weight 0) = %v, want it rejected naming the fix", err)
} }
@@ -75,7 +75,7 @@ func TestNodeCompileErrors(t *testing.T) {
`{"format":"x","repeat":2,"separator":5}`, // non-string separator `{"format":"x","repeat":2,"separator":5}`, // non-string separator
`{"format":"{x}","x":"v","repeat":2,"separator":""}`, // separator "" is the default `{"format":"{x}","x":"v","repeat":2,"separator":""}`, // separator "" is the default
} { } {
if _, err := compile(parse(t, bad)); err == nil { if _, err := linked(t, bad); err == nil {
t.Errorf("compile(%s) = nil error, want error", bad) t.Errorf("compile(%s) = nil error, want error", bad)
} }
} }
+4 -4
View File
@@ -7,7 +7,7 @@ import (
func TestOneItemChoiceIsRejected(t *testing.T) { func TestOneItemChoiceIsRejected(t *testing.T) {
for _, src := range []string{`["x"]`, `[{"format":"{d}","d":"x"}]`, `{"format":"{w}","w":["only"]}`} { for _, src := range []string{`["x"]`, `[{"format":"{d}","d":"x"}]`, `{"format":"{w}","w":["only"]}`} {
if _, err := compile(parse(t, src)); err == nil || !strings.Contains(err.Error(), "one-item choice") { if _, err := linked(t, src); err == nil || !strings.Contains(err.Error(), "one-item choice") {
t.Errorf("compile(%s) = %v, want the one-item choice rejected", src, err) t.Errorf("compile(%s) = %v, want the one-item choice rejected", src, err)
} }
} }
@@ -20,11 +20,11 @@ func TestRepeatedChoiceItemIsRejected(t *testing.T) {
`{"format":"{w}","w":["", "", "x"]}`: `{ "format": "", "weight": 2 }`, `{"format":"{w}","w":["", "", "x"]}`: `{ "format": "", "weight": 2 }`,
`{"format":"","w":[null,null,"a"]}`: "a null takes no weight", `{"format":"","w":[null,null,"a"]}`: "a null takes no weight",
} { } {
if _, err := compile(parse(t, src)); err == nil || !strings.Contains(err.Error(), want) { if _, err := linked(t, src); err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("compile(%s) = %v, want an error naming %s", src, err, want) t.Errorf("compile(%s) = %v, want an error naming %s", src, err, want)
} }
} }
if _, err := compile(parse(t, `[{"format":"a","weight":2}, "b"]`)); err != nil { if _, err := linked(t, `[{"format":"a","weight":2}, "b"]`); err != nil {
t.Errorf("compile(weighted a, b) = %v", err) t.Errorf("compile(weighted a, b) = %v", err)
} }
} }
@@ -41,7 +41,7 @@ func TestInertObjectIsRejected(t *testing.T) {
`{"format":"{x}","x":{"format":"{y}","y":"v","drawGroup":""}}`: `drawGroup "" is the default`, `{"format":"{x}","x":{"format":"{y}","y":"v","drawGroup":""}}`: `drawGroup "" is the default`,
`{"format":"{x}","x":{"format":"{y}","y":"v","drawGroup":1}}`: "drawGroup must be a string", `{"format":"{x}","x":{"format":"{y}","y":"v","drawGroup":1}}`: "drawGroup must be a string",
} { } {
if _, err := compile(parse(t, src)); err == nil || !strings.Contains(err.Error(), want) { if _, err := linked(t, src); err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("compile(%s) = %v, want an error mentioning %s", src, err, want) t.Errorf("compile(%s) = %v, want an error mentioning %s", src, err, want)
} }
} }
+1 -1
View File
@@ -46,7 +46,7 @@ func TestTransformArgs(t *testing.T) {
`{"format":"{lowercase(hex(2))}","x":"v"}`, `{"format":"{lowercase(hex(2))}","x":"v"}`,
`{"format":"{lowercase(x)} {x.a}","x":{"format":"{a}","a":"1"}}`, `{"format":"{lowercase(x)} {x.a}","x":{"format":"{a}","a":"1"}}`,
} { } {
if _, err := compile(parse(t, bad)); err == nil { if _, err := linked(t, bad); err == nil {
t.Errorf("compile(%s) = nil error, want it rejected", bad) t.Errorf("compile(%s) = nil error, want it rejected", bad)
} }
} }