Test the CLI reading its template from stdin, dropping one trailing newline, trimming around JSON and refusing an argument
This commit is contained in:
+204
-142
@@ -17,14 +17,21 @@ const (
|
||||
misc = "../../data/misc"
|
||||
)
|
||||
|
||||
func runOut(args ...string) (int, string, string) {
|
||||
func runOut(stdin string, args ...string) (int, string, string) {
|
||||
var out, errb bytes.Buffer
|
||||
code := run(args, &out, &errb)
|
||||
code := run(args, strings.NewReader(stdin), &out, &errb)
|
||||
return code, out.String(), errb.String()
|
||||
}
|
||||
|
||||
// runTerminal runs as from a shell with nothing piped in.
|
||||
func runTerminal(args ...string) (int, string, string) {
|
||||
var out, errb bytes.Buffer
|
||||
code := run(args, nil, &out, &errb)
|
||||
return code, out.String(), errb.String()
|
||||
}
|
||||
|
||||
func TestRunOutputsValue(t *testing.T) {
|
||||
code, out, errb := runOut("--data-path", svSE, "person")
|
||||
code, out, errb := runOut("{/person}", "--data-path", svSE)
|
||||
if code != 0 {
|
||||
t.Fatalf("run = %d, stderr=%q", code, errb)
|
||||
}
|
||||
@@ -37,11 +44,11 @@ func TestRunOutputsValue(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunDotPath(t *testing.T) {
|
||||
code, full, _ := runOut("--seed", "7", "--data-path", svSE, "person")
|
||||
code, full, _ := runOut("{/person}", "--seed", "7", "--data-path", svSE)
|
||||
if code != 0 {
|
||||
t.Fatalf("person run = %d", code)
|
||||
}
|
||||
code, last, errb := runOut("--seed", "7", "--data-path", svSE, "person.last")
|
||||
code, last, errb := runOut("{/person.last}", "--seed", "7", "--data-path", svSE)
|
||||
if code != 0 {
|
||||
t.Fatalf("person.last run = %d, stderr=%q", code, errb)
|
||||
}
|
||||
@@ -54,13 +61,13 @@ func TestRunDotPath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunSeedSpellings(t *testing.T) {
|
||||
_, want, _ := runOut("--seed", "42", "--data-path", svSE, "address")
|
||||
_, want, _ := runOut("{/address}", "--seed", "42", "--data-path", svSE)
|
||||
for _, args := range [][]string{
|
||||
{"--seed=42", "--data-path", svSE, "address"},
|
||||
{"-s", "42", "-d", svSE, "address"},
|
||||
{"--data-path", svSE, "address", "--seed", "42"},
|
||||
{"--seed=42", "--data-path", svSE},
|
||||
{"-s", "42", "-d", svSE},
|
||||
{"--data-path", svSE, "--seed", "42"},
|
||||
} {
|
||||
code, got, errb := runOut(args...)
|
||||
code, got, errb := runOut("{/address}", args...)
|
||||
if code != 0 {
|
||||
t.Fatalf("run(%v) = %d, stderr=%q", args, code, errb)
|
||||
}
|
||||
@@ -71,13 +78,13 @@ func TestRunSeedSpellings(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunShortFlagValues(t *testing.T) {
|
||||
_, want, _ := runOut("--seed", "42", "--data-path", svSE, "address")
|
||||
_, want, _ := runOut("{/address}", "--seed", "42", "--data-path", svSE)
|
||||
for _, args := range [][]string{
|
||||
{"-s42", "-d", svSE, "address"},
|
||||
{"-s", "42", "-d" + svSE, "address"},
|
||||
{"-d", svSE, "address", "-s42"},
|
||||
{"-s42", "-d", svSE},
|
||||
{"-s", "42", "-d" + svSE},
|
||||
{"-d", svSE, "-s42"},
|
||||
} {
|
||||
code, got, errb := runOut(args...)
|
||||
code, got, errb := runOut("{/address}", args...)
|
||||
if code != 0 {
|
||||
t.Fatalf("run(%v) = %d, stderr=%q", args, code, errb)
|
||||
}
|
||||
@@ -85,7 +92,7 @@ func TestRunShortFlagValues(t *testing.T) {
|
||||
t.Errorf("run(%v) = %q, want %q", args, got, want)
|
||||
}
|
||||
}
|
||||
_, three, _ := runOut("-s", "1", "-n3", "-d", svSE, "word")
|
||||
_, three, _ := runOut("{/word}", "-s", "1", "-n3", "-d", svSE)
|
||||
if lines := strings.Split(strings.TrimRight(three, "\n"), "\n"); len(lines) != 3 {
|
||||
t.Errorf("-n3 gave %d lines: %q", len(lines), three)
|
||||
}
|
||||
@@ -93,13 +100,13 @@ func TestRunShortFlagValues(t *testing.T) {
|
||||
args []string
|
||||
want string
|
||||
}{
|
||||
{[]string{"-s=42", "-d", svSE, "address"}, "-s42"},
|
||||
{[]string{"-s=42", "-d", svSE, "address"}, "--seed=42"},
|
||||
{[]string{"-d=" + svSE, "address"}, "--data-path="},
|
||||
{[]string{"-nd", "3", "-d", svSE, "word"}, `--repeat needs an integer in 1..1048576, got "d"`},
|
||||
{[]string{"--seed=", "-d", svSE, "word"}, `--seed needs an unsigned integer, got ""`},
|
||||
{[]string{"-s=42", "-d", svSE}, "-s42"},
|
||||
{[]string{"-s=42", "-d", svSE}, "--seed=42"},
|
||||
{[]string{"-d=" + svSE}, "--data-path="},
|
||||
{[]string{"-nd", "3", "-d", svSE}, `--repeat needs an integer in 1..1048576, got "d"`},
|
||||
{[]string{"--seed=", "-d", svSE}, `--seed needs an unsigned integer, got ""`},
|
||||
} {
|
||||
code, out, errb := runOut(c.args...)
|
||||
code, out, errb := runOut("{/word}", c.args...)
|
||||
if code != 2 || out != "" {
|
||||
t.Errorf("run(%v) = %d, stdout %q, want misuse", c.args, code, out)
|
||||
}
|
||||
@@ -107,20 +114,20 @@ func TestRunShortFlagValues(t *testing.T) {
|
||||
t.Errorf("run(%v) stderr = %q, want %q", c.args, errb, c.want)
|
||||
}
|
||||
}
|
||||
code, out, _ := runOut("-hd", svSE)
|
||||
code, out, _ := runOut("", "-hd", svSE)
|
||||
if code != 0 || !strings.Contains(out, "Usage") {
|
||||
t.Errorf("-hd (bundled help) = %d, %q, want usage", code, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunDoubleDashEndsFlags(t *testing.T) {
|
||||
code, out, errb := runOut("--data-path", svSE, "--", "person")
|
||||
code, out, errb := runOut("{/person}", "--data-path", svSE, "--")
|
||||
if code != 0 || strings.TrimSpace(out) == "" {
|
||||
t.Fatalf("run = %d, out=%q, stderr=%q", code, out, errb)
|
||||
}
|
||||
code, _, errb = runOut("--data-path", svSE, "--", "--list")
|
||||
if code != 1 || !strings.Contains(errb, "--list") {
|
||||
t.Errorf("after --, --list should be a path: code %d, stderr %q", code, errb)
|
||||
code, _, errb = runOut("{/person}", "--data-path", svSE, "--", "--list")
|
||||
if code != 2 || !strings.Contains(errb, "stdin") {
|
||||
t.Errorf("after --, --list should be an argument: code %d, stderr %q", code, errb)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,14 +136,14 @@ func TestRunSingleDashLongIsRejected(t *testing.T) {
|
||||
args []string
|
||||
want string
|
||||
}{
|
||||
{[]string{"-seed", "42", "-d", svSE, "person"}, "use --seed"},
|
||||
{[]string{"-seed=42", "-d", svSE, "person"}, "use --seed"},
|
||||
{[]string{"-data-path", svSE, "person"}, "use --data-path"},
|
||||
{[]string{"-seed", "42", "-d", svSE}, "use --seed"},
|
||||
{[]string{"-seed=42", "-d", svSE}, "use --seed"},
|
||||
{[]string{"-data-path", svSE}, "use --data-path"},
|
||||
{[]string{"-list", "-d", svSE}, "use --list"},
|
||||
{[]string{"--nope", "-d", svSE, "person"}, "unknown flag --nope"},
|
||||
{[]string{"-x", "-d", svSE, "person"}, "unknown flag -x"},
|
||||
{[]string{"--nope", "-d", svSE}, "unknown flag --nope"},
|
||||
{[]string{"-x", "-d", svSE}, "unknown flag -x"},
|
||||
} {
|
||||
code, _, errb := runOut(c.args...)
|
||||
code, _, errb := runOut("{/person}", c.args...)
|
||||
if code != 2 {
|
||||
t.Errorf("run(%v) = %d, want 2", c.args, code)
|
||||
}
|
||||
@@ -151,14 +158,14 @@ func TestRunFlagValues(t *testing.T) {
|
||||
args []string
|
||||
want string
|
||||
}{
|
||||
{[]string{"--seed", "-d", svSE, "person"}, `--seed needs an unsigned integer, got "-d"`},
|
||||
{[]string{"-d", svSE, "person", "--seed"}, "--seed needs a value"},
|
||||
{[]string{"--seed", "x", "-d", svSE, "person"}, "--seed"},
|
||||
{[]string{"--seed", "-d", svSE}, `--seed needs an unsigned integer, got "-d"`},
|
||||
{[]string{"-d", svSE, "--seed"}, "--seed needs a value"},
|
||||
{[]string{"--seed", "x", "-d", svSE}, "--seed"},
|
||||
{[]string{"--list=1", "-d", svSE}, "--list takes no value"},
|
||||
{[]string{"--repeat", "0", "-d", svSE, "person"}, "--repeat"},
|
||||
{[]string{"-n", "-1", "-d", svSE, "person"}, "--repeat"},
|
||||
{[]string{"--repeat", "0", "-d", svSE}, "--repeat"},
|
||||
{[]string{"-n", "-1", "-d", svSE}, "--repeat"},
|
||||
} {
|
||||
code, _, errb := runOut(c.args...)
|
||||
code, _, errb := runOut("{/person}", c.args...)
|
||||
if code != 2 {
|
||||
t.Errorf("run(%v) = %d, want 2", c.args, code)
|
||||
}
|
||||
@@ -169,7 +176,7 @@ func TestRunFlagValues(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunRepeat(t *testing.T) {
|
||||
code, out, errb := runOut("--seed", "1", "--repeat", "3", "--data-path", svSE, "word")
|
||||
code, out, errb := runOut("{/word}", "--seed", "1", "--repeat", "3", "--data-path", svSE)
|
||||
if code != 0 {
|
||||
t.Fatalf("run = %d, stderr=%q", code, errb)
|
||||
}
|
||||
@@ -177,14 +184,14 @@ func TestRunRepeat(t *testing.T) {
|
||||
if len(lines) != 3 {
|
||||
t.Errorf("repeat=3 gave %d lines: %q", len(lines), out)
|
||||
}
|
||||
_, short, _ := runOut("--seed", "1", "-n", "3", "-d", svSE, "word")
|
||||
_, short, _ := runOut("{/word}", "--seed", "1", "-n", "3", "-d", svSE)
|
||||
if short != out {
|
||||
t.Errorf("-n 3 = %q, want the same as --repeat 3 %q", short, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunSeparator(t *testing.T) {
|
||||
code, out, errb := runOut("--repeat", "3", "--separator", ",", "--data-path", svSE, "word")
|
||||
code, out, errb := runOut("{/word}", "--repeat", "3", "--separator", ",", "--data-path", svSE)
|
||||
if code != 0 {
|
||||
t.Fatalf("run = %d, stderr=%q", code, errb)
|
||||
}
|
||||
@@ -197,7 +204,7 @@ func TestRunSeparator(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunRepeatAdvancesRNG(t *testing.T) {
|
||||
code, out, errb := runOut("--seed", "1", "--repeat", "5", "--data-path", svSE, "person")
|
||||
code, out, errb := runOut("{/person}", "--seed", "1", "--repeat", "5", "--data-path", svSE)
|
||||
if code != 0 {
|
||||
t.Fatalf("run = %d, stderr=%q", code, errb)
|
||||
}
|
||||
@@ -211,30 +218,102 @@ func TestRunRepeatAdvancesRNG(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunMisuse(t *testing.T) {
|
||||
for _, args := range [][]string{{}, {"--data-path", svSE}, {"-d", svSE, "person", "word"}, {"-d", svSE, "--list", "person"}, {"--no-shipped-data", "sv_SE.person"}} {
|
||||
code, out, errb := runOut(args...)
|
||||
for _, c := range []struct {
|
||||
stdin string
|
||||
args []string
|
||||
}{
|
||||
{"", nil},
|
||||
{"", []string{"--data-path", svSE}},
|
||||
{"\n", []string{"--data-path", svSE}},
|
||||
{"{/person}", []string{"-d", svSE, "word"}},
|
||||
{"", []string{"-d", svSE, "--list", "person"}},
|
||||
{"{/sv_SE.person}", []string{"--no-shipped-data"}},
|
||||
} {
|
||||
code, out, errb := runOut(c.stdin, c.args...)
|
||||
if code != 2 {
|
||||
t.Errorf("run(%v) = %d, want 2", args, code)
|
||||
t.Errorf("run(%q, %v) = %d, want 2", c.stdin, c.args, code)
|
||||
}
|
||||
if !strings.Contains(errb, "try 'fejkdata --help'") {
|
||||
t.Errorf("run(%v) stderr = %q, want a pointer to --help", args, errb)
|
||||
t.Errorf("run(%q, %v) stderr = %q, want a pointer to --help", c.stdin, c.args, errb)
|
||||
}
|
||||
if out != "" {
|
||||
t.Errorf("run(%v) stdout = %q, want nothing", args, out)
|
||||
t.Errorf("run(%q, %v) stdout = %q, want nothing", c.stdin, c.args, out)
|
||||
}
|
||||
}
|
||||
// A template a shell split names both quotings, since which one cures it turns
|
||||
// on whether the argument carries a double-quoted JSON.
|
||||
_, _, errb := runOut("{date(1990-01-01,2010-12-31,", "January", "2,", "2006)}")
|
||||
for _, want := range []string{`wrap the whole argument in "…"`, `or in '…'`} {
|
||||
if !strings.Contains(errb, want) {
|
||||
t.Errorf("a split template stderr = %q, want it to name %s", errb, want)
|
||||
}
|
||||
|
||||
// TestRunTakesNoArgument pins the way from the old spelling, a template or a path as
|
||||
// an argument, to the one that runs: the template on stdin.
|
||||
func TestRunTakesNoArgument(t *testing.T) {
|
||||
for _, args := range [][]string{{"sv_SE.person"}, {"{/sv_SE.person}"}, {"name:", "{/sv_SE.person.last}"}} {
|
||||
code, out, errb := runOut("", args...)
|
||||
if code != 2 || out != "" || !strings.Contains(errb, "stdin") || !strings.Contains(errb, "| fejkdata") {
|
||||
t.Errorf("run(%v) = %d, %q, %q; want misuse showing a template piped to fejkdata", args, code, out, errb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunTerminalStdinIsMisuse(t *testing.T) {
|
||||
code, out, errb := runTerminal("--seed", "1")
|
||||
if code != 2 || out != "" || !strings.Contains(errb, "| fejkdata") {
|
||||
t.Errorf("run with nothing piped = %d, %q, %q; want misuse showing a template piped to fejkdata", code, out, errb)
|
||||
}
|
||||
for _, args := range [][]string{{"--list"}, {"--help"}, {"--version"}} {
|
||||
if code, out, errb := runTerminal(args...); code != 0 || out == "" {
|
||||
t.Errorf("run(%v) with nothing piped = %d, %q, %q; want it to run, reading no stdin", args, code, out, errb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunDropsOneTrailingNewline(t *testing.T) {
|
||||
for stdin, want := range map[string]string{
|
||||
"hihi": "hihi\n",
|
||||
"hihi\n": "hihi\n",
|
||||
"hihi\n\n": "hihi\n\n",
|
||||
"a\nb\n": "a\nb\n",
|
||||
" hihi \n": " hihi \n",
|
||||
"hihi\r\n": "hihi\r\n",
|
||||
"{{x}}\n": "{x}\n",
|
||||
"x: {/sv_SE.person.last}\n\n": "",
|
||||
} {
|
||||
code, out, errb := runOut(stdin, "--seed", "1")
|
||||
if want == "" {
|
||||
if code != 0 || !strings.HasPrefix(out, "x: ") || !strings.HasSuffix(out, "\n\n") || strings.Count(out, "\n") != 2 {
|
||||
t.Errorf("run(%q) = %d, %q, %q; want the template's own newline, then the line's", stdin, code, out, errb)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if code != 0 || out != want {
|
||||
t.Errorf("run(%q) = %d, %q, %q; want %q", stdin, code, out, errb, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunTextPrintsAsWritten(t *testing.T) {
|
||||
for _, stdin := range []string{"sv_SE.person", "42", "null", "true", "[Skåne län]", "x[1]y", `say "hi"`} {
|
||||
code, out, errb := runOut(stdin)
|
||||
if code != 0 || out != stdin+"\n" {
|
||||
t.Errorf("run(%q) = %d, %q, %q; want the text as written", stdin, code, out, errb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunTrimsWhitespaceAroundJSON(t *testing.T) {
|
||||
for _, stdin := range []string{
|
||||
"{\"format\":\"{x}\",\"x\":[\"a\",\"b\"]}\n",
|
||||
" {\"format\":\"{x}\",\"x\":[\"a\",\"b\"]}\n\n",
|
||||
"\t[\"a\",\"b\"]\r\n",
|
||||
" \"a\" \n",
|
||||
} {
|
||||
code, out, errb := runOut(stdin, "--seed", "1")
|
||||
if code != 0 || (out != "a\n" && out != "b\n") {
|
||||
t.Errorf("run(%q) = %d, %q, %q; want a or b", stdin, code, out, errb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunMultipleDataPaths(t *testing.T) {
|
||||
code, out, errb := runOut("-d", enUS, "--data-path", svSE, "person")
|
||||
code, out, errb := runOut("{/person}", "-d", enUS, "--data-path", svSE)
|
||||
if code != 0 {
|
||||
t.Fatalf("run(multi-dir) = %d, stderr=%q", code, errb)
|
||||
}
|
||||
@@ -244,7 +323,7 @@ func TestRunMultipleDataPaths(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunUnknownCategoryFails(t *testing.T) {
|
||||
code, _, errb := runOut("--data-path", svSE, "nope")
|
||||
code, _, errb := runOut("{/nope}", "--data-path", svSE)
|
||||
if code != 1 {
|
||||
t.Fatalf("run = %d, want 1", code)
|
||||
}
|
||||
@@ -254,7 +333,7 @@ func TestRunUnknownCategoryFails(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunList(t *testing.T) {
|
||||
code, out, errb := runOut("--data-path", svSE, "--list")
|
||||
code, out, errb := runOut("", "--data-path", svSE, "--list")
|
||||
if code != 0 {
|
||||
t.Fatalf("run = %d, stderr=%q", code, errb)
|
||||
}
|
||||
@@ -267,7 +346,7 @@ func TestRunList(t *testing.T) {
|
||||
|
||||
func TestRunHelpOnStdout(t *testing.T) {
|
||||
for _, h := range []string{"-h", "--help"} {
|
||||
code, out, errb := runOut(h)
|
||||
code, out, errb := runOut("", h)
|
||||
if code != 0 {
|
||||
t.Errorf("%s = %d, want 0", h, code)
|
||||
}
|
||||
@@ -278,7 +357,7 @@ func TestRunHelpOnStdout(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunVersion(t *testing.T) {
|
||||
code, out, errb := runOut("--version")
|
||||
code, out, errb := runOut("", "--version")
|
||||
if code != 0 {
|
||||
t.Fatalf("--version = %d, stderr=%q", code, errb)
|
||||
}
|
||||
@@ -289,7 +368,7 @@ func TestRunVersion(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunMissingDirFails(t *testing.T) {
|
||||
code, _, errb := runOut("--data-path", "../../data/nope", "person")
|
||||
code, _, errb := runOut("{/person}", "--data-path", "../../data/nope")
|
||||
if code != 1 {
|
||||
t.Fatalf("run = %d, want 1", code)
|
||||
}
|
||||
@@ -299,34 +378,20 @@ func TestRunMissingDirFails(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunShippedDataByDefault(t *testing.T) {
|
||||
code, out, errb := runOut("--seed", "1", "sv_SE.person")
|
||||
code, out, errb := runOut("{/sv_SE.person}", "--seed", "1")
|
||||
if code != 0 || strings.TrimSpace(out) == "" {
|
||||
t.Fatalf("run = %d, out=%q, stderr=%q", code, out, errb)
|
||||
}
|
||||
code, list, _ := runOut("--list")
|
||||
code, list, _ := runOut("", "--list")
|
||||
if code != 0 || !strings.Contains(list, "en_US.person\n") || !strings.Contains(list, "misc.uuid\n") {
|
||||
t.Errorf("--list without --data-path = %d, %q", code, list)
|
||||
}
|
||||
code, _, errb = runOut("person")
|
||||
code, _, errb = runOut("{/person}")
|
||||
if code != 1 || !strings.Contains(errb, "person") {
|
||||
t.Errorf("a category outside the shipped tree: code %d, stderr %q", code, errb)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClassify(t *testing.T) {
|
||||
for arg, want := range map[string]argKind{"sv_SE.person": argPath, "name: {x}": argTemplate} {
|
||||
if got, err := classify(arg); err != nil || got != want {
|
||||
t.Errorf("classify(%q) = %v, %v; want %v", arg, got, err, want)
|
||||
}
|
||||
}
|
||||
if _, err := classify("[abc]"); err == nil || !strings.Contains(err.Error(), `"["`) || !strings.Contains(err.Error(), "JSON") {
|
||||
t.Errorf("classify([abc]) = %v; want it rejected naming the leading bracket", err)
|
||||
}
|
||||
if got, err := classify("geo.SE.municipality[St. Louis].name"); err != nil || got != argPath {
|
||||
t.Errorf("classify(a selecting path) = %v, %v; want a path", got, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunSelectsATableRow(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
for name, content := range map[string]string{
|
||||
@@ -337,95 +402,92 @@ func TestRunSelectsATableRow(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if code, out, errb := runOut("--no-shipped-data", "-d", dir, "region[12]"); code != 0 || out != "Skåne län\n" {
|
||||
t.Fatalf("region[12] = %d, %q, stderr=%q", code, out, errb)
|
||||
if code, out, errb := runOut("{/region[12]}", "--no-shipped-data", "-d", dir); code != 0 || out != "Skåne län\n" {
|
||||
t.Fatalf("{/region[12]} = %d, %q, stderr=%q", code, out, errb)
|
||||
}
|
||||
if code, out, _ := runOut("--no-shipped-data", "-d", dir, "region[Skåne län].code"); code != 0 || out != "12\n" {
|
||||
t.Fatalf("region[Skåne län].code = %d, %q", code, out)
|
||||
if code, out, _ := runOut("{/region[Skåne län].code}", "--no-shipped-data", "-d", dir); code != 0 || out != "12\n" {
|
||||
t.Fatalf("{/region[Skåne län].code} = %d, %q", code, out)
|
||||
}
|
||||
if code, out, _ := runOut("--no-shipped-data", "-d", dir, "--format", "sql", "region[12]"); code != 0 || out != `INSERT INTO "region" ("code", "name") VALUES ('12', 'Skåne län');`+"\n" {
|
||||
t.Fatalf("--format sql region[12] = %d, %q, want the table named without its selector", code, out)
|
||||
if code, out, _ := runOut("{/region[12]}", "--no-shipped-data", "-d", dir, "--format", "sql"); code != 0 || out != `INSERT INTO "region" ("code", "name") VALUES ('12', 'Skåne län');`+"\n" {
|
||||
t.Fatalf("--format sql {/region[12]} = %d, %q, want the table named without its selector", code, out)
|
||||
}
|
||||
if code, _, errb := runOut("--no-shipped-data", "-d", dir, "region[99]"); code != 1 || !strings.Contains(errb, `"99"`) {
|
||||
t.Fatalf("region[99] = %d, stderr=%q, want a runtime error naming the row", code, errb)
|
||||
if code, _, errb := runOut("{/region[99]}", "--no-shipped-data", "-d", dir); code != 1 || !strings.Contains(errb, `"99"`) {
|
||||
t.Fatalf("{/region[99]} = %d, stderr=%q, want a runtime error naming the row", code, errb)
|
||||
}
|
||||
if code, _, errb := runOut("--no-shipped-data", "-d", dir, "[Skåne län]"); code != 2 || !strings.Contains(errb, "JSON") {
|
||||
t.Fatalf("[Skåne län] = %d, stderr=%q, want misuse: a leading bracket that is no JSON names nothing", code, errb)
|
||||
}
|
||||
if code, out, _ := runOut("--seed", "1", "--format", "csv", "misc.territory[SE]"); code != 0 || !strings.HasPrefix(out, "alpha2,") || !strings.Contains(out, "\nSE,SWE,") {
|
||||
t.Fatalf("--format csv misc.territory[SE] = %d, %q", code, out)
|
||||
if code, out, _ := runOut("{/misc.territory[SE]}", "--seed", "1", "--format", "csv"); code != 0 || !strings.HasPrefix(out, "alpha2,") || !strings.Contains(out, "\nSE,SWE,") {
|
||||
t.Fatalf("--format csv {/misc.territory[SE]} = %d, %q", code, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUsageReferencesResolve(t *testing.T) {
|
||||
for _, token := range regexp.MustCompile(`\{/[^}]+\}`).FindAllString(usage, -1) {
|
||||
path := token[2 : len(token)-1]
|
||||
code, out, errb := runOut("--seed", "1", path)
|
||||
code, out, errb := runOut(token, "--seed", "1")
|
||||
if code != 0 || strings.TrimSpace(out) == "" {
|
||||
t.Errorf("usage advertises %s: run %s = %d, %q, stderr %q", token, path, code, out, errb)
|
||||
t.Errorf("usage advertises %s: run = %d, %q, stderr %q", token, code, out, errb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunShapeMisuseBeforeLoad(t *testing.T) {
|
||||
code, _, errb := runOut("--no-shipped-data", "[abc]")
|
||||
if code != 2 || !strings.Contains(errb, "[abc]") || strings.Contains(errb, "--data-path") {
|
||||
t.Fatalf("shape misuse with no data = %d, %q; want the shape error before any load", code, errb)
|
||||
func TestRunArgumentMisuseBeforeLoad(t *testing.T) {
|
||||
code, _, errb := runOut("", "--no-shipped-data", "x")
|
||||
if code != 2 || !strings.Contains(errb, "stdin") || strings.Contains(errb, "--data-path") {
|
||||
t.Fatalf("an argument with no data = %d, %q; want the argument refused before any load", code, errb)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunInlineTemplate(t *testing.T) {
|
||||
code, out, errb := runOut("--seed", "1", "name: {/sv_SE.person.last}")
|
||||
code, out, errb := runOut("name: {/sv_SE.person.last}", "--seed", "1")
|
||||
if code != 0 || !strings.HasPrefix(out, "name: ") || strings.Contains(out, "{") {
|
||||
t.Fatalf("inline format string = %d, %q, stderr %q", code, out, errb)
|
||||
}
|
||||
code, out, errb = runOut("--seed", "1", `{"format":"name: {x}","x":["bosse","lina"]}`)
|
||||
code, out, errb = runOut(`{"format":"name: {x}","x":["bosse","lina"]}`, "--seed", "1")
|
||||
if code != 0 || (out != "name: bosse\n" && out != "name: lina\n") {
|
||||
t.Fatalf("inline JSON template = %d, %q, want one name, stderr %q", code, out, errb)
|
||||
}
|
||||
code, out, errb = runOut("--seed", "1", `"name: {/sv_SE.person.last}"`)
|
||||
code, out, errb = runOut(`"name: {/sv_SE.person.last}"`, "--seed", "1")
|
||||
if code != 0 || !strings.HasPrefix(out, "name: ") || strings.Contains(out, "{") {
|
||||
t.Fatalf("inline JSON string = %d, %q, stderr %q", code, out, errb)
|
||||
}
|
||||
code, out, errb = runOut("--seed", "1", "-n", "2", `{digits(1)}`)
|
||||
code, out, errb = runOut(`{digits(1)}`, "--seed", "1", "-n", "2")
|
||||
if code != 0 || len(strings.Split(strings.TrimRight(out, "\n"), "\n")) != 2 {
|
||||
t.Fatalf("inline template with --repeat = %d, %q, stderr %q", code, out, errb)
|
||||
}
|
||||
code, out, errb = runOut("{/sv_SE.person} hihi", "--seed", "1")
|
||||
if code != 0 || !strings.HasSuffix(out, " hihi\n") || strings.Contains(out, "{") {
|
||||
t.Fatalf("a reference and text = %d, %q, stderr %q", code, out, errb)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunTemplateMisuse(t *testing.T) {
|
||||
for arg, want := range map[string]string{
|
||||
for stdin, want := range map[string]string{
|
||||
"{bad": "unterminated",
|
||||
"[red,green]": `starts with "["`,
|
||||
`{"format":"x"}`: "is a string",
|
||||
"name: {/no.such.path}": "no entry",
|
||||
"{/sv_SE.person}": "write sv_SE.person",
|
||||
"x[1]y": `"]"`,
|
||||
` ["a","b"] `: "may not be padded",
|
||||
"a } b": "}}",
|
||||
} {
|
||||
code, out, errb := runOut("--seed", "1", arg)
|
||||
code, out, errb := runOut(stdin, "--seed", "1")
|
||||
if code != 2 || out != "" || !strings.Contains(errb, "try 'fejkdata --help'") || !strings.Contains(errb, want) {
|
||||
t.Errorf("run(%q) = %d, %q, %q; want misuse naming %q and --help", arg, code, out, errb, want)
|
||||
t.Errorf("run(%q) = %d, %q, %q; want misuse naming %q and --help", stdin, code, out, errb, want)
|
||||
}
|
||||
if strings.Contains(errb, "fejkdata: fejkdata:") {
|
||||
t.Errorf("run(%q) doubled the program prefix: %q", arg, errb)
|
||||
t.Errorf("run(%q) doubled the program prefix: %q", stdin, errb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunNoShippedData(t *testing.T) {
|
||||
code, list, errb := runOut("--no-shipped-data", "-d", misc, "--list")
|
||||
code, list, errb := runOut("", "--no-shipped-data", "-d", misc, "--list")
|
||||
if code != 0 {
|
||||
t.Fatalf("run = %d, stderr=%q", code, errb)
|
||||
}
|
||||
if strings.Contains(list, "sv_SE") || !strings.Contains(list, "uuid\n") {
|
||||
t.Errorf("--no-shipped-data --list = %q, want only the given dir", list)
|
||||
}
|
||||
code, out, _ := runOut("--no-shipped-data", "-d", misc, "-s", "3", "uuid")
|
||||
code, out, _ := runOut("{/uuid}", "--no-shipped-data", "-d", misc, "-s", "3")
|
||||
if code != 0 || strings.TrimSpace(out) == "" {
|
||||
t.Errorf("run = %d, out=%q", code, out)
|
||||
}
|
||||
code, _, errb = runOut("--no-shipped-data", "sv_SE.person")
|
||||
code, _, errb = runOut("{/sv_SE.person}", "--no-shipped-data")
|
||||
if code != 2 || !strings.Contains(errb, "--no-shipped-data needs at least one --data-path") {
|
||||
t.Errorf("--no-shipped-data alone = %d, %q, want misuse naming --data-path", code, errb)
|
||||
}
|
||||
@@ -433,11 +495,11 @@ func TestRunNoShippedData(t *testing.T) {
|
||||
|
||||
func TestRunRepeatIsBounded(t *testing.T) {
|
||||
for _, args := range [][]string{
|
||||
{"--repeat", "9223372036854775807", "sv_SE.word"},
|
||||
{"--repeat", "1048577", "sv_SE.word"},
|
||||
{"-n", "99999999999", "sv_SE.word"},
|
||||
{"--repeat", "9223372036854775807"},
|
||||
{"--repeat", "1048577"},
|
||||
{"-n", "99999999999"},
|
||||
} {
|
||||
code, out, errb := runOut(args...)
|
||||
code, out, errb := runOut("{/sv_SE.word}", args...)
|
||||
if code != 2 || out != "" || !strings.Contains(errb, "1..1048576") {
|
||||
t.Errorf("run(%v) = %d, %q, %q, want misuse naming the range", args, code, out, errb)
|
||||
}
|
||||
@@ -446,7 +508,7 @@ func TestRunRepeatIsBounded(t *testing.T) {
|
||||
if err := os.WriteFile(filepath.Join(dir, "x.json"), []byte(`"x"`), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, out, errb := runOut("--no-shipped-data", "-d", dir, "--repeat", "1048576", "--separator", "", "x")
|
||||
code, out, errb := runOut("{/x}", "--no-shipped-data", "-d", dir, "--repeat", "1048576", "--separator", "")
|
||||
if code != 0 || len(out) != 1048576+1 {
|
||||
t.Errorf("repeat at the cap = %d, %d bytes, stderr %q; want every render streamed", code, len(out), errb)
|
||||
}
|
||||
@@ -454,7 +516,7 @@ func TestRunRepeatIsBounded(t *testing.T) {
|
||||
|
||||
func TestRunListTakesNoRepeatOrSeparator(t *testing.T) {
|
||||
for _, args := range [][]string{{"--list", "-n", "3"}, {"--list", "--separator", ","}} {
|
||||
code, _, errb := runOut(args...)
|
||||
code, _, errb := runOut("", args...)
|
||||
if code != 2 || !strings.Contains(errb, "--list takes no") {
|
||||
t.Errorf("run(%v) = %d, %q, want misuse", args, code, errb)
|
||||
}
|
||||
@@ -462,14 +524,14 @@ func TestRunListTakesNoRepeatOrSeparator(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunUnknownFlagIsNamedByRune(t *testing.T) {
|
||||
code, _, errb := runOut("-ä", "sv_SE.word")
|
||||
code, _, errb := runOut("{/sv_SE.word}", "-ä")
|
||||
if code != 2 || !strings.Contains(errb, "unknown flag -ä") {
|
||||
t.Errorf("run(-ä) = %d, %q, want the flag named whole", code, errb)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunEmptyDataPathIsNamed(t *testing.T) {
|
||||
code, _, errb := runOut("--data-path=", "sv_SE.word")
|
||||
code, _, errb := runOut("{/sv_SE.word}", "--data-path=")
|
||||
if code != 1 || !strings.Contains(errb, "empty") {
|
||||
t.Errorf("run(--data-path=) = %d, %q, want the empty path named", code, errb)
|
||||
}
|
||||
@@ -486,7 +548,7 @@ func recordDir(t *testing.T) string {
|
||||
}
|
||||
|
||||
func TestRunRecordJSON(t *testing.T) {
|
||||
code, out, errb := runOut("--seed", "1", "--format", "json", "--repeat", "2", "--data-path", recordDir(t), "users")
|
||||
code, out, errb := runOut("{/users}", "--seed", "1", "--format", "json", "--repeat", "2", "--data-path", recordDir(t))
|
||||
if code != 0 {
|
||||
t.Fatalf("run = %d, stderr=%q", code, errb)
|
||||
}
|
||||
@@ -500,7 +562,7 @@ func TestRunRecordJSON(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunRecordNDJSON(t *testing.T) {
|
||||
code, out, errb := runOut("--seed", "1", "--format", "ndjson", "--repeat", "2", "--data-path", recordDir(t), "users")
|
||||
code, out, errb := runOut("{/users}", "--seed", "1", "--format", "ndjson", "--repeat", "2", "--data-path", recordDir(t))
|
||||
if code != 0 {
|
||||
t.Fatalf("run = %d, stderr=%q", code, errb)
|
||||
}
|
||||
@@ -517,7 +579,7 @@ func TestRunRecordNDJSON(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunRecordCSVRoundTrips(t *testing.T) {
|
||||
code, out, errb := runOut("--seed", "1", "--format", "csv", "--repeat", "3", "--data-path", recordDir(t), "users")
|
||||
code, out, errb := runOut("{/users}\n", "--seed", "1", "--format", "csv", "--repeat", "3", "--data-path", recordDir(t))
|
||||
if code != 0 {
|
||||
t.Fatalf("run = %d, stderr=%q", code, errb)
|
||||
}
|
||||
@@ -538,7 +600,7 @@ func TestRunRecordCSVRoundTrips(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunRecordSQL(t *testing.T) {
|
||||
code, out, errb := runOut("--seed", "1", "--format", "sql", "--repeat", "2", "--table", "people", "--data-path", recordDir(t), "users")
|
||||
code, out, errb := runOut("{/users}", "--seed", "1", "--format", "sql", "--repeat", "2", "--table", "people", "--data-path", recordDir(t))
|
||||
if code != 0 {
|
||||
t.Fatalf("run = %d, stderr=%q", code, errb)
|
||||
}
|
||||
@@ -549,14 +611,14 @@ func TestRunRecordSQL(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunRecordDefaultTable(t *testing.T) {
|
||||
code, out, errb := runOut("--seed", "1", "--format", "sql", "--data-path", recordDir(t), "users")
|
||||
code, out, errb := runOut("{/users}", "--seed", "1", "--format", "sql", "--data-path", recordDir(t))
|
||||
if code != 0 || !strings.HasPrefix(out, `INSERT INTO "users" (`) {
|
||||
t.Fatalf("default table = %d, %q, want the path's last segment; stderr %q", code, out, errb)
|
||||
t.Fatalf("default table = %d, %q, want the reference's last segment; stderr %q", code, out, errb)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunRecordInlineTemplate(t *testing.T) {
|
||||
code, out, errb := runOut("--seed", "1", "--format", "json", `{"format":"{x}","x":["a","b"]}`)
|
||||
code, out, errb := runOut(`{"format":"{x}","x":["a","b"]}`, "--seed", "1", "--format", "json")
|
||||
if code != 0 {
|
||||
t.Fatalf("inline record = %d, stderr=%q", code, errb)
|
||||
}
|
||||
@@ -571,13 +633,13 @@ func TestRunRecordMisuse(t *testing.T) {
|
||||
args []string
|
||||
want string
|
||||
}{
|
||||
{[]string{"--format", "yaml", "users"}, "--format takes csv, json, ndjson, sql or text"},
|
||||
{[]string{"--format", "json", "--separator", ",", "users"}, "--separator joins text values"},
|
||||
{[]string{"--table", "t", "users"}, "--table names the INSERT target"},
|
||||
{[]string{"--format", "json", "--table", "t", "users"}, "--table names the INSERT target"},
|
||||
{[]string{"--format", "sql", "--table", "", "users"}, "--table names the INSERT target"},
|
||||
{[]string{"--format", "yaml"}, "--format takes csv, json, ndjson, sql or text"},
|
||||
{[]string{"--format", "json", "--separator", ","}, "--separator joins text values"},
|
||||
{[]string{"--table", "t"}, "--table names the INSERT target"},
|
||||
{[]string{"--format", "json", "--table", "t"}, "--table names the INSERT target"},
|
||||
{[]string{"--format", "sql", "--table", ""}, "--table names the INSERT target"},
|
||||
} {
|
||||
code, out, errb := runOut(c.args...)
|
||||
code, out, errb := runOut("{/users}", c.args...)
|
||||
if code != 2 || out != "" || !strings.Contains(errb, c.want) {
|
||||
t.Errorf("run(%v) = %d, %q, %q; want misuse naming %q", c.args, code, out, errb, c.want)
|
||||
}
|
||||
@@ -589,7 +651,7 @@ func TestRunRecordOnABareValueIsRuntimeError(t *testing.T) {
|
||||
if err := os.WriteFile(filepath.Join(dir, "word.json"), []byte(`["a","b"]`), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, _, errb := runOut("--format", "json", "--data-path", dir, "word")
|
||||
code, _, errb := runOut("{/word}", "--format", "json", "--data-path", dir)
|
||||
if code != 1 {
|
||||
t.Fatalf("record on a choice = %d, want exit 1 (runtime error), stderr %q", code, errb)
|
||||
}
|
||||
@@ -601,22 +663,22 @@ func TestRunRecordRejectsATopLevelRepeat(t *testing.T) {
|
||||
if err := os.WriteFile(filepath.Join(dir, "rep.json"), []byte(content), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, _, errb := runOut("--format", "json", "--data-path", dir, "rep")
|
||||
code, _, errb := runOut("{/rep}", "--format", "json", "--data-path", dir)
|
||||
if code != 1 || !strings.Contains(errb, "carries repeat 3") {
|
||||
t.Errorf("record on a repeating path = %d, %q; want exit 1 naming the repeat", code, errb)
|
||||
}
|
||||
code, _, errb = runOut("--format", "json", content)
|
||||
code, _, errb = runOut(content, "--format", "json")
|
||||
if code != 2 || !strings.Contains(errb, "carries repeat 3") {
|
||||
t.Errorf("record on a repeating inline template = %d, %q; want exit 2 naming the repeat", code, errb)
|
||||
}
|
||||
if code, out, _ := runOut("--seed", "1", "--data-path", dir, "rep"); code != 0 || strings.TrimSpace(out) != "x-|x-|x-" {
|
||||
if code, out, _ := runOut("{/rep}", "--seed", "1", "--data-path", dir); code != 0 || strings.TrimSpace(out) != "x-|x-|x-" {
|
||||
t.Errorf("text view = %d, %q; want the repeat still composed", code, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunNumbersFollowTheShell(t *testing.T) {
|
||||
_, want, _ := runOut("--seed", "7", "--repeat", "3", "sv_SE.word")
|
||||
code, got, errb := runOut("--seed", "007", "--repeat", "+3", "sv_SE.word")
|
||||
_, want, _ := runOut("{/sv_SE.word}", "--seed", "7", "--repeat", "3")
|
||||
code, got, errb := runOut("{/sv_SE.word}", "--seed", "007", "--repeat", "+3")
|
||||
if code != 0 || got != want {
|
||||
t.Errorf("run(--seed 007 --repeat +3) = %d, %q, stderr %q; want the same as --seed 7 --repeat 3 %q", code, got, errb, want)
|
||||
}
|
||||
@@ -624,11 +686,11 @@ func TestRunNumbersFollowTheShell(t *testing.T) {
|
||||
|
||||
// TestRunRecordOfAFieldlessPath pins the way out of asking a category of one value
|
||||
// for columns: the record to write, and the quoting a shell needs to take it. The
|
||||
// argument is well-formed and only the data cannot serve it, so the code is 1.
|
||||
// template is well-formed and only the data cannot serve it, so the code is 1.
|
||||
func TestRunRecordOfAFieldlessPath(t *testing.T) {
|
||||
code, out, errb := runOut("--format", "csv", "sv_SE.personnummer")
|
||||
code, out, errb := runOut("{/sv_SE.personnummer}", "--format", "csv")
|
||||
if code != 1 {
|
||||
t.Fatalf("run(--format csv sv_SE.personnummer) = %d, want 1", code)
|
||||
t.Fatalf("run(--format csv) of {/sv_SE.personnummer} = %d, want 1", code)
|
||||
}
|
||||
for _, want := range []string{`{"format":"","personnummer":"{/sv_SE.personnummer}"}`, "single quotes"} {
|
||||
if !strings.Contains(errb, want) {
|
||||
|
||||
Reference in New Issue
Block a user