sqry-daemon 9.0.23

sqry daemon (sqryd) — persistent code-graph service
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
//! Task 9 U12 — `lifecycle_install` integration tests.
//!
//! Exercises the three platform-native service-unit generators via the
//! production `sqryd` binary CLI subcommands:
//!
//! - [`install_systemd_user_output_parses_as_valid_unit_file`] — Linux only;
//!   pipes the output through `systemd-analyze verify` when available, or
//!   performs a structural INI-content check if the tool is absent.
//!
//! - [`install_launchd_output_parses_as_valid_plist_xml`] — macOS only (skipped
//!   on Linux/Windows with an explicit reason); pipes through `plutil -lint`
//!   when on macOS.
//!
//! - [`install_windows_emits_both_variants`] — Windows only (skipped with a
//!   reason on all other platforms).
//!
//! # Binary location
//!
//! The `sqryd` binary is located in two steps:
//!
//! 1. `CARGO_BIN_EXE_sqryd` — set by Cargo's test harness when running
//!    integration tests from a workspace with `cargo test --workspace`.
//! 2. Walk from [`std::env::current_exe()`] up through
//!    `target/<profile>/deps/` → `target/<profile>/` and look for the
//!    `sqryd[.exe]` binary.
//!
//! Tests that cannot find the binary print a reason-annotated SKIP message and
//! return rather than failing — this preserves green CI when the binary has not
//! been built explicitly (e.g. `cargo test -p sqry-daemon` without `--bin`
//! targets) and avoids spurious failures in crates that run only a subset of
//! the workspace.
//!
//! # Platform gating
//!
//! The test file itself is unconditionally compiled.  Each individual test
//! uses `#[cfg_attr(not(target_os = "linux"), ignore = "...")]` or equivalent
//! runtime skip guards so that the test count visible to Cargo is stable
//! across platforms while the tests only exercise the generators that exist on
//! the current host.
//!
//! # Design reference
//!
//! `docs/development/sqryd-daemon/TASK_9_IMPLEMENTATION_DAG.md` §U12.
//! `docs/reviews/sqryd-daemon/2026-04-19/task-9-design_iter3_request.md` §F.5.

use std::{
    io::Write,
    path::PathBuf,
    process::{Command, Stdio},
};

use tempfile::NamedTempFile;

// ---------------------------------------------------------------------------
// Binary location helper
// ---------------------------------------------------------------------------

/// Locate the production `sqryd` binary.
///
/// Resolution order:
///
/// 1. `CARGO_BIN_EXE_sqryd` — set by Cargo when running integration tests in a
///    workspace with the `sqryd` binary as a workspace member.
/// 2. `<test-binary-dir>/sqryd[.exe]` — the test binary lives in
///    `target/<profile>/deps/`; `sqryd` is placed in `target/<profile>/`.
///    Walk one and two levels up from the test binary's parent directory.
///
/// Returns `None` when the binary cannot be found.  Callers should print a
/// skip message and return rather than panicking so that `cargo test -p
/// sqry-daemon` without an explicit `--bin sqryd` build doesn't fail.
fn find_sqryd_binary() -> Option<PathBuf> {
    // Prefer the Cargo-provided env var (most reliable, set during `cargo test
    // --workspace` and `cargo test -p sqry-daemon`).
    if let Ok(path) = std::env::var("CARGO_BIN_EXE_sqryd") {
        let p = PathBuf::from(path);
        if p.is_file() {
            return Some(p);
        }
    }

    let binary_name = format!("sqryd{}", std::env::consts::EXE_SUFFIX);
    let exe = std::env::current_exe().ok()?;

    // Integration test binaries live in target/<profile>/deps/.
    // `sqryd` is placed at target/<profile>/sqryd.
    let deps_dir = exe.parent()?; // target/<profile>/deps/
    let candidate = deps_dir.join(&binary_name);
    if candidate.is_file() {
        return Some(candidate);
    }
    let profile_dir = deps_dir.parent()?; // target/<profile>/
    let candidate = profile_dir.join(&binary_name);
    if candidate.is_file() {
        return Some(candidate);
    }

    None
}

// ---------------------------------------------------------------------------
// Linux: install-systemd-user → systemd-analyze verify (or structural check)
// ---------------------------------------------------------------------------

/// Invoke `sqryd install-systemd-user`, capture stdout, and validate the
/// produced unit file.
///
/// **Validation strategy:**
///
/// * If `systemd-analyze` is present on the `PATH`, the unit text is written
///   to a temporary `.service` file and passed to `systemd-analyze verify`.
///   A zero exit code is authoritative.
/// * If `systemd-analyze` is absent (uncommon on a normal Linux host but
///   possible in minimal CI containers), a structural content check is
///   performed instead:
///
///   - The text must contain `[Unit]`, `[Service]`, `[Install]` sections.
///   - `Type=notify` must be present.
///   - `ExecStart=` must reference the `foreground` subcommand.
///
///   This is the same set of assertions already covered by the unit tests in
///   `src/lifecycle/units/systemd.rs`; here they serve as a cross-binary
///   integration smoke-check rather than a strict systemd conformance check.
///
/// The test is **skipped with a reason** on non-Linux hosts.
#[test]
#[cfg_attr(
    not(target_os = "linux"),
    ignore = "install-systemd-user is Linux-only; test skipped on this platform"
)]
fn install_systemd_user_output_parses_as_valid_unit_file() {
    // Locate the sqryd binary.
    let Some(sqryd) = find_sqryd_binary() else {
        eprintln!(
            "SKIP install_systemd_user_output_parses_as_valid_unit_file: \
             sqryd binary not found — run `cargo build -p sqry-daemon --bin sqryd` first"
        );
        return;
    };

    // Invoke `sqryd install-systemd-user` and capture stdout.
    let output = Command::new(&sqryd)
        .arg("install-systemd-user")
        // Suppress tracing noise on stderr so test output is clean.
        .env("RUST_LOG", "off")
        .output()
        .unwrap_or_else(|e| panic!("failed to spawn {:?}: {e}", sqryd));

    assert!(
        output.status.success(),
        "sqryd install-systemd-user must exit 0; status={}, stderr={}",
        output.status,
        String::from_utf8_lossy(&output.stderr),
    );

    let unit_text = String::from_utf8_lossy(&output.stdout);
    assert!(
        !unit_text.trim().is_empty(),
        "sqryd install-systemd-user must produce non-empty output"
    );

    // --- Structural content assertions (always run, independent of
    //     whether systemd-analyze is available) ---
    assert!(
        unit_text.contains("[Unit]"),
        "unit file must contain [Unit] section; got:\n{unit_text}"
    );
    assert!(
        unit_text.contains("[Service]"),
        "unit file must contain [Service] section; got:\n{unit_text}"
    );
    assert!(
        unit_text.contains("[Install]"),
        "unit file must contain [Install] section; got:\n{unit_text}"
    );
    assert!(
        unit_text.contains("Type=notify"),
        "unit file must contain Type=notify; got:\n{unit_text}"
    );
    assert!(
        unit_text.contains("foreground"),
        "ExecStart must reference 'foreground' subcommand; got:\n{unit_text}"
    );
    assert!(
        unit_text.contains("WantedBy=default.target"),
        "user unit must target default.target; got:\n{unit_text}"
    );

    // --- systemd-analyze verify (when available) ---
    // Only attempt verification when the tool is present on the host.
    let analyze_available = Command::new("systemd-analyze")
        .arg("--version")
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status()
        .map(|s| s.success())
        .unwrap_or(false);

    if analyze_available {
        // Write the unit text to a temp file with a `.service` extension so
        // systemd-analyze recognises it as a service unit.
        let mut tmp = NamedTempFile::with_suffix(".service")
            .expect("create temp .service file for systemd-analyze verify");
        tmp.write_all(output.stdout.as_slice())
            .expect("write unit text to temp file");
        // Flush to ensure all bytes are on disk before systemd-analyze opens it.
        tmp.flush().expect("flush temp .service file");

        let verify = Command::new("systemd-analyze")
            .arg("verify")
            .arg(tmp.path())
            .output()
            .expect("spawn systemd-analyze verify");

        assert!(
            verify.status.success(),
            "systemd-analyze verify rejected the generated unit file (exit {}):\n\
             stdout: {}\nstderr: {}\nunit text:\n{}",
            verify.status,
            String::from_utf8_lossy(&verify.stdout),
            String::from_utf8_lossy(&verify.stderr),
            unit_text,
        );
    } else {
        eprintln!(
            "NOTE: systemd-analyze not available — skipping verify step; \
             structural content assertions still run"
        );
    }
}

/// Install a systemd *system* unit and validate its structure.
///
/// This test exercises the same `sqryd install-systemd-system` subcommand path
/// that U10 wires up, complementing the user-unit test above with a quick
/// cross-binary integration check.  It passes the current username (resolved
/// via `$USER`) as `--user` to avoid triggering the "no user provided" error
/// path, and checks the `%i` template specifier is present.
///
/// The test is **skipped with a reason** on non-Linux hosts.
#[test]
#[cfg_attr(
    not(target_os = "linux"),
    ignore = "install-systemd-system is Linux-only; test skipped on this platform"
)]
fn install_systemd_system_output_contains_percent_i_template() {
    // The entire body of this test uses `users::get_current_username()` and
    // the `install-systemd-system` subcommand, both of which are Linux-only.
    // All bindings — including the `sqryd` binary lookup — are placed inside a
    // single `#[cfg(target_os = "linux")]` block so that they are erased at
    // compile time on macOS and Windows.  This avoids `unused_variables`
    // warnings (which are hard failures under `-D warnings`) on non-Linux
    // cross-compilation targets.
    //
    // The `#[cfg_attr(not(target_os = "linux"), ignore)]` attribute above is a
    // *runtime* gate only; it does not affect compilation of the function body.
    #[cfg(target_os = "linux")]
    {
        let Some(sqryd) = find_sqryd_binary() else {
            eprintln!(
                "SKIP install_systemd_system_output_contains_percent_i_template: \
                 sqryd binary not found — run `cargo build -p sqry-daemon --bin sqryd` first"
            );
            return;
        };

        // We need a valid POSIX account name to pass as --user.
        // Prefer `users::get_current_username()` (OS lookup, immune to env
        // manipulation) over `$USER` (env var, unreliable in containers / su /
        // sudo contexts).  Fall back to $USER only if the OS lookup fails.
        // Skip the test if neither source can produce a non-empty name.
        let username = {
            let os_name = users::get_current_username()
                .and_then(|n| n.into_string().ok())
                .filter(|n| !n.is_empty());
            let env_name = std::env::var("USER").ok().filter(|n| !n.is_empty());
            match os_name.or(env_name) {
                Some(u) => u,
                None => {
                    eprintln!(
                        "SKIP install_systemd_system_output_contains_percent_i_template: \
                         cannot determine current username via OS lookup or $USER"
                    );
                    return;
                }
            }
        };

        let output = Command::new(&sqryd)
            .args(["install-systemd-system", "--user", &username])
            .env("RUST_LOG", "off")
            .output()
            .unwrap_or_else(|e| panic!("failed to spawn {:?}: {e}", sqryd));

        assert!(
            output.status.success(),
            "sqryd install-systemd-system --user {username} must exit 0; status={}, stderr={}",
            output.status,
            String::from_utf8_lossy(&output.stderr),
        );

        let unit_text = String::from_utf8_lossy(&output.stdout);
        assert!(
            unit_text.contains("User=%i"),
            "system unit must contain User=%i; got:\n{unit_text}"
        );
        assert!(
            unit_text.contains("Group=%i"),
            "system unit must contain Group=%i; got:\n{unit_text}"
        );
        assert!(
            unit_text.contains("WantedBy=multi-user.target"),
            "system unit must target multi-user.target; got:\n{unit_text}"
        );
    }
}

// ---------------------------------------------------------------------------
// macOS: install-launchd → plutil -lint (or structural check on Linux/Windows)
// ---------------------------------------------------------------------------

/// Invoke `sqryd install-launchd`, capture stdout, and validate the produced
/// plist XML.
///
/// **Validation strategy:**
///
/// * On macOS: write the output to a temp file and invoke `plutil -lint` on
///   it.  A zero exit code from `plutil` is authoritative proof of well-formed
///   plist XML.
/// * On Linux / Windows: the test is skipped at runtime with a reason message
///   because `install-launchd` is a macOS-only subcommand and the `sqryd`
///   binary compiled for Linux/Windows does not include it.
///
/// The test is **skipped** on non-macOS hosts.
#[test]
#[cfg_attr(
    not(target_os = "macos"),
    ignore = "install-launchd is macOS-only; test skipped on this platform"
)]
fn install_launchd_output_parses_as_valid_plist_xml() {
    let Some(sqryd) = find_sqryd_binary() else {
        eprintln!(
            "SKIP install_launchd_output_parses_as_valid_plist_xml: \
             sqryd binary not found — run `cargo build -p sqry-daemon --bin sqryd` first"
        );
        return;
    };

    let output = Command::new(&sqryd)
        .arg("install-launchd")
        .env("RUST_LOG", "off")
        .output()
        .unwrap_or_else(|e| panic!("failed to spawn {:?}: {e}", sqryd));

    assert!(
        output.status.success(),
        "sqryd install-launchd must exit 0; status={}, stderr={}",
        output.status,
        String::from_utf8_lossy(&output.stderr),
    );

    let plist_text = String::from_utf8_lossy(&output.stdout);
    assert!(
        !plist_text.trim().is_empty(),
        "sqryd install-launchd must produce non-empty output"
    );

    // --- Structural content assertions ---
    assert!(
        plist_text.contains("<?xml version=\"1.0\""),
        "plist must begin with the XML declaration; got:\n{plist_text}"
    );
    assert!(
        plist_text.contains("<plist version=\"1.0\">"),
        "plist must contain the root <plist> element; got:\n{plist_text}"
    );
    assert!(
        plist_text.contains("ai.verivus.sqry.sqryd"),
        "plist must contain the sqryd label; got:\n{plist_text}"
    );
    // Validate that KeepAlive and RunAtLoad are both present and set to
    // `<true/>`.  We check this with a proximity predicate rather than exact
    // adjacency (`<key>KeepAlive</key><true/>`) so that the assertions remain
    // correct whether the generator emits compact or indented XML.
    //
    // The predicate: locate the key text, then inspect the plist text starting
    // immediately after that key for the first `<true/>` or `<false/>` value
    // element before the next `<key>` entry.  This matches plist semantics
    // (a key's value is the very next element) while tolerating whitespace.
    let plist_key_is_true = |key: &str| -> bool {
        let key_tag = format!("<key>{key}</key>");
        let Some(after_key) = plist_text
            .find(key_tag.as_str())
            .map(|pos| &plist_text[pos + key_tag.len()..])
        else {
            return false;
        };
        // Scan forward: the value element must appear before the next <key>.
        let next_key_pos = after_key.find("<key>").unwrap_or(after_key.len());
        let between = &after_key[..next_key_pos];
        between.contains("<true/>")
    };
    assert!(
        plist_key_is_true("KeepAlive"),
        "plist must set KeepAlive to <true/>; got:\n{plist_text}"
    );
    assert!(
        plist_key_is_true("RunAtLoad"),
        "plist must set RunAtLoad to <true/>; got:\n{plist_text}"
    );
    assert!(
        plist_text.contains("</plist>"),
        "plist must close the root element; got:\n{plist_text}"
    );

    // --- plutil -lint validation (macOS only, when tool is present) ---
    // On macOS, `plutil` ships as part of the base system at `/usr/bin/plutil`.
    // We probe availability by checking whether that path is a file — this
    // avoids spawning `plutil` for the probe itself, which is error-prone:
    // `plutil --version` is not a supported flag and exits non-zero on all
    // macOS versions; `plutil -lint /dev/null` also exits non-zero because
    // /dev/null is not a valid plist.  A filesystem check has no such caveat.
    #[cfg(target_os = "macos")]
    let plutil_available = std::path::Path::new("/usr/bin/plutil").is_file();
    #[cfg(not(target_os = "macos"))]
    let plutil_available = false;

    if plutil_available {
        // Write the plist to a temp file with a `.plist` extension so
        // `plutil` recognises it correctly.
        let mut tmp =
            NamedTempFile::with_suffix(".plist").expect("create temp .plist file for plutil lint");
        tmp.write_all(output.stdout.as_slice())
            .expect("write plist text to temp file");
        tmp.flush().expect("flush temp .plist file");

        let lint = Command::new("plutil")
            .args(["-lint", tmp.path().to_str().expect("temp path is UTF-8")])
            .output()
            .expect("spawn plutil -lint");

        assert!(
            lint.status.success(),
            "plutil -lint rejected the generated plist (exit {}):\n\
             stdout: {}\nstderr: {}\nplist text:\n{}",
            lint.status,
            String::from_utf8_lossy(&lint.stdout),
            String::from_utf8_lossy(&lint.stderr),
            plist_text,
        );
    } else {
        eprintln!(
            "NOTE: plutil not available — skipping lint step; \
             structural content assertions still run"
        );
    }
}

// ---------------------------------------------------------------------------
// Windows: install-windows → both sc.exe and Task Scheduler XML variants
// ---------------------------------------------------------------------------

/// Invoke `sqryd install-windows` and assert that the output contains both the
/// `sc.exe create` command and the Task Scheduler XML document.
///
/// The test is **skipped with a reason** on all non-Windows hosts because the
/// `install-windows` subcommand is only compiled into the Windows binary.
#[test]
#[cfg_attr(
    not(target_os = "windows"),
    ignore = "install-windows is Windows-only; test skipped on this platform"
)]
fn install_windows_emits_both_variants() {
    let Some(sqryd) = find_sqryd_binary() else {
        eprintln!(
            "SKIP install_windows_emits_both_variants: \
             sqryd binary not found — run `cargo build -p sqry-daemon --bin sqryd` first"
        );
        return;
    };

    let output = Command::new(&sqryd)
        .arg("install-windows")
        .env("RUST_LOG", "off")
        .output()
        .unwrap_or_else(|e| panic!("failed to spawn {:?}: {e}", sqryd));

    assert!(
        output.status.success(),
        "sqryd install-windows must exit 0; status={}, stderr={}",
        output.status,
        String::from_utf8_lossy(&output.stderr),
    );

    let combined = String::from_utf8_lossy(&output.stdout);
    assert!(
        !combined.trim().is_empty(),
        "sqryd install-windows must produce non-empty output"
    );

    // ── sc.exe section ────────────────────────────────────────────────────────
    // The entrypoint prints "-- sc.exe create command --" before the script.
    assert!(
        combined.contains("-- sc.exe create command --"),
        "output must contain the exact sc.exe create command section header; got:\n{combined}"
    );
    assert!(
        combined.contains("sc.exe create sqryd"),
        "output must include the sc.exe create sqryd command; got:\n{combined}"
    );
    assert!(
        combined.contains("start= auto"),
        "sc.exe create command must set start= auto; got:\n{combined}"
    );

    // ── Task Scheduler XML section ────────────────────────────────────────────
    // The entrypoint prints "-- Task Scheduler XML --" before the XML.
    assert!(
        combined.contains("-- Task Scheduler XML --"),
        "output must contain the exact Task Scheduler XML section header; got:\n{combined}"
    );
    assert!(
        combined.contains("<?xml version=\"1.0\""),
        "output must contain the XML declaration for the task XML; got:\n{combined}"
    );
    assert!(
        combined.contains("<Task "),
        "output must contain the Task Scheduler <Task> root element; got:\n{combined}"
    );
    assert!(
        combined.contains("http://schemas.microsoft.com/windows/2004/02/mit/task"),
        "Task Scheduler XML must include the correct namespace; got:\n{combined}"
    );
    assert!(
        combined.contains("</Task>"),
        "Task Scheduler XML must be closed; got:\n{combined}"
    );

    // ── Both variants must not be confused ───────────────────────────────────
    // The entrypoint (run_install_windows in entrypoint.rs) emits the exact
    // separator "-- Task Scheduler XML --" between the two sections. Using
    // this exact string avoids accidental matches if the phrase appears in
    // comments or documentation elsewhere in the output.
    const XML_SEPARATOR: &str = "-- Task Scheduler XML --";

    // The sc.exe section must not contain the XML declaration.
    let sc_section = combined.split(XML_SEPARATOR).next().unwrap_or(&combined);
    assert!(
        !sc_section.contains("<?xml version="),
        "sc.exe section must not contain XML declarations; got:\n{sc_section}"
    );

    // The Task Scheduler XML section must not contain sc.exe commands.
    let xml_section = combined.split(XML_SEPARATOR).nth(1).unwrap_or_default();
    assert!(
        !xml_section.contains("sc.exe create"),
        "Task Scheduler XML section must not contain sc.exe commands; got:\n{xml_section}"
    );
}

// ---------------------------------------------------------------------------
// Cross-platform smoke: print-config always works
// ---------------------------------------------------------------------------

/// Invoke `sqryd print-config` and assert the output is valid TOML containing
/// key fields from the default `DaemonConfig`.
///
/// This test runs on all platforms and serves as a cross-platform integration
/// smoke-check that the binary starts up, loads the config subsystem, and
/// serialises the effective configuration correctly.
#[test]
fn print_config_emits_valid_toml_with_expected_keys() {
    let Some(sqryd) = find_sqryd_binary() else {
        eprintln!(
            "SKIP print_config_emits_valid_toml_with_expected_keys: \
             sqryd binary not found — run `cargo build -p sqry-daemon --bin sqryd` first"
        );
        return;
    };

    let output = Command::new(&sqryd)
        .arg("print-config")
        .env("RUST_LOG", "off")
        .output()
        .unwrap_or_else(|e| panic!("failed to spawn {:?}: {e}", sqryd));

    assert!(
        output.status.success(),
        "sqryd print-config must exit 0; status={}, stderr={}",
        output.status,
        String::from_utf8_lossy(&output.stderr),
    );

    let toml_text = String::from_utf8_lossy(&output.stdout);
    assert!(
        !toml_text.trim().is_empty(),
        "sqryd print-config must produce non-empty output"
    );

    // Check that TOML is parseable.
    let parsed: toml::Value = toml::from_str(&toml_text).unwrap_or_else(|e| {
        panic!("sqryd print-config output is not valid TOML: {e}\noutput:\n{toml_text}")
    });

    // Spot-check key fields that DaemonConfig always serialises.
    assert!(
        parsed.get("memory_limit_mb").is_some(),
        "TOML must contain memory_limit_mb; parsed:\n{toml_text}"
    );
    assert!(
        parsed.get("debounce_ms").is_some(),
        "TOML must contain debounce_ms; parsed:\n{toml_text}"
    );
    assert!(
        parsed.get("idle_timeout_minutes").is_some(),
        "TOML must contain idle_timeout_minutes; parsed:\n{toml_text}"
    );
}