diff --git a/checks/jq.go b/checks/jq.go index aad5f34..0688b97 100644 --- a/checks/jq.go +++ b/checks/jq.go @@ -1,6 +1,7 @@ package checks import ( + "bytes" "errors" "fmt" "io" @@ -9,6 +10,7 @@ import ( api "github.com/bootdotdev/bootdev/client" "github.com/goccy/go-json" "github.com/itchyny/gojq" + "github.com/tailscale/hujson" ) func prettyPrintStdoutJqTest(test api.StdoutJqTest, variables map[string]string) string { @@ -66,11 +68,22 @@ func runStdoutJqQuery(stdout string, test api.StdoutJqTest, variables map[string func parseJqInput(stdout string, inputMode string) (any, error) { mode := strings.ToLower(strings.TrimSpace(inputMode)) - if mode != "json" && mode != "jsonl" { - mode = "json" + if mode != "jsonc" && mode != "jsonl" { + mode = "jsonc" + } + var inputReader io.Reader + if mode == "jsonc" { + // HuJSON requires a newline to terminate a final line comment. + standardJSON, err := hujson.Standardize([]byte(stdout + "\n")) + if err != nil { + return nil, err + } + inputReader = bytes.NewReader(standardJSON) + } else { + inputReader = strings.NewReader(stdout) } - decoder := json.NewDecoder(strings.NewReader(stdout)) + decoder := json.NewDecoder(inputReader) decoder.UseNumber() if mode == "jsonl" { var values []any diff --git a/checks/jq_test.go b/checks/jq_test.go index f86667b..57e6731 100644 --- a/checks/jq_test.go +++ b/checks/jq_test.go @@ -17,8 +17,11 @@ func TestRunStdoutJqQuery(t *testing.T) { wantError bool }{ { - name: "queries json with interpolated query", - stdout: `{"users":[{"name":"Lane"},{"name":"Theo"}]}`, + name: "queries json with interpolated query", + stdout: `{ + // Users to query + "users": [/* users */ {"name":"Lane"},{"name":"Theo",},], + }`, test: api.StdoutJqTest{ InputMode: "json", Query: `.users[] | select(.name == "${name}") | .name`, @@ -29,6 +32,24 @@ func TestRunStdoutJqQuery(t *testing.T) { Results: []string{`"Theo"`}, }, }, + { + name: "default mode accepts comments and trailing commas", + stdout: `{"name": /* user */ "Boots",} // final comment without newline`, + test: api.StdoutJqTest{Query: `.name`}, + want: api.CLICommandJqOutput{ + Query: `.name`, + Results: []string{`"Boots"`}, + }, + }, + { + name: "preserves large integers", + stdout: `{"id":9007199254740993,}`, + test: api.StdoutJqTest{InputMode: "json", Query: `.id`}, + want: api.CLICommandJqOutput{ + Query: `.id`, + Results: []string{`9007199254740993`}, + }, + }, { name: "queries jsonl as array", stdout: "{\"id\":1}\n{\"id\":2}\n", @@ -43,7 +64,7 @@ func TestRunStdoutJqQuery(t *testing.T) { }, { name: "returns parse error", - stdout: `{not json}`, + stdout: `{"name":"Boots"} /* unterminated`, test: api.StdoutJqTest{ InputMode: "json", Query: `.name`, @@ -77,6 +98,9 @@ func TestRunStdoutJqQuery(t *testing.T) { if got.Error == "" { t.Fatal("expected an error") } + if len(got.Results) != 0 { + t.Fatalf("expected no results on error, got %v", got.Results) + } return } if !reflect.DeepEqual(got, tt.want) { @@ -91,9 +115,6 @@ func TestParseJqInputRejectsMultipleJSONValuesInJSONMode(t *testing.T) { if err == nil { t.Fatal("expected error for multiple JSON values in json mode") } - if err.Error() != "expected a single JSON value" { - t.Fatalf("expected single-value error, got %q", err.Error()) - } } func TestValFromJqPath(t *testing.T) { diff --git a/client/lessons.go b/client/lessons.go index c51ece0..a66b6d4 100644 --- a/client/lessons.go +++ b/client/lessons.go @@ -60,7 +60,7 @@ type CLICommandTest struct { } type StdoutJqTest struct { - InputMode string `yaml:"inputMode"` // "json" or "jsonl" + InputMode string `yaml:"inputMode"` // "jsonc" or "jsonl" Query string `yaml:"query"` ExpectedResults []JqExpectedResult `yaml:"expectedResults"` } diff --git a/go.mod b/go.mod index cdc47b7..af8caa1 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c github.com/spf13/cobra v1.10.2 github.com/spf13/viper v1.21.0 + github.com/tailscale/hujson v0.0.0-20241010212012-29efb4a0184b go.yaml.in/yaml/v3 v3.0.4 golang.org/x/mod v0.32.0 golang.org/x/term v0.39.0 diff --git a/go.sum b/go.sum index f2a46b0..e01cb80 100644 --- a/go.sum +++ b/go.sum @@ -90,6 +90,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/tailscale/hujson v0.0.0-20241010212012-29efb4a0184b h1:MNaGusDfB1qxEsl6iVb33Gbe777IKzPP5PDta0xGC8M= +github.com/tailscale/hujson v0.0.0-20241010212012-29efb4a0184b/go.mod h1:EbW0wDK/qEUYI0A5bqq0C2kF8JTQwWONmGDBbzsxxHo= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= diff --git a/version.txt b/version.txt index 2959091..4953ae0 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -v1.32.3 +v1.32.4