1pub mod fault_injection;
56pub mod fixtures;
57pub mod harness;
58pub mod logging;
59pub mod process_triage;
60pub mod test_workers;
61pub mod verification;
62
63pub use fault_injection::{
65 ELF_MAGIC, FaultFixture, FaultScenario, MACH_O_64_MAGIC, SimulatedDiskStats,
66};
67pub use fixtures::{
68 DEFAULT_MULTI_REPO_ALIAS_ROOT, DEFAULT_MULTI_REPO_CANONICAL_ROOT,
69 DEFAULT_MULTI_REPO_FIXTURE_NAMESPACE, DaemonConfigFixture, FixtureFailureMode, FixtureLayer,
70 FixtureReadiness, HookInputFixture, MultiRepoFixtureConfig, MultiRepoFixtureError,
71 MultiRepoFixtureMetadata, MultiRepoFixtureResult, MultiRepoFixtureSet, RustProjectFixture,
72 TestCaseFixture, WorkerFixture, WorkersFixture, reset_default_multi_repo_fixtures,
73 reset_multi_repo_fixtures,
74};
75pub use harness::{
76 CommandResult, HarnessConfig, HarnessError, HarnessResult, ProcessInfo,
77 ReliabilityCommandRecord, ReliabilityFailureHook, ReliabilityFailureHookFlags,
78 ReliabilityLifecycleCommand, ReliabilityScenarioReport, ReliabilityScenarioSpec,
79 ReliabilityWorkerLifecycleHooks, TestHarness, TestHarnessBuilder, cleanup_stale_test_artifacts,
80};
81pub use logging::{
82 LogEntry, LogLevel, LogSource, LoggerConfig, RELIABILITY_EVENT_SCHEMA_VERSION,
83 ReliabilityContext, ReliabilityEventInput, ReliabilityPhase, ReliabilityPhaseEvent,
84 TestLogSummary, TestLogger, TestLoggerBuilder,
85};
86pub use process_triage::{
87 PROCESS_TRIAGE_CONTRACT_SCHEMA_VERSION, ProcessTriageActionClass, ProcessTriageActionOutcome,
88 ProcessTriageActionRequest, ProcessTriageActionResult, ProcessTriageAdapterCommand,
89 ProcessTriageAuditRecord, ProcessTriageCommandBudget, ProcessTriageContract,
90 ProcessTriageContractError, ProcessTriageEscalationLevel, ProcessTriageFailure,
91 ProcessTriageFailureKind, ProcessTriagePolicyDecision, ProcessTriageRequest,
92 ProcessTriageResponse, ProcessTriageResponseStatus, ProcessTriageRetryPolicy,
93 ProcessTriageSafeActionPolicy, ProcessTriageTimeoutPolicy, ProcessTriageTrigger,
94 evaluate_triage_action, process_triage_request_schema, process_triage_response_schema,
95};
96pub use test_workers::{
97 ENV_SKIP_WORKER_CHECK, ENV_TIMEOUT_SECS, ENV_WORKERS_CONFIG, TestConfigError, TestConfigResult,
98 TestSettings, TestWorkerEntry, TestWorkersConfig, expand_tilde_path, get_config_path,
99 is_mock_ssh_mode, should_skip_worker_check,
100};
101pub use verification::{RemoteCompilationTest, VerificationConfig, VerificationResult};
102
103#[macro_export]
114macro_rules! e2e_test {
115 ($name:ident, $body:expr) => {
116 #[test]
117 fn $name() {
118 use $crate::e2e::{HarnessResult, TestHarnessBuilder};
119
120 fn run_test() -> HarnessResult<()> {
121 let harness = TestHarnessBuilder::new(stringify!($name))
122 .cleanup_on_success(true)
123 .cleanup_on_failure(false)
124 .build()?;
125
126 let body: fn(&$crate::e2e::TestHarness) -> HarnessResult<()> = $body;
127 let result = body(&harness);
128
129 if result.is_ok() {
130 harness.mark_passed();
131 }
132
133 result
134 }
135
136 if let Err(e) = run_test() {
137 panic!("E2E test failed: {}", e);
138 }
139 }
140 };
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146 use std::time::Duration;
147
148 #[test]
149 fn test_e2e_infrastructure_integration() {
150 let logger = TestLoggerBuilder::new("integration_test")
152 .print_realtime(false)
153 .build();
154
155 logger.info("Starting integration test");
156
157 let harness = TestHarnessBuilder::new("integration_test")
159 .cleanup_on_success(true)
160 .build()
161 .unwrap();
162
163 let project = RustProjectFixture::minimal("test-proj");
165 let project_dir = harness.create_dir("project").unwrap();
166 project.create_in(&project_dir).unwrap();
167
168 assert!(project_dir.join("Cargo.toml").exists());
170 assert!(project_dir.join("src/main.rs").exists());
171
172 let workers = WorkersFixture::mock_local(1);
174 let workers_toml = workers.to_toml();
175 assert!(workers_toml.contains("worker1"));
176
177 let socket_path = harness.test_dir().join("rch.sock");
179 let daemon_config = DaemonConfigFixture::minimal(&socket_path);
180 let daemon_toml = daemon_config.to_toml();
181 assert!(daemon_toml.contains("confidence_threshold"));
182
183 let hook_input = HookInputFixture::cargo_build();
185 let json = hook_input.to_json();
186 assert!(json.contains("cargo build"));
187
188 let result = harness.exec("echo", ["hello"]).unwrap();
190 harness.assert_success(&result, "echo").unwrap();
191 harness
192 .assert_stdout_contains(&result, "hello", "echo output")
193 .unwrap();
194
195 logger.info("Integration test completed");
196 harness.mark_passed();
197 }
198
199 #[test]
200 fn test_logger_standalone() {
201 let logger = TestLoggerBuilder::new("logger_test")
202 .print_realtime(false)
203 .min_level(LogLevel::Debug)
204 .max_entries(100)
205 .build();
206
207 logger.debug("Debug message");
208 logger.info("Info message");
209 logger.warn("Warning message");
210 logger.error("Error message");
211
212 let entries = logger.entries();
213 assert_eq!(entries.len(), 4);
214
215 let errors = logger.entries_by_level(LogLevel::Error);
216 assert_eq!(errors.len(), 1);
217
218 let summary = logger.summary();
219 assert_eq!(summary.total_entries, 4);
220 assert_eq!(*summary.counts_by_level.get(&LogLevel::Error).unwrap(), 1);
221 }
222
223 #[test]
224 fn test_harness_wait_for() {
225 let harness = TestHarnessBuilder::new("wait_test")
226 .cleanup_on_success(true)
227 .build()
228 .unwrap();
229
230 let test_file = harness.test_dir().join("marker.txt");
232
233 let file_path = test_file.clone();
235 std::thread::spawn(move || {
236 std::thread::sleep(Duration::from_millis(100));
237 std::fs::write(&file_path, "created").unwrap();
238 });
239
240 harness
242 .wait_for_file(&test_file, Duration::from_secs(2))
243 .unwrap();
244
245 assert!(test_file.exists());
246 harness.mark_passed();
247 }
248}