33//! Tests that need a live Origin sync endpoint use [`OriginServer`].
44//! The guard kills the process on drop.
55//!
6- //! The Origin binary is located relative to the nodedb workspace target dir.
7- //! If the binary is not present the test fails immediately with a clear message.
6+ //! The Origin binary is located in one of three ways (in priority order):
7+ //! 1. `NODEDB_BIN` env var — set by the nextest setup script.
8+ //! 2. `<project-root>/nodedb/target/release/nodedb` (pre-built release).
9+ //! 3. `<project-root>/nodedb/target/debug/nodedb` (pre-built debug).
10+ //!
11+ //! If no binary is found, [`OriginServer::try_spawn`] returns `None` and
12+ //! the calling test should print a skip message and return early.
813//!
914//! The sync WebSocket listener always binds to `0.0.0.0:9090` (the
1015//! `SyncListenerConfig` default). All interop test files are placed in the
@@ -19,47 +24,46 @@ use std::path::{Path, PathBuf};
1924use std:: process:: { Child , Command , Stdio } ;
2025use std:: time:: { Duration , Instant } ;
2126
22- /// Locate the nodedb Origin binary.
27+ /// Locate the nodedb Origin binary, if available.
28+ ///
29+ /// Returns `None` when no binary can be found — interop tests treat that as a
30+ /// skip (see [`OriginServer::try_spawn`]), so a Lite-only checkout still passes.
2331///
2432/// Search order:
25- /// 1. `NODEDB_BIN` env var (CI override).
26- /// 2. `<project-root>/nodedb/target/release/nodedb`
27- /// 3. `<project-root>/nodedb/target/debug/nodedb`
33+ /// 1. `NODEDB_BIN` env var — exported by the `build-origin` nextest setup
34+ /// script (`scripts/ensure-origin.sh`), which runs `cargo build -p nodedb`
35+ /// against this workspace's dev-dependency. This is the normal path under
36+ /// `cargo nextest run`.
37+ /// 2. This workspace's own `target/{debug,release}/nodedb` — for a manual
38+ /// `cargo build -p nodedb` outside nextest.
2839///
29- /// Project root is inferred by walking up from `CARGO_MANIFEST_DIR`.
30- pub fn find_origin_binary ( ) -> PathBuf {
31- if let Ok ( path) = env:: var ( "NODEDB_BIN" ) {
32- return PathBuf :: from ( path) ;
33- }
34-
35- // Walk up from CARGO_MANIFEST_DIR (nodedb-lite/nodedb-lite/).
36- let manifest =
37- env:: var ( "CARGO_MANIFEST_DIR" ) . expect ( "CARGO_MANIFEST_DIR must be set in test environment" ) ;
38- let manifest = Path :: new ( & manifest) ;
39- // Up two levels: nodedb-lite/nodedb-lite → nodedb-lite → project root.
40- let project_root = manifest
41- . parent ( )
42- . and_then ( |p| p. parent ( ) )
43- . expect ( "could not determine project root from CARGO_MANIFEST_DIR" ) ;
44-
45- let release = project_root. join ( "nodedb/target/release/nodedb" ) ;
46- if release. exists ( ) {
47- return release;
40+ /// There is deliberately NO hardcoded sibling-repo path: the Origin crate is
41+ /// resolved through cargo (crates.io or the local `[patch.crates-io]`), so its
42+ /// binary always lands in this workspace's own target directory.
43+ pub fn find_origin_binary ( ) -> Option < PathBuf > {
44+ if let Ok ( val) = env:: var ( "NODEDB_BIN" ) {
45+ let p = PathBuf :: from ( & val) ;
46+ if p. is_file ( ) {
47+ return Some ( p) ;
48+ }
4849 }
4950
50- let debug = project_root. join ( "nodedb/target/debug/nodedb" ) ;
51- if debug. exists ( ) {
52- return debug;
51+ // This workspace's own cargo target dir. CARGO_MANIFEST_DIR is
52+ // nodedb-lite/nodedb-lite/; its parent is the workspace root nodedb-lite/.
53+ let manifest = env:: var ( "CARGO_MANIFEST_DIR" ) . ok ( ) ?;
54+ let workspace_root = Path :: new ( & manifest) . parent ( ) ?. to_path_buf ( ) ;
55+ let target = env:: var ( "CARGO_TARGET_DIR" )
56+ . map ( PathBuf :: from)
57+ . unwrap_or_else ( |_| workspace_root. join ( "target" ) ) ;
58+
59+ for profile in [ "debug" , "release" ] {
60+ let candidate = target. join ( profile) . join ( "nodedb" ) ;
61+ if candidate. is_file ( ) {
62+ return Some ( candidate) ;
63+ }
5364 }
5465
55- panic ! (
56- "Origin binary not found. Expected one of:\n {}\n {}\n \
57- Build with: cd {}/nodedb && cargo build -p nodedb\n \
58- Or set NODEDB_BIN=/path/to/nodedb",
59- release. display( ) ,
60- debug. display( ) ,
61- project_root. display( ) ,
62- )
66+ None
6367}
6468
6569/// The sync WebSocket URL that Origin always listens on.
@@ -71,7 +75,7 @@ pub const ORIGIN_PGWIRE_ADDR: &str = "127.0.0.1:6432";
7175/// Guard for a running Origin server process.
7276///
7377/// Kills the process on drop. Tests obtain an instance via
74- /// [`OriginServer::spawn `] or [`OriginServer::spawn_with_pgwire `].
78+ /// [`OriginServer::try_spawn `] or [`OriginServer::try_spawn_with_pgwire `].
7579///
7680/// Each instance has its own temporary data directory so WAL / storage
7781/// state from previous runs cannot interfere.
@@ -88,22 +92,28 @@ pub struct OriginServer {
8892impl OriginServer {
8993 /// Spawn a fresh Origin server with a private temp data directory.
9094 ///
95+ /// Returns `None` if the Origin binary cannot be found (Origin repo absent
96+ /// or not built). The caller should print a skip message and return early.
97+ ///
9198 /// Blocks (up to 30 s) until the sync WebSocket port is accepting TCP
9299 /// connections.
93- pub fn spawn ( ) -> Self {
94- Self :: spawn_inner ( false )
100+ pub fn try_spawn ( ) -> Option < Self > {
101+ let binary = find_origin_binary ( ) ?;
102+ Some ( Self :: spawn_inner ( binary, false ) )
95103 }
96104
97105 /// Spawn a fresh Origin server with both the sync WebSocket (port 9090)
98106 /// and the pgwire listener (port 6432) enabled in trust auth mode.
99107 ///
108+ /// Returns `None` if the Origin binary cannot be found.
109+ ///
100110 /// Blocks until **both** ports are accepting TCP connections (up to 30 s).
101- pub fn spawn_with_pgwire ( ) -> Self {
102- Self :: spawn_inner ( true )
111+ pub fn try_spawn_with_pgwire ( ) -> Option < Self > {
112+ let binary = find_origin_binary ( ) ?;
113+ Some ( Self :: spawn_inner ( binary, true ) )
103114 }
104115
105- fn spawn_inner ( with_pgwire : bool ) -> Self {
106- let binary = find_origin_binary ( ) ;
116+ fn spawn_inner ( binary : PathBuf , with_pgwire : bool ) -> Self {
107117 let data_dir = tempfile:: tempdir ( ) . expect ( "create temp data dir for Origin" ) ;
108118
109119 let ( mut cmd, config_dir) = if with_pgwire {
0 commit comments