refactor(workspace): satisfy clippy all-targets warnings

This commit is contained in:
Bryan Helmkamp 2026-04-05 14:37:32 -04:00
parent 6af4943f4c
commit 420f82180e
No known key found for this signature in database
38 changed files with 201 additions and 136 deletions

View file

@ -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<dyn ProviderAdapter> =
Arc::new(OpenAiAdapter::new(twin.api_key.clone()).with_base_url(twin.base_url.clone()));
let mut providers: HashMap<String, Arc<dyn ProviderAdapter>> = HashMap::new();

View file

@ -1162,6 +1162,8 @@ pub(crate) async fn run_doctor(
#[cfg(test)]
mod tests {
#![allow(clippy::absolute_paths)]
use super::*;
// -- check_config --

View file

@ -782,6 +782,8 @@ mod hex {
#[cfg(test)]
mod tests {
#![allow(clippy::absolute_paths)]
use super::*;
// -- Binary detection --

View file

@ -436,6 +436,8 @@ fn event_exit_code(event: &EventEnvelope) -> Option<ExitCode> {
#[cfg(test)]
mod tests {
#![allow(clippy::absolute_paths)]
use super::*;
use fabro_interview::{Answer, AnswerValue};
use fabro_util::terminal::Styles;

View file

@ -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};

View file

@ -1,3 +1,5 @@
#![allow(clippy::absolute_paths)]
use std::process::Output;
use fabro_test::{fabro_snapshot, test_context, twin_openai};

View file

@ -1,3 +1,5 @@
#![allow(clippy::absolute_paths)]
use std::process::Output;
use fabro_test::{fabro_snapshot, test_context};

View file

@ -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};

View file

@ -1,5 +1,6 @@
#![allow(clippy::absolute_paths, clippy::single_char_pattern)]
use fabro_test::{fabro_snapshot, test_context};
use predicates;
#[test]
fn help() {

View file

@ -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;

View file

@ -1,3 +1,5 @@
#![allow(clippy::absolute_paths)]
use fabro_test::test_context;
use serde_json::Value;

View file

@ -1,3 +1,5 @@
#![allow(clippy::absolute_paths)]
mod artifacts;
mod exec;
mod lifecycle;

View file

@ -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() {

View file

@ -1,3 +1,5 @@
#![allow(clippy::absolute_paths)]
mod agent_linear;
mod command_agent_mixed;
mod command_pipeline;

View file

@ -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;
}

View file

@ -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))]
{

View file

@ -1,3 +1,5 @@
#![allow(clippy::print_stdout, clippy::print_stderr)]
use std::env;
use fabro_oauth::run_browser_flow;

View file

@ -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 {

View file

@ -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();

View file

@ -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")

View file

@ -1,3 +1,7 @@
pub(crate) fn test_app_state() -> std::sync::Arc<fabro_server::server::AppState> {
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<AppState> {
create_app_state()
}

View file

@ -1,3 +1,5 @@
#![allow(clippy::print_stderr, clippy::absolute_paths, clippy::exit)]
use std::sync::Arc;
use fabro_interview::{

View file

@ -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",

View file

@ -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())))
}

View file

@ -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);
}

View file

@ -371,6 +371,23 @@ fn next_pr_number(pull_requests: &HashMap<(String, String), Vec<PullRequest>>) -
+ 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()
}
}
}

View file

@ -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

View file

@ -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()
))

View file

@ -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,

View file

@ -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()
))

View file

@ -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()
))

View file

@ -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()

View file

@ -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<Client> {
pub async fn spawn_server() -> Result<TestServer> {
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<Client> {
.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<TestServer> {
pub fn fork_namespace(&self) -> Result<Self> {
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");

View file

@ -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);

View file

@ -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");

View file

@ -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()

View file

@ -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<T>(left: Option<T>, right: Option<T>, 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(())
}

View file

@ -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");