docs(plan): drop the launchd integration test

Task 6 is documentation-only. A real launchd cycle test would either
hard-code the live label and risk clobbering a working installation, or
need a test-only --label flag; the manual acceptance section covers the
mechanism instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EGntTHCW3sEPy1VBRopNNp
This commit is contained in:
2026-07-31 22:48:12 +02:00
co-authored by Claude Opus 5
parent f74eb1e865
commit 2ab74c992b
@@ -38,9 +38,14 @@
| `crates/xy/src/cli/mod.rs` | *Modify:* declare `mod service;` and re-export | | `crates/xy/src/cli/mod.rs` | *Modify:* declare `mod service;` and re-export |
| `crates/xy/src/main.rs` | *Modify:* `Service` subcommand arm; reorder path resolution before logger init | | `crates/xy/src/main.rs` | *Modify:* `Service` subcommand arm; reorder path resolution before logger init |
| `crates/xy/src/daemon/mod.rs` | *Modify:* drop the now-duplicated `ensure_dirs()` call | | `crates/xy/src/daemon/mod.rs` | *Modify:* drop the now-duplicated `ensure_dirs()` call |
| `crates/xy/tests/service.rs` | *New:* `#[ignore]` end-to-end launchd cycle |
| `README.md` | *Modify:* document the five verbs | | `README.md` | *Modify:* document the five verbs |
No integration test is planned. Driving a real launchd cycle would either
hard-code the live `se.aceofba.xy` label and risk clobbering a working
installation, or require a test-only `--label` flag on the CLI. The load/unload
mechanism is covered instead by the *Manual acceptance* section at the end of
this plan, which is the only check that can actually prove `RunAtLoad` works.
--- ---
### Task 1: `io::Write` for `RotatingLogWriter` ### Task 1: `io::Write` for `RotatingLogWriter`
@@ -1161,82 +1166,21 @@ git commit -m "feat(cli): xy service install/uninstall/start/stop/status"
--- ---
### Task 6: End-to-end test and documentation ### Task 6: Documentation
An ignored integration test that exercises a real launchd cycle under a distinct label, plus README coverage. Document the five verbs in the README. No integration test: driving a real
launchd cycle would either hard-code the live `se.aceofba.xy` label and risk
clobbering a working installation, or require a test-only `--label` flag on the
CLI. The *Manual acceptance* section below covers the mechanism instead.
**Files:** **Files:**
- Create: `crates/xy/tests/service.rs`
- Modify: `README.md` - Modify: `README.md`
**Interfaces:** **Interfaces:**
- Consumes: the `xy` binary built by cargo. The test shells out rather than linking, because `service.rs` is `pub(crate)`. - Consumes: nothing from earlier tasks.
- Produces: nothing consumed by later tasks. - Produces: nothing consumed by later tasks.
- [ ] **Step 1: Write the ignored end-to-end test** - [ ] **Step 1: Document the feature**
Create `crates/xy/tests/service.rs`:
```rust
use std::process::Command;
fn launchctl(verb: &str, arg: &str) -> bool {
Command::new("launchctl")
.arg(verb)
.arg(arg)
.output()
.map(|out| out.status.success())
.unwrap_or(false)
}
#[test]
#[ignore = "installs a real launchd agent; run manually with --ignored"]
fn install_start_stop_uninstall_cycle() {
let home = std::env::var("HOME").unwrap();
let plist = format!("{home}/Library/LaunchAgents/se.aceofba.xy-test.plist");
assert!(
!std::path::Path::new(&plist).exists(),
"test agent already installed; remove {plist} first"
);
let contents = format!(
r#"<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>Label</key><string>se.aceofba.xy-test</string>
<key>ProgramArguments</key><array><string>/bin/sleep</string><string>3600</string></array>
<key>RunAtLoad</key><true/>
</dict></plist>
"#
);
std::fs::write(&plist, contents).unwrap();
assert!(launchctl("load", &plist), "load failed");
assert!(launchctl("unload", &plist), "unload failed");
std::fs::remove_file(&plist).unwrap();
assert!(!std::path::Path::new(&plist).exists());
}
```
This deliberately drives `launchctl` against a harmless `/bin/sleep` agent under the `se.aceofba.xy-test` label rather than invoking `xy service install`, which would hard-code the real `se.aceofba.xy` label and could disturb a working installation. It verifies the load/unload mechanism Task 4 relies on.
- [ ] **Step 2: Confirm it is skipped by default**
Run: `cargo nextest run -p xy`
Expected: the test is listed as skipped, not run.
- [ ] **Step 3: Run it explicitly and confirm it passes**
Run: `cargo nextest run -p xy --run-ignored all -E 'test(/install_start_stop_uninstall_cycle/)'`
Expected: PASS. Then confirm no residue: `ls ~/Library/LaunchAgents/ | grep xy-test` returns nothing.
- [ ] **Step 4: Document the feature**
Add to `README.md`, after the existing command list: Add to `README.md`, after the existing command list:
@@ -1264,7 +1208,7 @@ visible only to launchd:
launchctl print gui/$UID/se.aceofba.xy launchctl print gui/$UID/se.aceofba.xy
``` ```
- [ ] **Step 5: Full suite, format, lint** - [ ] **Step 2: Full suite, format, lint**
```bash ```bash
cargo nextest run cargo nextest run
@@ -1272,11 +1216,11 @@ cargo +nightly fmt
cargo clippy --workspace cargo clippy --workspace
``` ```
- [ ] **Step 6: Commit** - [ ] **Step 3: Commit**
```bash ```bash
git add crates/xy/tests/service.rs README.md git add README.md
git commit -m "test(service): ignored launchd cycle test; document xy service" git commit -m "docs(readme): document xy service verbs"
``` ```
--- ---