Skip to content

Commit 6fc778c

Browse files
committed
Add JSONC input mode for jq checks
1 parent 4364497 commit 6fc778c

5 files changed

Lines changed: 66 additions & 2 deletions

File tree

checks/jq.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
api "github.com/bootdotdev/bootdev/client"
1010
"github.com/goccy/go-json"
1111
"github.com/itchyny/gojq"
12+
"github.com/tailscale/hujson"
1213
)
1314

1415
func prettyPrintStdoutJqTest(test api.StdoutJqTest, variables map[string]string) string {
@@ -66,9 +67,17 @@ func runStdoutJqQuery(stdout string, test api.StdoutJqTest, variables map[string
6667

6768
func parseJqInput(stdout string, inputMode string) (any, error) {
6869
mode := strings.ToLower(strings.TrimSpace(inputMode))
69-
if mode != "json" && mode != "jsonl" {
70+
if mode != "json" && mode != "jsonl" && mode != "jsonc" {
7071
mode = "json"
7172
}
73+
if mode == "jsonc" {
74+
// HuJSON requires a newline to terminate a final line comment.
75+
standardJSON, err := hujson.Standardize([]byte(stdout + "\n"))
76+
if err != nil {
77+
return nil, err
78+
}
79+
stdout = string(standardJSON)
80+
}
7281

7382
decoder := json.NewDecoder(strings.NewReader(stdout))
7483
decoder.UseNumber()

checks/jq_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,58 @@ func TestParseJqInputRejectsMultipleJSONValuesInJSONMode(t *testing.T) {
9696
}
9797
}
9898

99+
func TestRunStdoutJqQueryJSONC(t *testing.T) {
100+
tests := []struct {
101+
name string
102+
stdout string
103+
query string
104+
want []string
105+
}{
106+
{"queries normalized JSONC", `{
107+
// Users to query
108+
"users": [/* primary user */ {"name":"Boots",},],
109+
}`, `.users[].name`, []string{`"Boots"`}},
110+
{"large integer", `{"id":9007199254740993,}`, `.id`, []string{`9007199254740993`}},
111+
{"trailing line comment", `{"name":"Boots"} // comment`, `.name`, []string{`"Boots"`}},
112+
}
113+
for _, tt := range tests {
114+
t.Run(tt.name, func(t *testing.T) {
115+
got := runStdoutJqQuery(tt.stdout, api.StdoutJqTest{InputMode: "jsonc", Query: tt.query}, nil)
116+
if got.Error != "" {
117+
t.Fatalf("unexpected error: %s", got.Error)
118+
}
119+
if !reflect.DeepEqual(got.Results, tt.want) {
120+
t.Fatalf("results = %v, want %v", got.Results, tt.want)
121+
}
122+
})
123+
}
124+
}
125+
126+
func TestRunStdoutJqQueryReturnsJSONCParseError(t *testing.T) {
127+
got := runStdoutJqQuery(`{"name":"Boots"} /* unterminated`, api.StdoutJqTest{InputMode: "jsonc", Query: "."}, nil)
128+
if got.Error == "" || len(got.Results) != 0 || got.Query != "." {
129+
t.Fatalf("expected query with parse error and no results, got %#v", got)
130+
}
131+
}
132+
133+
func TestParseJqInputJSONCModeIsolation(t *testing.T) {
134+
for _, mode := range []string{"json", "jsonl"} {
135+
for _, stdout := range []string{`{/* comment */ "name":"Boots"}`, `{"name":"Boots",}`} {
136+
t.Run(mode+stdout, func(t *testing.T) {
137+
if _, err := parseJqInput(stdout, mode); err == nil {
138+
t.Fatal("expected strict input parsing to reject JSONC")
139+
}
140+
})
141+
}
142+
}
143+
t.Run("normalized input mode", func(t *testing.T) {
144+
got, err := parseJqInput(`/* comment */ true`, " JSONC \t")
145+
if err != nil || got != true {
146+
t.Fatalf("got %v, %v; want true, nil", got, err)
147+
}
148+
})
149+
}
150+
99151
func TestValFromJqPath(t *testing.T) {
100152
tests := []struct {
101153
name string

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"` // "json", "jsonl", or "jsonc"
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)