diff --git a/cli/cli_test.go b/cli/cli_test.go index 7cad4d4..a926107 100644 --- a/cli/cli_test.go +++ b/cli/cli_test.go @@ -563,13 +563,24 @@ func TestRunSelectsATableRow(t *testing.T) { } } -func TestUsageReferencesResolve(t *testing.T) { - for _, token := range regexp.MustCompile(`\{/[^}]+\}`).FindAllString(usage, -1) { - code, out, errb := runOut(token, "--seed", "1") - if code != 0 || strings.TrimSpace(out) == "" { - t.Errorf("usage advertises %s: run = %d, %q, stderr %q", token, code, out, errb) +func TestUsageExamplesRenderWithNoData(t *testing.T) { + examples := regexp.MustCompile(`(?m)^ echo '([^']*)' \| COMMAND$`).FindAllStringSubmatch(usage+"\n "+pipeHint("COMMAND")+"\n", -1) + if len(examples) < 3 { + t.Fatalf("usage and pipe hint hold %d echo examples, want the help's two and the hint's", len(examples)) + } + for _, e := range examples { + if code, out, errb := runOut(e[1], "--no-shipped-data"); code != 0 || strings.TrimSpace(out) == "" { + t.Errorf("echo %q with no data: run = %d, %q, stderr %q", e[1], code, out, errb) } } + for _, token := range regexp.MustCompile(`\{/[^}]*\}`).FindAllString(usage, -1) { + if token != "{/path}" && token != "{/table[KEY]}" { + t.Errorf("usage names %s, a path a custom CLI may not carry", token) + } + } + if !strings.Contains(usage, "\nExit codes: 0 success, 1 runtime error, 2 misuse.\n") { + t.Errorf("usage does not list the exit codes") + } } func TestRunOperandMisuseBeforeLoad(t *testing.T) { diff --git a/internal/datafiles/datafiles_test.go b/internal/datafiles/datafiles_test.go index 4f3db25..a11cff2 100644 --- a/internal/datafiles/datafiles_test.go +++ b/internal/datafiles/datafiles_test.go @@ -100,7 +100,7 @@ func TestWalkLabelsWhatACategoryFileGetsWrong(t *testing.T) { want string }{ {"its compile", fstest.MapFS{"a.json": {Data: []byte(`"x"`)}}, errors.New("bad"), "lbl/a.json: bad"}, - {"its JSON", fstest.MapFS{"a.json": {Data: []byte(`{`)}}, nil, "lbl/a.json: unexpected end of JSON input"}, + {"its JSON", fstest.MapFS{"a.json": {Data: []byte(`{`)}}, nil, "lbl/a.json: line 1, column 1: unexpected end of JSON input"}, {"its name", fstest.MapFS{"a.b.json": {Data: []byte(`"x"`)}}, nil, "lbl/a.b.json: category "}, {"its folder's name", fstest.MapFS{"a.b/c.json": {Data: []byte(`"x"`)}}, nil, "lbl/a.b: folder "}, } { @@ -121,11 +121,11 @@ func TestWalkRefusesADiskPathThatIsNoDirectory(t *testing.T) { dir := t.TempDir() for _, c := range []struct{ path, want string }{ {"", "a data path is empty"}, - {filepath.Join(dir, "none"), "data path " + filepath.Join(dir, "none") + ": "}, + {filepath.Join(dir, "none"), "data path " + filepath.Join(dir, "none") + ": no such directory"}, } { _, err := walk(t, Dir(c.path)) - if err == nil || !strings.HasPrefix(err.Error(), c.want) { - t.Errorf("Walk(%q) = %v, want it to start %q", c.path, err, c.want) + if err == nil || err.Error() != c.want { + t.Errorf("Walk(%q) = %v, want %q", c.path, err, c.want) } } } @@ -158,7 +158,7 @@ func TestDirReadsTheDirectoryAndLabelsItsErrorsWithIt(t *testing.T) { if err == nil || !strings.HasPrefix(err.Error(), filepath.ToSlash(filepath.Join(dir, "a.json"))+": ") { t.Errorf("Walk = %v, want the error labelled with the directory", err) } - if _, err := walk(t, Dir(filepath.Join(dir, "a.json"))); err == nil || err.Error() != filepath.Join(dir, "a.json")+" is not a directory" { + if _, err := walk(t, Dir(filepath.Join(dir, "a.json"))); err == nil || err.Error() != "data path "+filepath.Join(dir, "a.json")+": not a directory" { t.Errorf("Walk = %v, want a file refused as no directory", err) } } diff --git a/internal/jsonvalue/jsonvalue_test.go b/internal/jsonvalue/jsonvalue_test.go new file mode 100644 index 0000000..b30851e --- /dev/null +++ b/internal/jsonvalue/jsonvalue_test.go @@ -0,0 +1,15 @@ +package jsonvalue + +import "testing" + +func TestDecodeNamesTheLineAndColumnOfASyntaxError(t *testing.T) { + for in, want := range map[string]string{ + "{\"a\": 1,}": "line 1, column 9: invalid character '}' looking for beginning of object key string", + "{\n \"a\": 1\n \"b\": 2\n}": "line 3, column 3: invalid character '\"' after object key:value pair", + "": "line 1, column 1: unexpected end of JSON input", + } { + if _, err := Decode([]byte(in)); err == nil || err.Error() != want { + t.Errorf("Decode(%q) = %v, want %q", in, err, want) + } + } +} diff --git a/loading_test.go b/loading_test.go index ab6445c..e7fddb8 100644 --- a/loading_test.go +++ b/loading_test.go @@ -205,7 +205,7 @@ func TestNewErrors(t *testing.T) { if err := os.WriteFile(file, []byte("{}"), 0o644); err != nil { t.Fatal(err) } - if _, err := New(WithDataPath(file)); err == nil || !strings.Contains(err.Error(), "xx_XX is not a directory") { + if _, err := New(WithDataPath(file)); err == nil || !strings.Contains(err.Error(), "xx_XX: not a directory") { t.Errorf("New(file) = %v, want not-a-directory error", err) } // Invalid JSON in a category file fails. diff --git a/record_test.go b/record_test.go index 768850b..2dea0d6 100644 --- a/record_test.go +++ b/record_test.go @@ -74,7 +74,7 @@ func TestRecordErrors(t *testing.T) { want string }{ {"bare", "has no fields, so no columns"}, - {"pick", "names a choice"}, + {"pick", `names a choice, not a template; a record is a template whose fields are its columns; render it as a column of one: {"format":"","pick":"{/pick}"}`}, {"group", "names a folder"}, {"nope", "no entry"}, } { diff --git a/shipped_data_test.go b/shipped_data_test.go index 8a657fd..dfd89e7 100644 --- a/shipped_data_test.go +++ b/shipped_data_test.go @@ -89,6 +89,12 @@ func TestAGeneratorWithNoDataRendersWhatReadsNone(t *testing.T) { if _, err := f.FakeRecord("x"); err == nil || !strings.Contains(err.Error(), "no data is loaded") { t.Errorf("%s: FakeRecord(x) = %v, want a reference to nothing refused, saying no data is loaded", name, err) } + var tagged struct { + X string `fake:"x"` + } + if err := f.FakeStruct(&tagged); err == nil || !strings.Contains(err.Error(), "no data is loaded") { + t.Errorf("%s: FakeStruct(tag x) = %v, want a reference to nothing refused, saying no data is loaded", name, err) + } } }