Skip to content

feat: add --json flag to list command#1

Closed
glitch418x wants to merge 4 commits intomainfrom
feat/list-json-output
Closed

feat: add --json flag to list command#1
glitch418x wants to merge 4 commits intomainfrom
feat/list-json-output

Conversation

@glitch418x
Copy link
Contributor

Summary

Adds a --json flag to the list subcommand for machine-readable output.

Changes

  • Add serde + serde_json dependencies
  • Derive Serialize on TccEntry and sub-types
  • Add --json bool flag to List command — outputs a JSON array when set, existing table output unchanged
  • 2 new integration tests (valid JSON array + client filter works with JSON output)

Usage

# Human-readable (unchanged)
tccutil-rs list

# Machine-readable
tccutil-rs list --json
tccutil-rs list --json --client Accessibility

Checklist

  • cargo fmt clean
  • cargo clippy -- -D warnings clean
  • 65 tests passing

Adds serde/serde_json dependencies and a --json flag to the list
subcommand. When passed, entries serialize as a JSON array instead
of the human-readable table. Includes integration tests for valid
JSON output and filtered JSON output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@glitch418x glitch418x self-assigned this Feb 18, 2026
@glitch418x glitch418x requested a review from altaywtf February 18, 2026 06:31
Cargo.toml Outdated
serde_json = "1"

[dev-dependencies]
serde_json = "1"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Redundant serde_json in [dev-dependencies]

serde_json is already declared as a regular [dependencies] entry (line 20), which is available to tests automatically. Listing it again under [dev-dependencies] is harmless but unnecessary — Cargo will simply unify the two entries. The duplicate line can be removed.

Suggested change
serde_json = "1"

match db.list(client.as_deref(), service.as_deref()) {
Ok(entries) => print_entries(&entries, compact),
Ok(entries) => {
if json {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: --compact is silently ignored when --json is used

When a user passes both --json and --compact, the compact flag is destructured but never consulted in the JSON branch. The user gets no feedback that --compact had no effect. Consider either:

  • Emitting a warning to stderr (e.g. eprintln!("Warning: --compact has no effect with --json")), or
  • Making the two flags mutually exclusive via a clap conflicts_with attribute on the --compact arg.

@kilo-code-bot
Copy link

kilo-code-bot bot commented Feb 18, 2026

Code Review Summary

Status: 5 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 4
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
Cargo.toml N/A Redundant serde_json in [dev-dependencies] (pre-existing; now resolved — serde_json is correctly in [dependencies] only)
tests/integration.rs 245 Vacuous test — filter correctness is never verified when DB is empty
tests/integration.rs 152 Field-presence assertions are vacuously skipped when array is empty
tests/integration.rs N/A Tautological schema check — the mock entry is constructed with the exact fields listed in EXPECTED_JSON_FIELDS

SUGGESTION

File Line Issue
src/main.rs 429 --compact is destructured but silently ignored when --json is set (mitigated by conflicts_with = "json" on the clap arg)
Other Observations (not in diff)

No additional issues found in unchanged code.

Files Reviewed (5 files)
  • Cargo.toml — 1 issue (pre-existing, now resolved)
  • src/lib.rs — 0 issues
  • src/main.rs — 1 issue
  • src/tcc.rs — 0 issues
  • tests/integration.rs — 3 issues

Fix these issues in Kilo Cloud

…ith json, test robustness

- Remove redundant serde_json from [dev-dependencies] (already in [dependencies])
- Add conflicts_with("json") to --compact arg so clap errors on mutual use
- Restructure list_json_outputs_valid_json_array to assert unconditionally
- Add list_json_service_filter_returns_valid_structure test
- Add list_compact_and_json_conflict integration + unit tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
);

let parsed: Value = serde_json::from_str(&stdout).expect("output should be valid JSON");
let arr = parsed.as_array().expect("should be an array");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Vacuous test — filter correctness is never verified when the DB is empty

If no TCC entries match --client apple (e.g. in a CI environment without a real TCC database), arr will be an empty slice and the for entry in arr loop at line 215 never executes. The assertion inside the loop is therefore never evaluated, so the test passes without actually verifying that the client filter works correctly.

Consider adding an explicit check:

// Ensure the test is meaningful only when entries exist
if !arr.is_empty() {
    for entry in arr {
        let client = entry["client"].as_str().expect("client should be a string");
        assert!(
            client.to_lowercase().contains("apple"),
            "filtered entry should contain 'apple', got: {}",
            client
        );
    }
}
// Or document the vacuous-pass behaviour explicitly:
// assert!(arr.is_empty() || arr.iter().all(|e| ...));

Alternatively, use a fixture/mock DB so the test always has data to validate against.

assert!(success, "tccutil-rs --user list --json should exit 0");

// Always assert: output is valid JSON and is an array
let parsed: Value = serde_json::from_str(&stdout).expect("output should be valid JSON");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Field-presence assertions are vacuously skipped when the DB is empty

If the user TCC database has no entries (common in CI), arr is empty and the for (i, entry) in arr.iter().enumerate() loop never runs. The EXPECTED_JSON_FIELDS check is therefore never exercised, so the test only verifies that the output is a valid JSON array — not that each entry contains the required fields.

Consider adding a guard or using a fixture DB to ensure at least one entry is present when the field-structure assertions need to run.

Both list_json_outputs_valid_json_array and
list_json_with_client_filter_only_contains_matching_entries had
field/filter assertions inside for-loops over DB entries. When the
TCC DB is empty (always the case in CI), these loops execute zero
iterations, giving false confidence.

Fix:
- Fields test: add unconditional mock-entry field check that verifies
  the EXPECTED_JSON_FIELDS list matches the TccEntry serialization
  contract, independent of DB contents.
- Filter test: add unconditional assertion that filtering by a
  guaranteed-nonexistent client produces a valid empty JSON array,
  exercising the filter code path even on empty DBs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
// Unconditional field check: verify the serialization contract by
// round-tripping a representative JSON object through the expected schema.
// This guarantees field coverage even when the TCC DB is empty (CI).
let mock_entry = serde_json::json!({
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Tautological schema check — the mock entry is constructed from the same literal fields being tested

The mock_entry is a serde_json::json!({...}) literal that is hand-written with exactly the fields in EXPECTED_JSON_FIELDS. Checking that those fields exist in the literal you just wrote is always true and provides zero coverage of TccEntry's actual serialization output.

To actually verify the serialization contract, serialize a real TccEntry value and assert the fields on the result:

// In a #[cfg(test)] block in src/tcc.rs or via a test helper:
let entry = TccEntry {
    service_raw: "kTCCServiceCamera".into(),
    service_display: "Camera".into(),
    client: "com.example.app".into(),
    auth_value: 2,
    last_modified: "2024-01-01 00:00:00".into(),
    is_system: false,
};
let v: serde_json::Value = serde_json::to_value(&entry).unwrap();
for field in EXPECTED_JSON_FIELDS {
    assert!(v.get(field).is_some(), "TccEntry missing field '{}'", field);
}

This would catch renames or removals of fields in TccEntry that the current mock check cannot detect.

…pect panic

- Replace tautological mock JSON schema test with actual TccEntry
  serialization check (catches field additions/removals/renames)
- Add lib.rs to expose TccEntry to integration tests
- Strengthen filter test with explicit object/field assertions
- Replace .expect() panic on serialization failure with stderr + exit(1)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@glitch418x
Copy link
Contributor Author

Closing — superseded by PR #3 (CodeRabbit review branch).

@glitch418x glitch418x closed this Feb 18, 2026
@glitch418x glitch418x deleted the feat/list-json-output branch February 20, 2026 18:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant