test(cli): support JSON stream decoding in golden tests to fix protojson instability#6433
test(cli): support JSON stream decoding in golden tests to fix protojson instability#6433sachin9058 wants to merge 6 commits into
Conversation
- decode newline-delimited JSON using json.Decoder - compare structured output instead of raw strings - add tests for stream, single JSON, invalid, and non-JSON cases - eliminate flakiness caused by protojson formatting
|
IMO you need to add those json testdata again to verify it passes |
…o-end coverage - decode newline-delimited JSON using json.Decoder - compare structured output instead of raw strings - retain JSONEq for single JSON objects - fallback to string comparison for non-JSON outputs - add unit tests for JSON stream parsing - add artifact list JSON golden test with fixture fixes mindersec#6430
…ON stream coverage
|
actually you should probably need to add the json stream testdata and those profile, repo testcase that was removed in this pr #6417 since they fall directly under the scope of what we're fixing here |
|
@DharunMR Yes, thank you. I actually forgot to add the golden earlier I had only included parsing tests. I’ve now added them, and CI is passing. However, the NATS test is still failing. It looks flaky because when I run it locally multiple times, it consistently passes. For now, I’ve retriggered the CI Let’s see what happens. |
|
Yes, Once the CI passes and it gets merged, I will rebase and revert the golden files that I removed there. |
|
Well CI is green now ... That JSON stream error is passing now let see what @evankanderson thinks about this fix... |
|
@DharunMR |
|
yep, i thought adding those testcase back is good but we can wait for @evankanderson |
|
@evankanderson Hey evan can u take a look on this ??? |
evankanderson
left a comment
There was a problem hiding this comment.
I think this will probably work, but I'm concerned that we're only partially hiding the underlying non-determinism, because the golden files on-disk won't necessarily have the same formatting each time they are generated.
| cmd.Println(out) | ||
| if _, err := cmd.OutOrStdout().Write([]byte(out + "\n")); err != nil { | ||
| return cli.MessageAndError("Error writing yaml to stdout", err) | ||
| } |
There was a problem hiding this comment.
This is basically the same thing as cmd.Println() except that it logs an error message if we try to redirect to something which doesn't accept writes. I'm not sure the error message in that case is a big improvement, but I don't want to block the rest of the value of this PR on that.
| if err != nil { | ||
| return cli.MessageAndError("Error getting json from proto", err) | ||
| // Emit each artifact as a separate JSON object (JSON stream) | ||
| for _, art := range artifactList.Results { |
There was a problem hiding this comment.
I think it's fine for the output to be JSON-LD; it also seems fine to define that we'll only emit a single JSON element.
In either case, it would be great to record CLI patterns somewhere -- maybe in DEVELOPMENT.md? Right now, we're not totally consistent about any of the following:
- Output columns and names
- JSON output contents
- Positional parameters and flags like
--file - Tabular output and multi-row output
It would be great to have a "design philosophy" around these that people could reference when making changes, such that the CLI starts to feel more consistently and thoughtfully designed.
I don't think it needs to happen in this PR, but it would be useful to start making some of these decisions explicit.
| // WithCLIClient is an alias for WithRPCClient kept for backwards/alternate usage | ||
| // by tests or callers expecting a "CLI"-named helper. | ||
| func WithCLIClient[T any](ctx context.Context, client T) context.Context { | ||
| return WithRPCClient[T](ctx, client) | ||
| } | ||
|
|
There was a problem hiding this comment.
I don't understand why we wouldn't just switch over to one consistent name here -- this is in internal, so nothing outside the codebase should be depending on it.
| // Try standard JSON comparison first | ||
| if json.Valid(expected) && json.Valid([]byte(actual)) { | ||
| // if it's valid json compare the objects (ignores spaces/newlines) | ||
| require.JSONEq(t, string(expected), actual, "JSON Output does not match golden file") | ||
| } else { | ||
| // if it's a table, txt, or yaml fallback to exact string matching | ||
| require.Equal(t, string(expected), actual, "Output does not match golden file") | ||
| return | ||
| } |
There was a problem hiding this comment.
I'm concerned that using require.JSONeq here or some other non-byte comparison will cause the golden files to "churn" when we run --update.
Taking the output from protojson and running it through the standard Go encoding/json module feels like it might be the better approach (and would avoid needing to add a bunch of complex test evaluation logic)
There was a problem hiding this comment.
It's less efficient, but we're talking about some in-memory operations on strings <10KB vs filesystem output, so the extra parsing is probably in the noise.
|
(Sorry, was out Friday and had a busy weekend.) Thanks to @DharunMR for taking a first look at this PR and helping get some of the details correct. |
Summary
This PR fixes unstable CLI golden tests caused by protojson formatting differences and newline-delimited JSON output.
Currently, CLI outputs can be a JSON stream (multiple JSON objects), which makes string-based comparisons and JSONEq unsuitable. This results in flaky tests across environments (local vs CI).
This change introduces a decoder-driven approach using json.Decoder to parse JSON streams and compare structured data instead of raw strings.
Key changes:
Added support for newline-delimited JSON (JSON streams) in golden file comparison
Retained JSONEq for single JSON objects (backward compatibility)
Added graceful fallback to string comparison for non-JSON outputs (YAML, tables, etc.)
Removed heuristic-based detection in favor of decoder-driven parsing
Added unit tests for:
This ensures deterministic and environment-independent test behavior.
Fixes #6430
Testing
The changes were tested using the existing CLI test suite and additional unit tests.
Steps:
Run all tests:
Verified:
All existing tests pass, and new tests validate the updated behavior.