P2 slice 3: opening and closing the books (plan 3.9, D-21, D-23)
One wanderbooks executable. The books are open while the viewer, an agent session or a terminal holds them: a host process takes leases on a socket, starts the server (the hidden mode `wanderbooks server`) with a pipe on its input, and closes it 10 s after the last lease ends. No background service. One host even in a race (run/host.lock). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
1 parent
168e5c9846
commit
a298d154b6
31 files changed
+11937
-112
No files matched your search
@@ -103,6 +103,14 @@ fn each_mode_names_itself_in_its_usage() {
|
||||
"{err}"
|
||||
);
|
||||
|
||||
let out = wanderbooks().args(["server", "--help"]).output().unwrap();
|
||||
assert!(out.status.success());
|
||||
let err = String::from_utf8_lossy(&out.stderr);
|
||||
assert!(
|
||||
err.contains("usage: wanderbooks server [--root DIR]"),
|
||||
"{err}"
|
||||
);
|
||||
|
||||
let out = wanderbooks().arg("--help").output().unwrap();
|
||||
let err = String::from_utf8_lossy(&out.stderr);
|
||||
for line in [
|
||||
@@ -129,9 +137,9 @@ fn the_viewer_names_itself_in_its_usage() {
|
||||
#[test]
|
||||
fn a_mode_refuses_what_it_does_not_know() {
|
||||
let modes: &[&str] = if cfg!(feature = "gui") {
|
||||
&["dev-stdio", "gui"]
|
||||
&["dev-stdio", "gui", "server"]
|
||||
} else {
|
||||
&["dev-stdio"]
|
||||
&["dev-stdio", "server"]
|
||||
};
|
||||
for &mode in modes {
|
||||
let out = wanderbooks()
|
||||
@@ -150,7 +158,7 @@ fn a_mode_refuses_what_it_does_not_know() {
|
||||
|
||||
#[test]
|
||||
fn a_mode_comes_first() {
|
||||
for mode in ["gui", "dev-stdio"] {
|
||||
for mode in ["gui", "dev-stdio", "server"] {
|
||||
let out = wanderbooks().args(["--codex", mode]).output().unwrap();
|
||||
assert_eq!(out.status.code(), Some(2), "{mode}: {out:?}");
|
||||
let err = String::from_utf8_lossy(&out.stderr);
|
||||
@@ -218,3 +226,65 @@ fn the_server_only_build_has_no_viewer() {
|
||||
"{err}"
|
||||
);
|
||||
}
|
||||
|
||||
/// Books in the data root are the books server's alone (plan 3.1): the
|
||||
/// test server refuses them, and the books server needs a root.
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn dev_stdio_refuses_books_in_the_private_folder() {
|
||||
let home = tempfile::Builder::new()
|
||||
.prefix("wb-one-root-")
|
||||
.tempdir()
|
||||
.unwrap();
|
||||
let out = wanderbooks()
|
||||
.args(["setup", "--home"])
|
||||
.arg(home.path())
|
||||
.env_remove("XDG_DATA_HOME")
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(out.status.success(), "{out:?}");
|
||||
let root = home.path().join(".local/share/wanderbooks");
|
||||
for inside in [
|
||||
root.join("books/acme"),
|
||||
root.clone(),
|
||||
root.join("books/a/b"),
|
||||
] {
|
||||
let out = wanderbooks()
|
||||
.args(["dev-stdio", "--data-dir"])
|
||||
.arg(&inside)
|
||||
.stdin(std::process::Stdio::null())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert_eq!(out.status.code(), Some(2), "{inside:?}: {out:?}");
|
||||
let err = String::from_utf8_lossy(&out.stderr);
|
||||
assert!(err.contains("only the books server opens"), "{err}");
|
||||
assert!(!inside.join("journal.jsonl").exists(), "{inside:?}");
|
||||
}
|
||||
assert!(!root.join("books/acme").exists());
|
||||
// A relative path, from inside the root.
|
||||
let out = wanderbooks()
|
||||
.args(["dev-stdio", "--data-dir", "acme"])
|
||||
.current_dir(root.join("books"))
|
||||
.stdin(std::process::Stdio::null())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert_eq!(out.status.code(), Some(2), "{out:?}");
|
||||
assert!(!root.join("books/acme").exists());
|
||||
|
||||
// No root yet: the books server says to run setup.
|
||||
let empty = tempfile::Builder::new()
|
||||
.prefix("wb-one-noroot-")
|
||||
.tempdir()
|
||||
.unwrap();
|
||||
let out = wanderbooks()
|
||||
.args(["server", "--root"])
|
||||
.arg(empty.path().join("none"))
|
||||
.stdin(std::process::Stdio::null())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert_eq!(out.status.code(), Some(2), "{out:?}");
|
||||
assert!(
|
||||
String::from_utf8_lossy(&out.stderr).contains("run `wanderbooks setup` first"),
|
||||
"{out:?}"
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user