From 26f3a73f58a32def13ccd3db882d7d09040128f5 Mon Sep 17 00:00:00 2001 From: l0ng-ai Date: Tue, 28 Jul 2026 19:39:19 +0800 Subject: [PATCH] fix(ci): gate the tty7-server test suites that need --stdio on Unix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `--stdio` is refused on Windows by design, and the control socket it probes for is Unix-domain, so `stdio_conformance` and `workspace_store` join `remote_router`/`routed_pane` in carrying a file-level `cfg(unix)`. `cli.rs` keeps its argument-handling cases everywhere — `--version`, `--help`, `agent-hook` and the usage error say nothing about transports — and gates only the bridge and probe cases, which spawn a `--stdio` child or stand up a listener. --- crates/tty7-server/tests/cli.rs | 27 ++++++++++++++++++- crates/tty7-server/tests/stdio_conformance.rs | 5 ++++ crates/tty7-server/tests/workspace_store.rs | 4 +++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/crates/tty7-server/tests/cli.rs b/crates/tty7-server/tests/cli.rs index 36a0e0ab..ceb4eb1b 100644 --- a/crates/tty7-server/tests/cli.rs +++ b/crates/tty7-server/tests/cli.rs @@ -6,22 +6,40 @@ //! a connection to a control server that is already running, which is the path //! an `ssh host tty7-server --stdio` takes on a machine with a live daemon and //! which no amount of `Host` conformance would exercise. +//! +//! Everything `--stdio` is Unix-only — the flag is refused on Windows, where a +//! machine is reached over its own transport rather than by shipping a server +//! onto it (contract §8). The plain argument handling below is not, and runs +//! everywhere. +use std::process::{Command, Stdio}; + +#[cfg(unix)] use std::io; +#[cfg(unix)] use std::path::PathBuf; -use std::process::{Child, Command, Stdio}; +#[cfg(unix)] +use std::process::Child; +#[cfg(unix)] use std::sync::{Arc, Mutex}; +#[cfg(unix)] use tty7_core::daemon::control::{ControlHello, LinkShutdown}; +#[cfg(unix)] use tty7_core::host::Host; +#[cfg(unix)] use tty7_core::host::local::LocalHost; +#[cfg(unix)] use tty7_core::host::remote::RemoteHost; +#[cfg(unix)] use tty7_core::host::server; const EXE: &str = env!("CARGO_BIN_EXE_tty7-server"); +#[cfg(unix)] struct ServerProcess(Mutex>); +#[cfg(unix)] impl LinkShutdown for ServerProcess { fn shutdown_link(&self) -> io::Result<()> { if let Some(mut c) = self.0.lock().unwrap_or_else(|e| e.into_inner()).take() { @@ -33,6 +51,7 @@ impl LinkShutdown for ServerProcess { } /// Start `tty7-server --stdio ` and connect a `RemoteHost` to its pipes. +#[cfg(unix)] fn stdio_child(args: &[&str]) -> io::Result> { let mut child = Command::new(EXE) .arg("--stdio") @@ -49,6 +68,7 @@ fn stdio_child(args: &[&str]) -> io::Result> { } /// A control server on a temp socket, for the bridge to reach. +#[cfg(unix)] fn listening_server(dir: &tempfile::TempDir) -> PathBuf { let sock = dir.path().join("control.sock"); let listener = server::bind_control_socket(&sock).unwrap(); @@ -63,6 +83,7 @@ fn listening_server(dir: &tempfile::TempDir) -> PathBuf { /// stream carries belongs to the client and the server at the far end, and a /// bridge with an opinion about the protocol would become a third party to a /// negotiation it is not qualified to join. +#[cfg(unix)] #[test] fn the_bridge_carries_a_whole_session() { let dir = tempfile::TempDir::new().unwrap(); @@ -92,6 +113,7 @@ fn the_bridge_carries_a_whole_session() { /// `--bridge` with nowhere to bridge to fails rather than quietly serving /// itself. An operator who asked for the bridge is telling us a server exists; /// silently becoming that server would fork the machine's state in two. +#[cfg(unix)] #[test] fn an_explicit_bridge_with_no_server_fails() { let dir = tempfile::TempDir::new().unwrap(); @@ -105,6 +127,7 @@ fn an_explicit_bridge_with_no_server_fails() { /// With neither flag, `--stdio` probes: nothing listening means serve here, so a /// machine that has never run a daemon is still reachable over ssh. +#[cfg(unix)] #[test] fn the_default_mode_serves_when_nothing_is_listening() { let dir = tempfile::TempDir::new().unwrap(); @@ -117,6 +140,7 @@ fn the_default_mode_serves_when_nothing_is_listening() { /// ...and something listening means bridge to it, so a second `--stdio` session /// joins the machine's existing server instead of standing up a rival. +#[cfg(unix)] #[test] fn the_default_mode_bridges_when_a_server_is_listening() { let dir = tempfile::TempDir::new().unwrap(); @@ -130,6 +154,7 @@ fn the_default_mode_bridges_when_a_server_is_listening() { } /// Contradictory flags are refused rather than one silently winning. +#[cfg(unix)] #[test] fn serve_and_bridge_together_are_refused() { let out = Command::new(EXE) diff --git a/crates/tty7-server/tests/stdio_conformance.rs b/crates/tty7-server/tests/stdio_conformance.rs index 86364f8e..a25004e2 100644 --- a/crates/tty7-server/tests/stdio_conformance.rs +++ b/crates/tty7-server/tests/stdio_conformance.rs @@ -22,6 +22,11 @@ //! isolation: no case can be explained by another's leftover state, a hung //! server fails exactly one case, and a crash names the behaviour that caused it. +// Unix-only: every case here is a `--stdio` child, and `--stdio` is refused on +// Windows by design — a Windows machine is reached over its own transport, not +// by shipping a server onto it (contract §8). +#![cfg(unix)] + use std::io; use std::path::Path; use std::process::{Child, Command, Stdio}; diff --git a/crates/tty7-server/tests/workspace_store.rs b/crates/tty7-server/tests/workspace_store.rs index 686fed80..85661260 100644 --- a/crates/tty7-server/tests/workspace_store.rs +++ b/crates/tty7-server/tests/workspace_store.rs @@ -19,6 +19,10 @@ //! another's leftovers and nothing here can touch the developer's real //! `~/.local/share/tty7/workspaces.json`. +// Unix-only, for the same reason as `stdio_conformance.rs`: the server under +// test is a `--stdio` child, and two of the cases stand up a control socket. +#![cfg(unix)] + use std::io; use std::path::{Path, PathBuf}; use std::process::{Child, Command, Stdio};