Pin the route an operand fence names
The operand label had no test: every rejected case reported through a token
edge, so flipping the flag that distinguishes the two left the suite green
while the error went back to inventing a {b} the format never wrote. Each
case now asserts the route it names, and one reports through an operand edge,
which is the branch that was uncovered.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+28
-3
@@ -148,25 +148,50 @@ func TestALevelRenderedOnlyByAPathTokenIsHeld(t *testing.T) {
|
|||||||
// shown is not the value computed. Two names that merely draw from one source are
|
// shown is not the value computed. Two names that merely draw from one source are
|
||||||
// two draws, as {word} {word} is — the rule everywhere else in the engine.
|
// two draws, as {word} {word} is — the rule everywhere else in the engine.
|
||||||
func TestACalcOperandIsHeldAgainstEveryRoute(t *testing.T) {
|
func TestACalcOperandIsHeldAgainstEveryRoute(t *testing.T) {
|
||||||
rejected := map[string]map[string]string{
|
rejected := map[string]struct {
|
||||||
|
files map[string]string
|
||||||
|
want string // the route the error names, spelled as the author wrote it
|
||||||
|
}{
|
||||||
"a reference beside the operand": {
|
"a reference beside the operand": {
|
||||||
|
map[string]string{
|
||||||
"cat": `{"format":"{..cat.net} x 2 = {calc(net * 2, 2)}","net":["10.00","20.00"]}`,
|
"cat": `{"format":"{..cat.net} x 2 = {calc(net * 2, 2)}","net":["10.00","20.00"]}`,
|
||||||
},
|
},
|
||||||
|
`{..cat.net} renders "net"`,
|
||||||
|
},
|
||||||
"a reference one level down": {
|
"a reference one level down": {
|
||||||
|
map[string]string{
|
||||||
"cat": `{"format":"{calc(net * 2, 2)} {q}","net":["10.00","20.00"],` +
|
"cat": `{"format":"{calc(net * 2, 2)} {q}","net":["10.00","20.00"],` +
|
||||||
`"q":{"format":"{..cat.net}"}}`,
|
`"q":{"format":"{..cat.net}"}}`,
|
||||||
},
|
},
|
||||||
|
`{q} renders "net"`,
|
||||||
|
},
|
||||||
// A reference ending at the operand binds the choice itself, not a variant,
|
// A reference ending at the operand binds the choice itself, not a variant,
|
||||||
// so wrapping the operand in one changes nothing.
|
// so wrapping the operand in one changes nothing.
|
||||||
"a reference to an operand wrapped in a choice": {
|
"a reference to an operand wrapped in a choice": {
|
||||||
|
map[string]string{
|
||||||
"cat": `{"format":"{calc(n * 2, 2)} {q}","n":[{"format":"{v}","v":["1","2"]}],` +
|
"cat": `{"format":"{calc(n * 2, 2)} {q}","n":[{"format":"{v}","v":["1","2"]}],` +
|
||||||
`"q":{"format":"{..cat.n}"}}`,
|
`"q":{"format":"{..cat.n}"}}`,
|
||||||
},
|
},
|
||||||
|
`{q} renders "n"`,
|
||||||
|
},
|
||||||
|
// The reaching route can be another operand, which is not a token — so the
|
||||||
|
// error must name it as one rather than invent a {b} the format never wrote.
|
||||||
|
"an operand reaching another operand": {
|
||||||
|
map[string]string{
|
||||||
|
"cat": `{"format":"{calc(a + b, 0)}","a":{"format":"{x}","x":["1","2"]},` +
|
||||||
|
`"b":{"format":"{..cat.a}"}}`,
|
||||||
|
},
|
||||||
|
`calc operand "b" renders "a"`,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for name, files := range rejected {
|
for name, c := range rejected {
|
||||||
_, err := New([]string{writeData(t, files)})
|
_, err := New([]string{writeData(t, c.files)})
|
||||||
if err == nil || !strings.Contains(err.Error(), "a {calc()} also reads") {
|
if err == nil || !strings.Contains(err.Error(), "a {calc()} also reads") {
|
||||||
t.Errorf("%s: New = %v, want the second route to the operand rejected", name, err)
|
t.Errorf("%s: New = %v, want the second route to the operand rejected", name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), c.want) {
|
||||||
|
t.Errorf("%s: New = %v, want it to name %q", name, err, c.want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user