Tests for overlap via calc and references, repeat behind a choice, and nested choice draws
This commit is contained in:
@@ -70,6 +70,74 @@ func TestRenderingALevelAndReadingIntoItIsRejected(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCalcOperandNamingABoundLevelIsRejected(t *testing.T) {
|
||||||
|
// A calc operand renders its field, so naming a bound level in one is the same
|
||||||
|
// overlap as a bare token: {calc(item * 1)} renders what {item.price} reads a
|
||||||
|
// path into, and the two disagree.
|
||||||
|
_, err := New([]string{writeData(t, map[string]string{
|
||||||
|
"cat": `{"format":"{item.price}|{calc(item * 1)}","item":[` +
|
||||||
|
`{"format":"{price}","price":"10"},{"format":"{price}","price":"20"}]}`,
|
||||||
|
})})
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "reads a path into") {
|
||||||
|
t.Fatalf("New = %v, want the calc operand rejected as an overlap", err)
|
||||||
|
}
|
||||||
|
// Arithmetic over fields no path names is untouched.
|
||||||
|
if _, err := New([]string{writeData(t, map[string]string{
|
||||||
|
"cat": `{"format":"{net} x {qty} = {calc(net * qty, 2)}","net":["19.99"],"qty":["3"]}`,
|
||||||
|
})}); err != nil {
|
||||||
|
t.Errorf("New = %v, want plain arithmetic accepted", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReferenceNamingABoundLevelIsRejected(t *testing.T) {
|
||||||
|
// A reference can name a bound level from the data root, which renders it
|
||||||
|
// afresh beside the path that reads its held draw — the same overlap by
|
||||||
|
// another spelling.
|
||||||
|
_, err := New([]string{writeData(t, map[string]string{
|
||||||
|
"cat": `{"format":"{p.first}|{..cat.p}","p":[{"format":"{first}","first":["Anna","Bo"]}]}`,
|
||||||
|
})})
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "reads a path into") {
|
||||||
|
t.Fatalf("New = %v, want the reference rejected as an overlap", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNestedChoiceDrawsOneVariant(t *testing.T) {
|
||||||
|
// A choice item may itself be a choice, so drawing a bound head unwraps until
|
||||||
|
// it reaches a value. Stopping at one level leaves a choice where the walk
|
||||||
|
// expects a template.
|
||||||
|
f := engine(21)
|
||||||
|
seen := map[string]bool{}
|
||||||
|
for i := 0; i < 200; i++ {
|
||||||
|
seen[mustRender(t, f, `{"format":"{p.x}","p":[[{"format":"{x}","x":"1"}],[{"format":"{x}","x":"2"}]]}`)] = true
|
||||||
|
}
|
||||||
|
if !seen["1"] || !seen["2"] || len(seen) != 2 {
|
||||||
|
t.Fatalf("200 draws produced %v, want 1 and 2", seen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRepeatingLevelIsNamedInTheError(t *testing.T) {
|
||||||
|
// The level carrying the repeat is the one to fix, so the error names it
|
||||||
|
// rather than the head the path started from.
|
||||||
|
_, err := New([]string{writeData(t, map[string]string{
|
||||||
|
"cat": `{"format":"[{p.a.b}]","p":{"format":"{a}","a":{"format":"{b}","repeat":3,"separator":",","b":["z"]}}}`,
|
||||||
|
})})
|
||||||
|
if err == nil || !strings.Contains(err.Error(), `"p.a"`) {
|
||||||
|
t.Fatalf("New = %v, want it to name the level p.a that carries the repeat", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPathIntoARepeatingLevelBehindAChoiceIsRejected(t *testing.T) {
|
||||||
|
// A choice of rows is the shape this feature is for, so the repeat rule has to
|
||||||
|
// reach inside one — otherwise the direct spelling is a load error and the same
|
||||||
|
// mistake behind a choice silently drops the repeat.
|
||||||
|
_, err := New([]string{writeData(t, map[string]string{
|
||||||
|
"cat": `{"format":"[{p.a}]","p":[{"format":"{a}","repeat":3,"separator":",","a":["x"]},{"format":"{a}","a":["y"]}]}`,
|
||||||
|
})})
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "repeat") {
|
||||||
|
t.Fatalf("New = %v, want the repeat behind a choice rejected", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestPathIntoARepeatingLevelIsRejected(t *testing.T) {
|
func TestPathIntoARepeatingLevelIsRejected(t *testing.T) {
|
||||||
// A path reads one level's draw, so it can never apply that level's repeat.
|
// A path reads one level's draw, so it can never apply that level's repeat.
|
||||||
// The engine rejects options that cannot take effect, and this is one.
|
// The engine rejects options that cannot take effect, and this is one.
|
||||||
|
|||||||
Reference in New Issue
Block a user