Weighted person names, valid personal ids and date() in both locales #21
+48
-2
@@ -298,8 +298,8 @@ func TestBuiltinDateAndTime(t *testing.T) {
|
||||
func TestBuiltinDateArgs(t *testing.T) {
|
||||
for tmpl, want := range map[string]string{
|
||||
`"{date(1990-13-01,1990-12-31,'2006-01-02')}"`: "1990-13-01",
|
||||
`"{date(1990-12-31,1990-01-01,'2006-01-02')}"`: "before",
|
||||
`"{date(1990-01-01,1990-01-01,'2006-01-02')}"`: "before",
|
||||
`"{date(1990-12-31,1990-01-01,'2006-01-02')}"`: "is after",
|
||||
`"{date(1990-01-01,1990-01-01,'2006-01-02')}"`: "write it as text",
|
||||
`"{date(1990-01-01,1990-12-31,2006-01-02)}"`: "'2006-01-02'",
|
||||
`"{date(1990-01-01,1990-12-31,'January 2, 2006)}"`: "'",
|
||||
`"{date(1990-01-01,1990-12-31,'x')}"`: "text",
|
||||
@@ -318,3 +318,49 @@ func TestBuiltinDateArgs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestBuiltinLayoutErrorsNameARunnableSpelling pins the two layout errors a user
|
||||
// can follow: a double-quoted layout is named single-quoted, without its own
|
||||
// quotes carried into the suggestion, and a bare one says a shell ate them.
|
||||
func TestBuiltinLayoutErrorsNameARunnableSpelling(t *testing.T) {
|
||||
_, err := compile(parse(t, `"{date(1990-01-01,1990-12-31,\"2006-01-02\")}"`))
|
||||
if err == nil || !strings.Contains(err.Error(), "write '2006-01-02'") {
|
||||
t.Fatalf("a double-quoted layout = %v, want it named single-quoted", err)
|
||||
}
|
||||
if strings.Contains(err.Error(), `'"`) {
|
||||
t.Errorf("%v names a layout that renders its own quotes", err)
|
||||
}
|
||||
_, err = compile(parse(t, `"{time(15:04)}"`))
|
||||
if err == nil || !strings.Contains(err.Error(), "write '15:04'") || !strings.Contains(err.Error(), "shell") {
|
||||
t.Fatalf("a bare layout = %v, want it named quoted and the shell explained", err)
|
||||
}
|
||||
// The comma hint belongs to a layout that split, not to a call given extra args.
|
||||
_, err = compile(parse(t, `"{time(0,12,'15:04')}"`))
|
||||
if err == nil || !strings.Contains(err.Error(), "takes 1 argument, got 3") {
|
||||
t.Fatalf("time with three args = %v, want the count named in the singular", err)
|
||||
}
|
||||
if strings.Contains(err.Error(), "holding a comma") {
|
||||
t.Errorf("%v offers the comma hint though the layout is already quoted", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestBuiltinDateSpansOneDay pins that from == to is a day: with a clock layout it
|
||||
// draws every second of it, and without one it could only emit one value.
|
||||
func TestBuiltinDateSpansOneDay(t *testing.T) {
|
||||
f := engine(1)
|
||||
seen := map[string]bool{}
|
||||
for i := 0; i < 500; i++ {
|
||||
got := mustRender(t, f, `"{date(2026-01-01,2026-01-01,'2006-01-02 15:04:05')}"`)
|
||||
if !strings.HasPrefix(got, "2026-01-01 ") {
|
||||
t.Fatalf("date over one day = %q, out of range", got)
|
||||
}
|
||||
seen[got] = true
|
||||
}
|
||||
if len(seen) < 400 {
|
||||
t.Fatalf("date over one day drew %d distinct seconds in 500, want the whole day", len(seen))
|
||||
}
|
||||
_, err := compile(parse(t, `"{date(2026-01-01,2026-01-01,'2006-01-02')}"`))
|
||||
if err == nil || !strings.Contains(err.Error(), "write it as text") {
|
||||
t.Fatalf("one day in a date-only layout = %v, want it named a constant", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,3 +392,25 @@ func TestShippedStreetNumberFormats(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestShippedUSTitleAgreesWithSex pins that one person's title and sex agree, and
|
||||
// that the everyday titles are reachable.
|
||||
func TestShippedUSTitleAgreesWithSex(t *testing.T) {
|
||||
f := newGenerator(t, "data", WithSeed(4))
|
||||
female := map[string]bool{"Miss": true, "Mrs": true, "Ms": true}
|
||||
seen := map[string]bool{}
|
||||
for i := 0; i < 3000; i++ {
|
||||
got := fakeTemplate(t, f, `{/en_US.person.prefix}|{/en_US.person.sex}`)
|
||||
title, sex, _ := strings.Cut(got, "|")
|
||||
title = strings.TrimSpace(title)
|
||||
seen[title] = true
|
||||
if title == "Mr" && sex != "male" || female[title] && sex != "female" {
|
||||
t.Fatalf("%q: the title contradicts the sex", got)
|
||||
}
|
||||
}
|
||||
for _, want := range []string{"", "Mr", "Ms"} {
|
||||
if !seen[want] {
|
||||
t.Errorf("en_US.person.prefix never drew %q in 3000 draws", want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,3 +547,18 @@ func TestRecordTemplateRejectsATopLevelRepeat(t *testing.T) {
|
||||
t.Errorf("NewTemplate on the same input = %v, want the string view to still compile", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRecordOfAFieldlessCategoryNamesTheWrapper pins the way out of the first
|
||||
// command a fixture author types: a category of one value has no columns, and the
|
||||
// error names the record that gives it one.
|
||||
func TestRecordOfAFieldlessCategoryNamesTheWrapper(t *testing.T) {
|
||||
f := newGenerator(t, "data", WithSeed(1))
|
||||
want := `'{"format":"","personnummer":"{/sv_SE.personnummer}"}'`
|
||||
_, err := f.FakeRecord("sv_SE.personnummer")
|
||||
if err == nil || !strings.Contains(err.Error(), want) {
|
||||
t.Fatalf("FakeRecord(sv_SE.personnummer) = %v, want it to name %s", err, want)
|
||||
}
|
||||
if _, err := f.FakeRecordTemplate(`{"format":"","personnummer":"{/sv_SE.personnummer}"}`); err != nil {
|
||||
t.Fatalf("the named wrapper does not render: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
+31
-2
@@ -172,7 +172,7 @@ func TestTableSelectsARowByKeyOrName(t *testing.T) {
|
||||
}
|
||||
for path, want := range map[string]string{
|
||||
"region[99]": `"99"`,
|
||||
"locality[Sandby]": "L7 L8",
|
||||
"locality[Sandby]": "L7, L8",
|
||||
"region[12].code[1]": "not a table",
|
||||
"region[]": "empty",
|
||||
"region[12": "]",
|
||||
@@ -334,7 +334,7 @@ func TestTableSelectorInAReference(t *testing.T) {
|
||||
}
|
||||
for name, c := range map[string]struct{ json, want string }{
|
||||
"unknown row": {`"{/region[99].name}"`, `"99"`},
|
||||
"ambiguous name": {`"{/locality[Sandby].name}"`, "L7 L8"},
|
||||
"ambiguous name": {`"{/locality[Sandby].name}"`, "L7, L8"},
|
||||
"not inside": {`"{/region[12].municipality[0180].name}"`, "not inside"},
|
||||
"selector on template": {`"{/x[1].a}"`, "not a table"},
|
||||
} {
|
||||
@@ -744,3 +744,32 @@ func TestNamedTableWithoutAKeyResolvesInsideItsParent(t *testing.T) {
|
||||
t.Fatalf("a name selected inside a pinned parent = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAmbiguousNameNamesARunnablePath pins what a shell user reads: each row is
|
||||
// named as the path they can type, from the root, and the rows are listed plainly.
|
||||
func TestAmbiguousNameNamesARunnablePath(t *testing.T) {
|
||||
files := map[string]string{}
|
||||
for name, body := range siblings() {
|
||||
files["se/"+name] = body
|
||||
}
|
||||
files["se/street.json"] = `{"format":"{name}","rows":"street.tsv","name":"name","parent":"locality","weight":"segments"}`
|
||||
f := newGenerator(t, writeFiles(t, files), WithSeed(1))
|
||||
_, err := f.Fake("se.street[Sandbyvägen]")
|
||||
if err == nil {
|
||||
t.Fatal("se.street[Sandbyvägen] resolved, though two rows carry that name")
|
||||
}
|
||||
for _, want := range []string{"se.locality[L7].street[Sandbyvägen]", "se.locality[L8].street[Sandbyvägen]"} {
|
||||
if !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("%v does not name %s", err, want)
|
||||
}
|
||||
if got := fake(t, f, want); got != "Sandbyvägen" {
|
||||
t.Errorf("Fake(%q) = %q, want the named path to run", want, got)
|
||||
}
|
||||
}
|
||||
if strings.Contains(err.Error(), "[se.locality") {
|
||||
t.Errorf("%v lists the rows as a Go slice; separate them with commas", err)
|
||||
}
|
||||
if _, err := f.Fake("se.locality[Sandby]"); err == nil || !strings.Contains(err.Error(), "one of L7, L8") {
|
||||
t.Fatalf("se.locality[Sandby] = %v, want its keys listed plainly", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user