Tests for references held like siblings and the lowercase, uppercase and ascii transforms

This commit is contained in:
2026-09-02 12:42:21 +02:00
parent f0305f191f
commit 8fa33694b9
3 changed files with 121 additions and 11 deletions
+6 -6
View File
@@ -88,18 +88,18 @@ func TestCalcReproducible(t *testing.T) {
}
}
// TestCalcTokenOperandsReadsOnlyACalc pins what the helper answers for a body that
// TestTokenOperandsReadsOnlyAnOperandBuiltin pins what the helper answers for a body that
// is not a calc, and for one whose expression does not parse: nothing, either way.
// checkCalc is what reports a bad expression, so the callers that run after it
// never meet one — but they must not have to depend on that order to be safe.
func TestCalcTokenOperandsReadsOnlyACalc(t *testing.T) {
func TestTokenOperandsReadsOnlyAnOperandBuiltin(t *testing.T) {
for _, body := range []string{"plain", "luhn()", "calc()", "calc(1 +)", "calc(()"} {
if got := calcTokenOperands(body); got != nil {
t.Errorf("calcTokenOperands(%q) = %v, want none", body, got)
if got := tokenOperands(body); got != nil {
t.Errorf("tokenOperands(%q) = %v, want none", body, got)
}
}
if got := calcTokenOperands("calc(net * qty)"); len(got) != 2 {
t.Errorf("calcTokenOperands(calc(net * qty)) = %v, want both operands", got)
if got := tokenOperands("calc(net * qty)"); len(got) != 2 {
t.Errorf("tokenOperands(calc(net * qty)) = %v, want both operands", got)
}
}
+62 -5
View File
@@ -1,6 +1,9 @@
package fejkdata
import "testing"
import (
"strings"
"testing"
)
// TestRootReferenceAcrossFolders is the headline case: a category in one folder
// pulls a value from another via a {..path} reference resolved from the data root.
@@ -15,8 +18,7 @@ func TestRootReferenceAcrossFolders(t *testing.T) {
}
}
// TestReferenceIntoAField reaches a field inside a referenced category, crossing
// a single-variant choice and then a template field (..who.last).
// TestReferenceIntoAField reaches a field inside a referenced category.
func TestReferenceIntoAField(t *testing.T) {
dir := writeData(t, map[string]string{
"who": `{"format":"{first} {last}","first":"Ada","last":"Byron"}`,
@@ -76,8 +78,8 @@ func TestReferenceErrors(t *testing.T) {
cases := map[string]map[string]string{
"missing target": {"card": `"{..nope.gone}"`},
"folder target": {"en_US/word": `"w"`, "card": `"{..en_US}"`},
"multi-variant on the path": {
"who": `[{"format":"{f}","f":"1"},{"format":"{f}","f":"2"}]`,
"a variant on the path lacks the field": {
"who": `[{"format":"{f}","f":"1"},{"format":"{g}","g":"2"}]`,
"card": `"{..who.f}"`,
},
"empty reference path": {"card": `"{..}"`},
@@ -208,3 +210,58 @@ func TestNewErrorPathIsCanonical(t *testing.T) {
}
}
}
func TestReferencePathIsHeld(t *testing.T) {
dir := writeData(t, map[string]string{
"person": `[{"format":"{first} {last}","first":"Anna","last":"Andersson"},{"format":"{first} {last}","first":"Bo","last":"Berg"}]`,
"card": `"{..person.first} {..person.last}"`,
})
f := newGenerator(t, dir, WithSeed(3))
seen := map[string]bool{}
for i := 0; i < 50; i++ {
got := fake(t, f, "card")
if got != "Anna Andersson" && got != "Bo Berg" {
t.Fatalf("card = %q, want one person's first and last name", got)
}
seen[got] = true
}
if len(seen) != 2 {
t.Fatalf("card only ever rendered %v", seen)
}
}
func TestReferenceThroughChoiceNeedsEveryVariant(t *testing.T) {
_, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{
"who": `[{"format":"{f}{h}","f":"1","h":"x"},{"format":"{g}{h}","g":"2","h":"y"}]`,
"card": `"{..who.f}"`,
})))
if err == nil || !strings.Contains(err.Error(), "not every variant") {
t.Fatalf("New = %v, want the missing variant named", err)
}
}
func TestBareReferenceDrawsEachTime(t *testing.T) {
dir := writeData(t, map[string]string{
"die": `["1","2","3","4","5","6"]`,
"roll": `"{..die} {..die}"`,
})
f := newGenerator(t, dir, WithSeed(1))
for i := 0; i < 50; i++ {
if got := fake(t, f, "roll"); got[0] != got[2] {
return
}
}
t.Fatal("two bare references always agreed; each should be its own draw")
}
func TestReferenceOverlapIsRejected(t *testing.T) {
for name, file := range map[string]string{
"head beside a path": `{"format":"{..cat.p} {..cat.p.first}","p":[{"format":"{first}","first":"A"},{"format":"{first}","first":"B"}]}`,
"sibling path beside a reference path": `{"format":"{p.first} {..cat.p.last}","p":[{"format":"{first}","first":"A","last":"1"},{"format":"{first}","first":"B","last":"2"}]}`,
} {
_, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{"cat": file})))
if err == nil || !strings.Contains(err.Error(), "reads a path into") {
t.Errorf("%s: New = %v, want the overlap rejected", name, err)
}
}
}
+53
View File
@@ -0,0 +1,53 @@
package fejkdata
import "testing"
func TestTransforms(t *testing.T) {
f := engine(1)
for src, want := range map[string]string{
`{"format":"{uppercase(x)}|{lowercase(x)}|{ascii(x)}","x":"Åsa Ödegård-Nuñez"}`: "ÅSA ÖDEGÅRD-NUÑEZ|åsa ödegård-nuñez|Asa Odegard-Nunez",
`{"format":"{lowercase(ascii(x))}","x":"Åsa"}`: "asa",
`{"format":"{ascii(x)}","x":"Ærø ß 日本"}`: "AEro ss ",
} {
if got := mustRender(t, f, src); got != want {
t.Errorf("render(%s) = %q, want %q", src, got, want)
}
}
}
func TestTransformReadsTheHeldDraw(t *testing.T) {
f := engine(2)
src := `{"format":"{p.first}={lowercase(p.first)}","p":[{"format":"{first}","first":"Anna"},{"format":"{first}","first":"Bo"}]}`
for i := 0; i < 50; i++ {
if got := mustRender(t, f, src); got != "Anna=anna" && got != "Bo=bo" {
t.Fatalf("render = %q, want the transform over the same draw", got)
}
}
}
func TestTransformOverAReference(t *testing.T) {
dir := writeData(t, map[string]string{
"person": `[{"format":"{first} {last}","first":"Åsa","last":"Öberg"},{"format":"{first} {last}","first":"Bo","last":"Ek"}]`,
"email": `"{..person.first} {..person.last} <{lowercase(ascii(..person.first))}.{lowercase(ascii(..person.last))}@example.com>"`,
})
f := newGenerator(t, dir, WithSeed(5))
for i := 0; i < 50; i++ {
if got := fake(t, f, "email"); got != "Åsa Öberg <asa.oberg@example.com>" && got != "Bo Ek <bo.ek@example.com>" {
t.Fatalf("email = %q, want a record from one draw", got)
}
}
}
func TestTransformArgs(t *testing.T) {
for _, bad := range []string{
`{"format":"{lowercase()}","x":"v"}`,
`{"format":"{lowercase(nope)}","x":"v"}`,
`{"format":"{ascii(x,y)}","x":"v","y":"w"}`,
`{"format":"{lowercase(hex(2))}","x":"v"}`,
`{"format":"{lowercase(x)} {x.a}","x":{"format":"{a}","a":"1"}}`,
} {
if _, err := compile(parse(t, bad)); err == nil {
t.Errorf("compile(%s) = nil error, want it rejected", bad)
}
}
}