Skip to content

Commit 8aaa66f

Browse files
committed
Allow comments and trailing commas in JSON checks
1 parent 4364497 commit 8aaa66f

5 files changed

Lines changed: 47 additions & 10 deletions

File tree

checks/jq.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package checks
22

33
import (
4+
"bytes"
45
"errors"
56
"fmt"
67
"io"
@@ -9,6 +10,7 @@ import (
910
api "github.com/bootdotdev/bootdev/client"
1011
"github.com/goccy/go-json"
1112
"github.com/itchyny/gojq"
13+
"github.com/tailscale/hujson"
1214
)
1315

1416
func prettyPrintStdoutJqTest(test api.StdoutJqTest, variables map[string]string) string {
@@ -66,11 +68,22 @@ func runStdoutJqQuery(stdout string, test api.StdoutJqTest, variables map[string
6668

6769
func parseJqInput(stdout string, inputMode string) (any, error) {
6870
mode := strings.ToLower(strings.TrimSpace(inputMode))
69-
if mode != "json" && mode != "jsonl" {
70-
mode = "json"
71+
if mode != "jsonc" && mode != "jsonl" {
72+
mode = "jsonc"
73+
}
74+
var inputReader io.Reader
75+
if mode == "jsonc" {
76+
// HuJSON requires a newline to terminate a final line comment.
77+
standardJSON, err := hujson.Standardize([]byte(stdout + "\n"))
78+
if err != nil {
79+
return nil, err
80+
}
81+
inputReader = bytes.NewReader(standardJSON)
82+
} else {
83+
inputReader = strings.NewReader(stdout)
7184
}
7285

73-
decoder := json.NewDecoder(strings.NewReader(stdout))
86+
decoder := json.NewDecoder(inputReader)
7487
decoder.UseNumber()
7588
if mode == "jsonl" {
7689
var values []any

checks/jq_test.go

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ func TestRunStdoutJqQuery(t *testing.T) {
1717
wantError bool
1818
}{
1919
{
20-
name: "queries json with interpolated query",
21-
stdout: `{"users":[{"name":"Lane"},{"name":"Theo"}]}`,
20+
name: "queries json with interpolated query",
21+
stdout: `{
22+
// Users to query
23+
"users": [/* users */ {"name":"Lane"},{"name":"Theo",},],
24+
}`,
2225
test: api.StdoutJqTest{
2326
InputMode: "json",
2427
Query: `.users[] | select(.name == "${name}") | .name`,
@@ -29,6 +32,24 @@ func TestRunStdoutJqQuery(t *testing.T) {
2932
Results: []string{`"Theo"`},
3033
},
3134
},
35+
{
36+
name: "default mode accepts comments and trailing commas",
37+
stdout: `{"name": /* user */ "Boots",} // final comment without newline`,
38+
test: api.StdoutJqTest{Query: `.name`},
39+
want: api.CLICommandJqOutput{
40+
Query: `.name`,
41+
Results: []string{`"Boots"`},
42+
},
43+
},
44+
{
45+
name: "preserves large integers",
46+
stdout: `{"id":9007199254740993,}`,
47+
test: api.StdoutJqTest{InputMode: "json", Query: `.id`},
48+
want: api.CLICommandJqOutput{
49+
Query: `.id`,
50+
Results: []string{`9007199254740993`},
51+
},
52+
},
3253
{
3354
name: "queries jsonl as array",
3455
stdout: "{\"id\":1}\n{\"id\":2}\n",
@@ -43,7 +64,7 @@ func TestRunStdoutJqQuery(t *testing.T) {
4364
},
4465
{
4566
name: "returns parse error",
46-
stdout: `{not json}`,
67+
stdout: `{"name":"Boots"} /* unterminated`,
4768
test: api.StdoutJqTest{
4869
InputMode: "json",
4970
Query: `.name`,
@@ -77,6 +98,9 @@ func TestRunStdoutJqQuery(t *testing.T) {
7798
if got.Error == "" {
7899
t.Fatal("expected an error")
79100
}
101+
if len(got.Results) != 0 {
102+
t.Fatalf("expected no results on error, got %v", got.Results)
103+
}
80104
return
81105
}
82106
if !reflect.DeepEqual(got, tt.want) {
@@ -91,9 +115,6 @@ func TestParseJqInputRejectsMultipleJSONValuesInJSONMode(t *testing.T) {
91115
if err == nil {
92116
t.Fatal("expected error for multiple JSON values in json mode")
93117
}
94-
if err.Error() != "expected a single JSON value" {
95-
t.Fatalf("expected single-value error, got %q", err.Error())
96-
}
97118
}
98119

99120
func TestValFromJqPath(t *testing.T) {

client/lessons.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ type CLICommandTest struct {
6060
}
6161

6262
type StdoutJqTest struct {
63-
InputMode string `yaml:"inputMode"` // "json" or "jsonl"
63+
InputMode string `yaml:"inputMode"` // "jsonc" or "jsonl"
6464
Query string `yaml:"query"`
6565
ExpectedResults []JqExpectedResult `yaml:"expectedResults"`
6666
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ require (
1212
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
1313
github.com/spf13/cobra v1.10.2
1414
github.com/spf13/viper v1.21.0
15+
github.com/tailscale/hujson v0.0.0-20241010212012-29efb4a0184b
1516
go.yaml.in/yaml/v3 v3.0.4
1617
golang.org/x/mod v0.32.0
1718
golang.org/x/term v0.39.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
9090
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
9191
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
9292
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
93+
github.com/tailscale/hujson v0.0.0-20241010212012-29efb4a0184b h1:MNaGusDfB1qxEsl6iVb33Gbe777IKzPP5PDta0xGC8M=
94+
github.com/tailscale/hujson v0.0.0-20241010212012-29efb4a0184b/go.mod h1:EbW0wDK/qEUYI0A5bqq0C2kF8JTQwWONmGDBbzsxxHo=
9395
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
9496
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
9597
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=

0 commit comments

Comments
 (0)