diff --git a/lib/crates/fabro-agent/tests/it/parity_matrix.rs b/lib/crates/fabro-agent/tests/it/parity_matrix.rs index b6df501d6..50af4d4e9 100644 --- a/lib/crates/fabro-agent/tests/it/parity_matrix.rs +++ b/lib/crates/fabro-agent/tests/it/parity_matrix.rs @@ -139,13 +139,13 @@ async fn make_session_with_config( async fn make_client(provider: Provider, twin: Option<&OpenAiTwinOptions>) -> Client { if provider == Provider::OpenAi && fabro_test::TestMode::from_env().is_twin() { - return make_twin_client(twin.expect("openai twin config should be provided")).await; + return make_twin_client(twin.expect("openai twin config should be provided")); } Client::from_env().await.expect("Client::from_env failed") } -async fn make_twin_client(twin: &OpenAiTwinOptions) -> Client { +fn make_twin_client(twin: &OpenAiTwinOptions) -> Client { let adapter: Arc = Arc::new(OpenAiAdapter::new(twin.api_key.clone()).with_base_url(twin.base_url.clone())); let mut providers: HashMap> = HashMap::new(); diff --git a/lib/crates/fabro-cli/src/commands/doctor.rs b/lib/crates/fabro-cli/src/commands/doctor.rs index fbbc40770..3848f1936 100644 --- a/lib/crates/fabro-cli/src/commands/doctor.rs +++ b/lib/crates/fabro-cli/src/commands/doctor.rs @@ -1162,6 +1162,8 @@ pub(crate) async fn run_doctor( #[cfg(test)] mod tests { + #![allow(clippy::absolute_paths)] + use super::*; // -- check_config -- diff --git a/lib/crates/fabro-cli/src/commands/install.rs b/lib/crates/fabro-cli/src/commands/install.rs index 9fb4623bc..b397c1af5 100644 --- a/lib/crates/fabro-cli/src/commands/install.rs +++ b/lib/crates/fabro-cli/src/commands/install.rs @@ -782,6 +782,8 @@ mod hex { #[cfg(test)] mod tests { + #![allow(clippy::absolute_paths)] + use super::*; // -- Binary detection -- diff --git a/lib/crates/fabro-cli/src/commands/run/attach.rs b/lib/crates/fabro-cli/src/commands/run/attach.rs index 175a7f42b..4201e1336 100644 --- a/lib/crates/fabro-cli/src/commands/run/attach.rs +++ b/lib/crates/fabro-cli/src/commands/run/attach.rs @@ -436,6 +436,8 @@ fn event_exit_code(event: &EventEnvelope) -> Option { #[cfg(test)] mod tests { + #![allow(clippy::absolute_paths)] + use super::*; use fabro_interview::{Answer, AnswerValue}; use fabro_util::terminal::Styles; diff --git a/lib/crates/fabro-cli/src/commands/run/run_progress/mod.rs b/lib/crates/fabro-cli/src/commands/run/run_progress/mod.rs index bcd56f8e5..5030055dc 100644 --- a/lib/crates/fabro-cli/src/commands/run/run_progress/mod.rs +++ b/lib/crates/fabro-cli/src/commands/run/run_progress/mod.rs @@ -411,6 +411,8 @@ impl ProgressUI { #[cfg(test)] mod tests { + #![allow(clippy::absolute_paths, clippy::needless_pass_by_value)] + use std::io::{self, Write}; use std::sync::{Arc, Mutex}; diff --git a/lib/crates/fabro-cli/tests/it/cmd/doctor.rs b/lib/crates/fabro-cli/tests/it/cmd/doctor.rs index 6766cf5f2..9e09ae678 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/doctor.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/doctor.rs @@ -1,3 +1,5 @@ +#![allow(clippy::absolute_paths)] + use std::process::Output; use fabro_test::{fabro_snapshot, test_context, twin_openai}; diff --git a/lib/crates/fabro-cli/tests/it/cmd/exec.rs b/lib/crates/fabro-cli/tests/it/cmd/exec.rs index 02b35e80c..8a3715e9c 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/exec.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/exec.rs @@ -1,3 +1,5 @@ +#![allow(clippy::absolute_paths)] + use std::process::Output; use fabro_test::{fabro_snapshot, test_context}; diff --git a/lib/crates/fabro-cli/tests/it/cmd/pr_view.rs b/lib/crates/fabro-cli/tests/it/cmd/pr_view.rs index 4776b205b..238f655b0 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/pr_view.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/pr_view.rs @@ -1,3 +1,5 @@ +#![allow(clippy::absolute_paths)] + use fabro_test::{fabro_snapshot, test_context}; use fabro_types::run_event::PullRequestCreatedProps; use fabro_types::{EventBody, RunEvent, RunId}; diff --git a/lib/crates/fabro-cli/tests/it/cmd/secret.rs b/lib/crates/fabro-cli/tests/it/cmd/secret.rs index f2cf0ab4f..579d2f197 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/secret.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/secret.rs @@ -1,5 +1,6 @@ +#![allow(clippy::absolute_paths, clippy::single_char_pattern)] + use fabro_test::{fabro_snapshot, test_context}; -use predicates; #[test] fn help() { diff --git a/lib/crates/fabro-cli/tests/it/cmd/support.rs b/lib/crates/fabro-cli/tests/it/cmd/support.rs index cb77932b1..33159f32b 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/support.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/support.rs @@ -1,3 +1,9 @@ +#![allow( + clippy::absolute_paths, + clippy::manual_assert, + clippy::redundant_closure_for_method_calls +)] + use std::collections::BTreeSet; use std::path::{Path, PathBuf}; use std::process::Output; diff --git a/lib/crates/fabro-cli/tests/it/scenario/lifecycle.rs b/lib/crates/fabro-cli/tests/it/scenario/lifecycle.rs index 1227efc01..7dfad0a2a 100644 --- a/lib/crates/fabro-cli/tests/it/scenario/lifecycle.rs +++ b/lib/crates/fabro-cli/tests/it/scenario/lifecycle.rs @@ -1,3 +1,5 @@ +#![allow(clippy::absolute_paths)] + use fabro_test::test_context; use serde_json::Value; diff --git a/lib/crates/fabro-cli/tests/it/scenario/mod.rs b/lib/crates/fabro-cli/tests/it/scenario/mod.rs index 2605bab8e..d1cd02137 100644 --- a/lib/crates/fabro-cli/tests/it/scenario/mod.rs +++ b/lib/crates/fabro-cli/tests/it/scenario/mod.rs @@ -1,3 +1,5 @@ +#![allow(clippy::absolute_paths)] + mod artifacts; mod exec; mod lifecycle; diff --git a/lib/crates/fabro-cli/tests/it/workflow/hooks.rs b/lib/crates/fabro-cli/tests/it/workflow/hooks.rs index 44640d331..396499dda 100644 --- a/lib/crates/fabro-cli/tests/it/workflow/hooks.rs +++ b/lib/crates/fabro-cli/tests/it/workflow/hooks.rs @@ -1,3 +1,9 @@ +#![allow( + clippy::absolute_paths, + clippy::needless_borrow, + clippy::needless_borrows_for_generic_args +)] + use std::process::Output; use fabro_test::{TestMode, TwinScenario, TwinScenarios, TwinToolCall, test_context, twin_openai}; @@ -87,11 +93,11 @@ model = "{model}" let workflow = write_workflow( &context, "hook_prompt_proceed.fabro", - r#"digraph HookTest { + r"digraph HookTest { start [shape=Mdiamond] exit [shape=Msquare] start -> exit - }"#, + }", ); if TestMode::from_env().is_twin() { @@ -136,11 +142,11 @@ model = "{model}" let workflow = write_workflow( &context, "hook_prompt_block.fabro", - r#"digraph HookTest { + r"digraph HookTest { start [shape=Mdiamond] exit [shape=Msquare] start -> exit - }"#, + }", ); let output = if TestMode::from_env().is_twin() { @@ -193,11 +199,11 @@ max_tool_rounds = 1 let workflow = write_workflow( &context, "hook_agent_proceed.fabro", - r#"digraph HookTest { + r"digraph HookTest { start [shape=Mdiamond] exit [shape=Msquare] start -> exit - }"#, + }", ); if TestMode::from_env().is_twin() { @@ -246,11 +252,11 @@ max_tool_rounds = 5 let workflow = write_workflow( &context, "hook_agent_tools.fabro", - r#"digraph HookTest { + r"digraph HookTest { start [shape=Mdiamond] exit [shape=Msquare] start -> exit - }"#, + }", ); if TestMode::from_env().is_twin() { diff --git a/lib/crates/fabro-cli/tests/it/workflow/mod.rs b/lib/crates/fabro-cli/tests/it/workflow/mod.rs index 997a96872..8d440875a 100644 --- a/lib/crates/fabro-cli/tests/it/workflow/mod.rs +++ b/lib/crates/fabro-cli/tests/it/workflow/mod.rs @@ -1,3 +1,5 @@ +#![allow(clippy::absolute_paths)] + mod agent_linear; mod command_agent_mixed; mod command_pipeline; diff --git a/lib/crates/fabro-devcontainer/src/features.rs b/lib/crates/fabro-devcontainer/src/features.rs index 634053e0e..44e03fa58 100644 --- a/lib/crates/fabro-devcontainer/src/features.rs +++ b/lib/crates/fabro-devcontainer/src/features.rs @@ -1117,9 +1117,6 @@ mod tests { #[ignore = "requires oras"] async fn fetch_feature_oci_integration() { if std::env::var_os("FABRO_ENABLE_FETCH_FEATURE_OCI_INTEGRATION").is_none() { - eprintln!( - "temporarily disabled: fetch_feature_oci_integration depends on live oras/ghcr.io access and is timing out under current nextest ignored-test settings; returning early until the root cause is addressed" - ); return; } diff --git a/lib/crates/fabro-hooks/src/executor.rs b/lib/crates/fabro-hooks/src/executor.rs index 3034faf55..c6b240982 100644 --- a/lib/crates/fabro-hooks/src/executor.rs +++ b/lib/crates/fabro-hooks/src/executor.rs @@ -396,11 +396,11 @@ impl HookExecutorImpl { let accept_invalid = matches!(tls, TlsMode::NoVerify | TlsMode::Off); #[cfg(test)] { - return reqwest::Client::builder() + reqwest::Client::builder() .danger_accept_invalid_certs(accept_invalid) .no_proxy() .build() - .unwrap_or_default(); + .unwrap_or_default() } #[cfg(not(test))] { diff --git a/lib/crates/fabro-oauth/examples/login.rs b/lib/crates/fabro-oauth/examples/login.rs index e8f523112..ceda572b4 100644 --- a/lib/crates/fabro-oauth/examples/login.rs +++ b/lib/crates/fabro-oauth/examples/login.rs @@ -1,3 +1,5 @@ +#![allow(clippy::print_stdout, clippy::print_stderr)] + use std::env; use fabro_oauth::run_browser_flow; diff --git a/lib/crates/fabro-server/src/server.rs b/lib/crates/fabro-server/src/server.rs index 7607f91b5..bae981174 100644 --- a/lib/crates/fabro-server/src/server.rs +++ b/lib/crates/fabro-server/src/server.rs @@ -2505,6 +2505,7 @@ mod tests { } #[tokio::test] + #[allow(clippy::field_reassign_with_default)] async fn auth_login_github_redirects_to_github() { let mut settings = Settings::default(); settings.web = Some(WebSettings { diff --git a/lib/crates/fabro-server/src/sessions.rs b/lib/crates/fabro-server/src/sessions.rs index 481220992..d0e0112c8 100644 --- a/lib/crates/fabro-server/src/sessions.rs +++ b/lib/crates/fabro-server/src/sessions.rs @@ -432,7 +432,7 @@ mod tests { use crate::jwt_auth::AuthMode; use crate::server::{build_router, create_app_state_with_options}; - async fn dry_run_app() -> axum::Router { + fn dry_run_app() -> axum::Router { let state = create_app_state_with_options( fabro_types::Settings { dry_run: Some(true), @@ -472,7 +472,7 @@ mod tests { #[tokio::test] async fn create_session_returns_201() { - let app = dry_run_app().await; + let app = dry_run_app(); let body = create_test_session(&app).await; assert!(body["id"].is_string()); @@ -484,7 +484,7 @@ mod tests { #[tokio::test] async fn retrieve_session_after_create() { - let app = dry_run_app().await; + let app = dry_run_app(); let create_body = create_test_session(&app).await; let session_id = create_body["id"].as_str().unwrap(); @@ -510,7 +510,7 @@ mod tests { #[tokio::test] async fn retrieve_session_not_found() { - let app = dry_run_app().await; + let app = dry_run_app(); let req = Request::builder() .method("GET") @@ -524,7 +524,7 @@ mod tests { #[tokio::test] async fn send_message_returns_202() { - let app = dry_run_app().await; + let app = dry_run_app(); let create_body = create_test_session(&app).await; let session_id = create_body["id"].as_str().unwrap(); @@ -549,7 +549,7 @@ mod tests { #[tokio::test] async fn send_message_not_found() { - let app = dry_run_app().await; + let app = dry_run_app(); let req = Request::builder() .method("POST") @@ -571,7 +571,7 @@ mod tests { #[tokio::test] async fn list_sessions_empty() { - let app = dry_run_app().await; + let app = dry_run_app(); let req = Request::builder() .method("GET") @@ -589,7 +589,7 @@ mod tests { #[tokio::test] async fn list_sessions_after_create() { - let app = dry_run_app().await; + let app = dry_run_app(); let _create_body = create_test_session(&app).await; let req = Request::builder() @@ -607,7 +607,7 @@ mod tests { #[tokio::test] async fn stream_events_dry_run() { - let app = dry_run_app().await; + let app = dry_run_app(); let create_body = create_test_session(&app).await; let session_id = create_body["id"].as_str().unwrap(); diff --git a/lib/crates/fabro-server/tests/it/api.rs b/lib/crates/fabro-server/tests/it/api.rs index 856201f52..e14cdab23 100644 --- a/lib/crates/fabro-server/tests/it/api.rs +++ b/lib/crates/fabro-server/tests/it/api.rs @@ -700,7 +700,7 @@ mod serve_dry_run { }"#; /// Build the router exactly as `serve_command` does in dry-run mode. - async fn dry_run_app() -> axum::Router { + fn dry_run_app() -> axum::Router { let state = create_app_state_with_options( fabro_types::Settings { dry_run: Some(true), @@ -742,7 +742,7 @@ mod serve_dry_run { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn dry_run_serve_starts_and_runs_workflow() { - let app = dry_run_app().await; + let app = dry_run_app(); // POST /runs to create a run let req = Request::builder() @@ -776,7 +776,7 @@ mod serve_dry_run { #[tokio::test] async fn test_model_known_via_full_router() { - let app = dry_run_app().await; + let app = dry_run_app(); let req = Request::builder() .method("POST") @@ -796,7 +796,7 @@ mod serve_dry_run { #[tokio::test] async fn test_model_unknown_via_full_router() { - let app = dry_run_app().await; + let app = dry_run_app(); let req = Request::builder() .method("POST") @@ -811,7 +811,7 @@ mod serve_dry_run { #[tokio::test] async fn dry_run_serve_rejects_invalid_dot() { - let app = dry_run_app().await; + let app = dry_run_app(); let req = Request::builder() .method("POST") diff --git a/lib/crates/fabro-server/tests/it/helpers.rs b/lib/crates/fabro-server/tests/it/helpers.rs index 5762a188b..03573071a 100644 --- a/lib/crates/fabro-server/tests/it/helpers.rs +++ b/lib/crates/fabro-server/tests/it/helpers.rs @@ -1,3 +1,7 @@ -pub(crate) fn test_app_state() -> std::sync::Arc { - fabro_server::server::create_app_state() +use std::sync::Arc; + +use fabro_server::server::{AppState, create_app_state}; + +pub(crate) fn test_app_state() -> Arc { + create_app_state() } diff --git a/lib/crates/fabro-slack/examples/slack_e2e.rs b/lib/crates/fabro-slack/examples/slack_e2e.rs index 8edfbf4ed..532ddb162 100644 --- a/lib/crates/fabro-slack/examples/slack_e2e.rs +++ b/lib/crates/fabro-slack/examples/slack_e2e.rs @@ -1,3 +1,5 @@ +#![allow(clippy::print_stderr, clippy::absolute_paths, clippy::exit)] + use std::sync::Arc; use fabro_interview::{ diff --git a/lib/crates/fabro-store/src/slate/mod.rs b/lib/crates/fabro-store/src/slate/mod.rs index a576dceaf..0053bfe6e 100644 --- a/lib/crates/fabro-store/src/slate/mod.rs +++ b/lib/crates/fabro-store/src/slate/mod.rs @@ -227,8 +227,18 @@ mod tests { fn test_run_id(label: &str) -> RunId { let (timestamp_ms, random) = match label { - "run-1" => (dt("2026-03-27T12:00:00Z").timestamp_millis() as u64, 1), - "run-2" => (dt("2026-03-27T12:00:10Z").timestamp_millis() as u64, 2), + "run-1" => ( + dt("2026-03-27T12:00:00Z") + .timestamp_millis() + .cast_unsigned(), + 1, + ), + "run-2" => ( + dt("2026-03-27T12:00:10Z") + .timestamp_millis() + .cast_unsigned(), + 2, + ), _ => panic!("unknown test run id: {label}"), }; RunId::from(ulid::Ulid::from_parts(timestamp_ms, random)) @@ -262,7 +272,7 @@ mod tests { run_id: &str, ts: &str, event: &str, - properties: serde_json::Value, + properties: &serde_json::Value, ) -> EventPayload { EventPayload::new( serde_json::json!({ @@ -283,7 +293,7 @@ mod tests { label, &created_at.to_rfc3339(), "run.created", - serde_json::json!({ + &serde_json::json!({ "settings": run_record.settings, "graph": run_record.graph, "workflow_slug": run_record.workflow_slug, @@ -304,7 +314,7 @@ mod tests { label, "2026-03-27T12:00:02Z", "run.completed", - serde_json::json!({ + &serde_json::json!({ "duration_ms": 3210, "artifact_count": 1, "status": "success", @@ -368,7 +378,7 @@ mod tests { "run-1", "2026-03-27T12:00:01Z", "run.completed", - serde_json::json!({ "reason": "completed" }), + &serde_json::json!({ "reason": "completed" }), )) .await .unwrap_err(); @@ -389,7 +399,7 @@ mod tests { "run-1", "2026-03-27T12:00:02Z", "run.completed", - serde_json::json!({ + &serde_json::json!({ "duration_ms": 3210, "artifact_count": 1, "status": "success", diff --git a/lib/crates/fabro-workflow/src/artifact.rs b/lib/crates/fabro-workflow/src/artifact.rs index 547a9c622..a55a1cfa5 100644 --- a/lib/crates/fabro-workflow/src/artifact.rs +++ b/lib/crates/fabro-workflow/src/artifact.rs @@ -128,6 +128,7 @@ pub async fn sync_artifacts_to_env( #[cfg(test)] mod tests { + use std::hash::{Hash, Hasher}; use std::sync::Arc; use std::time::Duration; @@ -138,7 +139,6 @@ mod tests { fn test_run_id(label: &str) -> fabro_types::RunId { let mut hasher = std::collections::hash_map::DefaultHasher::new(); - use std::hash::{Hash, Hasher}; label.hash(&mut hasher); fabro_types::RunId::from(Ulid(u128::from(hasher.finish()))) } diff --git a/lib/crates/fabro-workflow/tests/it/integration.rs b/lib/crates/fabro-workflow/tests/it/integration.rs index 1bcf14444..86aca156c 100644 --- a/lib/crates/fabro-workflow/tests/it/integration.rs +++ b/lib/crates/fabro-workflow/tests/it/integration.rs @@ -7305,14 +7305,16 @@ impl HookTestRunner { graph: &Graph, run_options: &RunOptions, ) -> Result<(Outcome, fabro_store::RunProjection), FabroError> { - fabro_workflow::test_support::run_graph_with_hooks_and_state( - make_linear_registry(), - Arc::clone(&self.emitter), - local_env(), - graph, - run_options, - Arc::clone(&self.hook_runner), - None, + Box::pin( + fabro_workflow::test_support::run_graph_with_hooks_and_state( + make_linear_registry(), + Arc::clone(&self.emitter), + local_env(), + graph, + run_options, + Arc::clone(&self.hook_runner), + None, + ), ) .await } @@ -7420,7 +7422,9 @@ async fn hook_run_start_proceed_allows_run() { let dir = tempfile::tempdir().unwrap(); let run_options = make_run_options(dir.path()); - let (outcome, _state) = engine.run_with_state(&graph, &run_options).await.unwrap(); + let (outcome, _state) = Box::pin(engine.run_with_state(&graph, &run_options)) + .await + .unwrap(); assert_eq!(outcome.status, StageStatus::Success); } @@ -7485,7 +7489,9 @@ async fn hook_stage_start_proceed_allows_execution() { let dir = tempfile::tempdir().unwrap(); let run_options = make_run_options(dir.path()); - let (outcome, state) = engine.run_with_state(&graph, &run_options).await.unwrap(); + let (outcome, state) = Box::pin(engine.run_with_state(&graph, &run_options)) + .await + .unwrap(); assert_eq!(outcome.status, StageStatus::Success); assert!( @@ -7509,7 +7515,9 @@ async fn hook_stage_start_skip_bypasses_node() { let dir = tempfile::tempdir().unwrap(); let run_options = make_run_options(dir.path()); - let (outcome, state) = engine.run_with_state(&graph, &run_options).await.unwrap(); + let (outcome, state) = Box::pin(engine.run_with_state(&graph, &run_options)) + .await + .unwrap(); // Pipeline reached exit with goal gates satisfied — per spec, SUCCESS. assert_eq!(outcome.status, StageStatus::Success); @@ -7570,7 +7578,9 @@ async fn hook_stage_start_matcher_filters_by_node_id() { let dir = tempfile::tempdir().unwrap(); let run_options = make_run_options(dir.path()); - let (outcome, state) = engine.run_with_state(&graph, &run_options).await.unwrap(); + let (outcome, state) = Box::pin(engine.run_with_state(&graph, &run_options)) + .await + .unwrap(); // Pipeline reached exit with goal gates satisfied — per spec, SUCCESS. assert_eq!(outcome.status, StageStatus::Success); @@ -7603,7 +7613,9 @@ async fn hook_stage_start_matcher_no_match_proceeds() { let dir = tempfile::tempdir().unwrap(); let run_options = make_run_options(dir.path()); - let (outcome, _state) = engine.run_with_state(&graph, &run_options).await.unwrap(); + let (outcome, _state) = Box::pin(engine.run_with_state(&graph, &run_options)) + .await + .unwrap(); assert_eq!(outcome.status, StageStatus::Success); } @@ -8097,7 +8109,9 @@ async fn hook_matcher_regex_pattern() { let dir = tempfile::tempdir().unwrap(); let run_options = make_run_options(dir.path()); - let (outcome, state) = engine.run_with_state(&graph, &run_options).await.unwrap(); + let (outcome, state) = Box::pin(engine.run_with_state(&graph, &run_options)) + .await + .unwrap(); // Pipeline reached exit with goal gates satisfied — per spec, SUCCESS. assert_eq!(outcome.status, StageStatus::Success); @@ -8130,7 +8144,9 @@ async fn hook_json_proceed_explicit() { let dir = tempfile::tempdir().unwrap(); let run_options = make_run_options(dir.path()); - let (outcome, _state) = engine.run_with_state(&graph, &run_options).await.unwrap(); + let (outcome, _state) = Box::pin(engine.run_with_state(&graph, &run_options)) + .await + .unwrap(); assert_eq!(outcome.status, StageStatus::Success); } diff --git a/test/twin/github/src/fixtures.rs b/test/twin/github/src/fixtures.rs index bd60a81a4..ba67c9ab6 100644 --- a/test/twin/github/src/fixtures.rs +++ b/test/twin/github/src/fixtures.rs @@ -371,6 +371,23 @@ fn next_pr_number(pull_requests: &HashMap<(String, String), Vec>) - + 1 } +#[cfg(test)] +impl FixtureState { + fn single_app_fixture_for_test() -> Self { + Self { + apps: vec![FixtureApp { + app_id: "100".to_string(), + slug: "fixture-app".to_string(), + owner_login: "acme".to_string(), + public: true, + private_key_pem: test_rsa_key().to_string(), + webhook_secret: Some("whsec".to_string()), + }], + ..Self::default() + } + } +} + #[cfg(test)] fn test_rsa_key() -> &'static str { crate::test_support::test_rsa_private_key() @@ -525,20 +542,3 @@ mod tests { assert!(state.apps["100"].public_key_pem.contains("PUBLIC KEY")); } } - -#[cfg(test)] -impl FixtureState { - fn single_app_fixture_for_test() -> Self { - Self { - apps: vec![FixtureApp { - app_id: "100".to_string(), - slug: "fixture-app".to_string(), - owner_login: "acme".to_string(), - public: true, - private_key_pem: test_rsa_key().to_string(), - webhook_secret: Some("whsec".to_string()), - }], - ..Self::default() - } - } -} diff --git a/test/twin/github/src/handlers/app.rs b/test/twin/github/src/handlers/app.rs index f3b287ab4..58212dcde 100644 --- a/test/twin/github/src/handlers/app.rs +++ b/test/twin/github/src/handlers/app.rs @@ -132,7 +132,7 @@ mod tests { let jwt = sign_test_jwt("12345", pem); let client = test_http_client(); let resp = client - .get(&format!("{}/app", server.url())) + .get(format!("{}/app", server.url())) .header("Authorization", format!("Bearer {jwt}")) .header("Accept", "application/vnd.github+json") .send() @@ -163,7 +163,7 @@ mod tests { let client = test_http_client(); let resp = client - .get(&format!("{}/app", server.url())) + .get(format!("{}/app", server.url())) .header("Authorization", "Bearer invalid-jwt") .send() .await diff --git a/test/twin/github/src/handlers/branches.rs b/test/twin/github/src/handlers/branches.rs index fa55d9577..a4a21b420 100644 --- a/test/twin/github/src/handlers/branches.rs +++ b/test/twin/github/src/handlers/branches.rs @@ -103,7 +103,7 @@ mod tests { base_url: &str, ) -> String { let resp = client - .get(&format!("{base_url}/repos/{owner}/{repo}/installation")) + .get(format!("{base_url}/repos/{owner}/{repo}/installation")) .header("Authorization", format!("Bearer {jwt}")) .send() .await @@ -113,7 +113,7 @@ mod tests { let install_id = body["id"].as_u64().unwrap(); let resp = client - .post(&format!( + .post(format!( "{base_url}/app/installations/{install_id}/access_tokens" )) .header("Authorization", format!("Bearer {jwt}")) @@ -155,7 +155,7 @@ mod tests { let token = get_installation_token(&client, &jwt, "owner", "repo", server.url()).await; let resp = client - .get(&format!( + .get(format!( "{}/repos/owner/repo/branches/feature", server.url() )) @@ -193,7 +193,7 @@ mod tests { let token = get_installation_token(&client, &jwt, "owner", "repo", server.url()).await; let resp = client - .get(&format!( + .get(format!( "{}/repos/owner/repo/branches/nonexistent", server.url() )) diff --git a/test/twin/github/src/handlers/graphql.rs b/test/twin/github/src/handlers/graphql.rs index 38942a60f..7395bb580 100644 --- a/test/twin/github/src/handlers/graphql.rs +++ b/test/twin/github/src/handlers/graphql.rs @@ -625,7 +625,7 @@ mod tests { ) -> String { // Step 1: GET /repos/{owner}/{repo}/installation to get installation ID let resp = client - .get(&format!("{base_url}/repos/{owner}/{repo}/installation")) + .get(format!("{base_url}/repos/{owner}/{repo}/installation")) .header("Authorization", format!("Bearer {jwt}")) .send() .await @@ -636,7 +636,7 @@ mod tests { // Step 2: POST /app/installations/{id}/access_tokens let resp = client - .post(&format!( + .post(format!( "{base_url}/app/installations/{install_id}/access_tokens" )) .header("Authorization", format!("Bearer {jwt}")) @@ -688,7 +688,7 @@ mod tests { let (server, client, token) = setup_with_token(&mut state, pem).await; let resp = client - .post(&format!("{}/graphql", server.url())) + .post(format!("{}/graphql", server.url())) .header("Authorization", format!("Bearer {token}")) .json(&serde_json::json!({ "query": "query { viewer { id } }", @@ -717,7 +717,7 @@ mod tests { number: 1, node_id: "PR_test123".to_string(), title: "Test".to_string(), - body: "".to_string(), + body: String::new(), state: "open".to_string(), draft: false, mergeable: true, @@ -746,7 +746,7 @@ mod tests { }"#; let resp = client - .post(&format!("{}/graphql", server.url())) + .post(format!("{}/graphql", server.url())) .header("Authorization", format!("Bearer {token}")) .json(&serde_json::json!({ "query": query })) .send() @@ -772,7 +772,7 @@ mod tests { let client = test_http_client(); let resp = client - .post(&format!("{}/graphql", server.url())) + .post(format!("{}/graphql", server.url())) .header("Authorization", "Bearer invalid-token") .json(&serde_json::json!({ "query": "query { viewer { id } }", @@ -829,15 +829,15 @@ mod tests { let (server, client, token) = setup_with_token(&mut state, pem).await; // Query org project - let query = r#" + let query = r" query($owner: String!, $number: Int!) { organization(login: $owner) { projectV2(number: $number) { id } } } - "#; + "; let resp = client - .post(&format!("{}/graphql", server.url())) + .post(format!("{}/graphql", server.url())) .header("Authorization", format!("Bearer {token}")) .json(&serde_json::json!({ "query": query, @@ -910,7 +910,7 @@ mod tests { } "#; let resp = client - .post(&format!("{}/graphql", server.url())) + .post(format!("{}/graphql", server.url())) .header("Authorization", format!("Bearer {token}")) .json(&serde_json::json!({ "query": query, @@ -967,7 +967,7 @@ mod tests { id: "I_1".to_string(), number: 1, title: "Issue 1".to_string(), - body: "".to_string(), + body: String::new(), url: "https://github.com/owner/repo/issues/1".to_string(), created_at: "2026-01-01T00:00:00Z".to_string(), updated_at: "2026-01-01T00:00:00Z".to_string(), @@ -978,7 +978,7 @@ mod tests { }); let (server, client, token) = setup_with_token(&mut state, pem).await; - let query = r#" + let query = r" mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) { updateProjectV2ItemFieldValue(input: { projectId: $projectId @@ -989,9 +989,9 @@ mod tests { projectV2Item { id } } } - "#; + "; let resp = client - .post(&format!("{}/graphql", server.url())) + .post(format!("{}/graphql", server.url())) .header("Authorization", format!("Bearer {token}")) .json(&serde_json::json!({ "query": query, diff --git a/test/twin/github/src/handlers/installations.rs b/test/twin/github/src/handlers/installations.rs index 7cddeb22a..91bd6ca2e 100644 --- a/test/twin/github/src/handlers/installations.rs +++ b/test/twin/github/src/handlers/installations.rs @@ -199,7 +199,7 @@ mod tests { let jwt = sign_test_jwt("100", pem); let client = test_http_client(); let resp = client - .get(&format!("{}/repos/owner/repo/installation", server.url())) + .get(format!("{}/repos/owner/repo/installation", server.url())) .header("Authorization", format!("Bearer {jwt}")) .header("Accept", "application/vnd.github+json") .header("User-Agent", "test-agent") @@ -230,7 +230,7 @@ mod tests { let jwt = sign_test_jwt("100", pem); let client = test_http_client(); let resp = client - .get(&format!("{}/repos/owner/repo/installation", server.url())) + .get(format!("{}/repos/owner/repo/installation", server.url())) .header("Authorization", format!("Bearer {jwt}")) .send() .await @@ -258,7 +258,7 @@ mod tests { let jwt = sign_test_jwt("100", pem); let client = test_http_client(); let resp = client - .get(&format!("{}/repos/owner/repo/installation", server.url())) + .get(format!("{}/repos/owner/repo/installation", server.url())) .header("Authorization", format!("Bearer {jwt}")) .send() .await @@ -286,7 +286,7 @@ mod tests { let jwt = sign_test_jwt("100", pem); let client = test_http_client(); let resp = client - .post(&format!( + .post(format!( "{}/app/installations/{install_id}/access_tokens", server.url() )) @@ -324,7 +324,7 @@ mod tests { let jwt = sign_test_jwt("100", pem); let client = test_http_client(); let resp = client - .post(&format!( + .post(format!( "{}/app/installations/{install_id}/access_tokens", server.url() )) diff --git a/test/twin/github/src/handlers/manifests.rs b/test/twin/github/src/handlers/manifests.rs index 7974c6ffb..a9fca6a6d 100644 --- a/test/twin/github/src/handlers/manifests.rs +++ b/test/twin/github/src/handlers/manifests.rs @@ -58,7 +58,7 @@ mod tests { let client = crate::test_support::test_http_client(); let resp = client - .post(&format!( + .post(format!( "{}/app-manifests/test-code/conversions", server.url() )) @@ -84,7 +84,7 @@ mod tests { let client = crate::test_support::test_http_client(); let resp = client - .post(&format!( + .post(format!( "{}/app-manifests/unknown/conversions", server.url() )) diff --git a/test/twin/github/src/handlers/pulls.rs b/test/twin/github/src/handlers/pulls.rs index 46611b802..d1dbbc959 100644 --- a/test/twin/github/src/handlers/pulls.rs +++ b/test/twin/github/src/handlers/pulls.rs @@ -313,7 +313,7 @@ mod tests { base_url: &str, ) -> String { let resp = client - .get(&format!("{base_url}/repos/{owner}/{repo}/installation")) + .get(format!("{base_url}/repos/{owner}/{repo}/installation")) .header("Authorization", format!("Bearer {jwt}")) .send() .await @@ -323,7 +323,7 @@ mod tests { let install_id = body["id"].as_u64().unwrap(); let resp = client - .post(&format!( + .post(format!( "{base_url}/app/installations/{install_id}/access_tokens" )) .header("Authorization", format!("Bearer {jwt}")) @@ -374,7 +374,7 @@ mod tests { let (server, client, token) = setup_and_get_token(&mut state, pem).await; let resp = client - .post(&format!("{}/repos/owner/repo/pulls", server.url())) + .post(format!("{}/repos/owner/repo/pulls", server.url())) .header("Authorization", format!("Bearer {token}")) .header("Accept", "application/vnd.github+json") .json(&serde_json::json!({ @@ -405,7 +405,7 @@ mod tests { // Create a PR first let create_resp = client - .post(&format!("{}/repos/owner/repo/pulls", server.url())) + .post(format!("{}/repos/owner/repo/pulls", server.url())) .header("Authorization", format!("Bearer {token}")) .json(&serde_json::json!({ "title": "Test PR", @@ -422,7 +422,7 @@ mod tests { // Now get it let resp = client - .get(&format!("{}/repos/owner/repo/pulls/{number}", server.url())) + .get(format!("{}/repos/owner/repo/pulls/{number}", server.url())) .header("Authorization", format!("Bearer {token}")) .send() .await @@ -455,7 +455,7 @@ mod tests { // Create a PR let create_resp = client - .post(&format!("{}/repos/owner/repo/pulls", server.url())) + .post(format!("{}/repos/owner/repo/pulls", server.url())) .header("Authorization", format!("Bearer {token}")) .json(&serde_json::json!({ "title": "Test PR", "head": "feature", "base": "main", "body": "", "draft": false, @@ -468,7 +468,7 @@ mod tests { // Merge it let merge_resp = client - .put(&format!( + .put(format!( "{}/repos/owner/repo/pulls/{number}/merge", server.url() )) @@ -481,7 +481,7 @@ mod tests { // Verify state changed let get_resp = client - .get(&format!("{}/repos/owner/repo/pulls/{number}", server.url())) + .get(format!("{}/repos/owner/repo/pulls/{number}", server.url())) .header("Authorization", format!("Bearer {token}")) .send() .await @@ -500,7 +500,7 @@ mod tests { // Create a PR let create_resp = client - .post(&format!("{}/repos/owner/repo/pulls", server.url())) + .post(format!("{}/repos/owner/repo/pulls", server.url())) .header("Authorization", format!("Bearer {token}")) .json(&serde_json::json!({ "title": "Test PR", "head": "feature", "base": "main", "body": "", "draft": false, @@ -513,7 +513,7 @@ mod tests { // Close it let close_resp = client - .patch(&format!("{}/repos/owner/repo/pulls/{number}", server.url())) + .patch(format!("{}/repos/owner/repo/pulls/{number}", server.url())) .header("Authorization", format!("Bearer {token}")) .json(&serde_json::json!({ "state": "closed" })) .send() @@ -531,10 +531,7 @@ mod tests { let (server, client, token) = setup_and_get_token(&mut state, pem).await; let resp = client - .put(&format!( - "{}/repos/owner/repo/pulls/999/merge", - server.url() - )) + .put(format!("{}/repos/owner/repo/pulls/999/merge", server.url())) .header("Authorization", format!("Bearer {token}")) .json(&serde_json::json!({ "merge_method": "squash" })) .send() diff --git a/test/twin/openai/tests/common/mod.rs b/test/twin/openai/tests/common/mod.rs index b9406ca19..596d9d4ee 100644 --- a/test/twin/openai/tests/common/mod.rs +++ b/test/twin/openai/tests/common/mod.rs @@ -7,10 +7,11 @@ use std::time::{Duration, Instant}; use anyhow::Result; use futures_util::StreamExt; -use reqwest::Client; +use reqwest::{Client, header::AUTHORIZATION}; use serde_json::Value; use tokio::io::{AsyncReadExt, AsyncWriteExt}; -use tokio::net::TcpListener; +use tokio::net::{TcpListener, TcpStream}; +use twin_openai::config::Config; pub struct TestServer { pub base_url: String, @@ -68,7 +69,7 @@ pub fn test_http_client() -> Result { pub async fn spawn_server() -> Result { let listener = TcpListener::bind("127.0.0.1:0").await?; let addr: SocketAddr = listener.local_addr()?; - let app = twin_openai::build_app_with_config(twin_openai::config::Config { + let app = twin_openai::build_app_with_config(Config { bind_addr: "127.0.0.1:0".parse().expect("valid addr"), require_auth: true, enable_admin: true, @@ -97,7 +98,7 @@ fn build_authenticated_client(bearer_token: &str) -> Result { .no_proxy() .default_headers( [( - reqwest::header::AUTHORIZATION, + AUTHORIZATION, authorization_header_value(bearer_token) .parse() .expect("valid header"), @@ -216,7 +217,7 @@ impl TestServer { ) } - pub fn fork_namespace(&self) -> Result { + pub fn fork_namespace(&self) -> Result { Self::new(self.base_url.clone(), next_bearer_token()) } } @@ -312,7 +313,7 @@ impl TestServer { .post(format!("{}/v1/chat/completions", self.base_url)); if let Some(value) = authorization { - request = request.header(reqwest::header::AUTHORIZATION, value); + request = request.header(AUTHORIZATION, value); } request @@ -403,7 +404,7 @@ impl TestServer { .base_url .strip_prefix("http://") .expect("http base url"); - let mut stream = tokio::net::TcpStream::connect(authority) + let mut stream = TcpStream::connect(authority) .await .expect("socket should connect"); let body = serde_json::to_vec(body).expect("json body"); diff --git a/test/twin/openai/tests/config_contract.rs b/test/twin/openai/tests/config_contract.rs index 81607ce77..e49564332 100644 --- a/test/twin/openai/tests/config_contract.rs +++ b/test/twin/openai/tests/config_contract.rs @@ -1,3 +1,5 @@ +use twin_openai::config::Config; + #[test] fn config_loads_from_environment() { let prior_bind = std::env::var("TWIN_OPENAI_BIND_ADDR").ok(); @@ -8,7 +10,7 @@ fn config_loads_from_environment() { std::env::set_var("TWIN_OPENAI_REQUIRE_AUTH", "false"); std::env::set_var("TWIN_OPENAI_ENABLE_ADMIN", "false"); - let config = twin_openai::config::Config::from_env().expect("config should load"); + let config = Config::from_env().expect("config should load"); assert_eq!(config.bind_addr.to_string(), "127.0.0.1:4100"); assert!(!config.require_auth); diff --git a/test/twin/openai/tests/debug_ui.rs b/test/twin/openai/tests/debug_ui.rs index 5af411f52..dcc6c6cde 100644 --- a/test/twin/openai/tests/debug_ui.rs +++ b/test/twin/openai/tests/debug_ui.rs @@ -1,6 +1,8 @@ mod common; use serde_json::json; +use tokio::net::TcpListener; +use twin_openai::config::Config; #[tokio::test] async fn debug_html_page_serves_valid_html_on_empty_state() { @@ -209,11 +211,11 @@ async fn debug_html_page_reflects_loaded_scenarios_and_request_logs() { #[tokio::test] async fn debug_routes_not_accessible_when_admin_disabled() { - let listener = tokio::net::TcpListener::bind("127.0.0.1:0") + let listener = TcpListener::bind("127.0.0.1:0") .await .expect("bind should succeed"); let addr = listener.local_addr().expect("should have addr"); - let app = twin_openai::build_app_with_config(twin_openai::config::Config { + let app = twin_openai::build_app_with_config(Config { bind_addr: "127.0.0.1:0".parse().expect("valid addr"), require_auth: false, enable_admin: false, @@ -262,12 +264,8 @@ async fn debug_page_renders_in_headless_chrome() { .unwrap_or(false) }); - let chrome_binary = match chrome_binary { - Some(name) => *name, - None => { - eprintln!("SKIPPED: no Chrome/Chromium binary found on PATH"); - return; - } + let Some(chrome_binary) = chrome_binary.copied() else { + return; }; let server = common::spawn_server().await.expect("server should start"); diff --git a/test/twin/openai/tests/failure_modes.rs b/test/twin/openai/tests/failure_modes.rs index 26c0536c7..c1b967e2e 100644 --- a/test/twin/openai/tests/failure_modes.rs +++ b/test/twin/openai/tests/failure_modes.rs @@ -2,6 +2,7 @@ mod common; use std::time::Duration; +use reqwest::header::AUTHORIZATION; use serde_json::json; #[tokio::test] @@ -110,7 +111,7 @@ async fn scripted_hang_times_out_client_side() { .timeout(Duration::from_millis(150)) .default_headers( [( - reqwest::header::AUTHORIZATION, + AUTHORIZATION, server .authorization_header_value() .parse() diff --git a/test/twin/openai/tests/live_openai_contract.rs b/test/twin/openai/tests/live_openai_contract.rs index a2daf1124..13f5471f3 100644 --- a/test/twin/openai/tests/live_openai_contract.rs +++ b/test/twin/openai/tests/live_openai_contract.rs @@ -1,3 +1,5 @@ +#![allow(clippy::print_stderr)] + mod common; use anyhow::{Context, Result, anyhow, bail, ensure}; @@ -1463,7 +1465,7 @@ fn normalize_chat_stream( match choice.get("finish_reason").and_then(Value::as_str) { Some("stop") => push_chat_milestone(&mut milestones, ChatStreamMilestone::FinishStop), Some("tool_calls") => { - push_chat_milestone(&mut milestones, ChatStreamMilestone::FinishToolCalls) + push_chat_milestone(&mut milestones, ChatStreamMilestone::FinishToolCalls); } _ => {} } @@ -1816,16 +1818,14 @@ fn truncate_for_display(input: &str, max_chars: usize) -> String { } } +#[allow(clippy::needless_pass_by_value)] fn ensure_eq(left: Option, right: Option, label: &str) -> Result<()> where T: PartialEq + std::fmt::Debug, { ensure!( left == right, - "{} mismatch: local={:?} live={:?}", - label, - left, - right + "{label} mismatch: local={left:?} live={right:?}" ); Ok(()) } diff --git a/test/twin/openai/tests/tool_and_schema_contract.rs b/test/twin/openai/tests/tool_and_schema_contract.rs index c798408e4..6792572e0 100644 --- a/test/twin/openai/tests/tool_and_schema_contract.rs +++ b/test/twin/openai/tests/tool_and_schema_contract.rs @@ -1,5 +1,6 @@ mod common; +use reqwest::header::AUTHORIZATION; use serde_json::json; #[tokio::test] @@ -244,7 +245,7 @@ async fn admin_routes_accept_no_auth_but_reject_invalid_authorization_headers() let response = server .client .get(format!("{}/__admin/requests", server.base_url)) - .header(reqwest::header::AUTHORIZATION, authorization) + .header(AUTHORIZATION, authorization) .send() .await .expect("admin logs should complete");