#![allow(dead_code)]
use axum::{
extract::State,
http::{header, StatusCode},
response::IntoResponse,
routing::post,
Router,
};
use serde_json::json;
use std::sync::{
atomic::{AtomicUsize, Ordering},
Arc,
};
use tokio_util::sync::CancellationToken;
#[derive(Clone)]
pub struct MockState {
pub call_count: Arc<AtomicUsize>,
}
async fn chat_completions_handler(
State(state): State<MockState>,
body: axum::body::Bytes,
) -> impl IntoResponse {
if let Err(e) = serde_json::from_slice::<serde_json::Value>(&body) {
eprintln!("[compile_loop_openai_mock] failed to parse request body: {e}");
let err_body = json!({ "error": format!("bad request: {e}") }).to_string();
return (
StatusCode::BAD_REQUEST,
[(header::CONTENT_TYPE, "application/json")],
err_body,
);
}
let prev = state.call_count.fetch_add(1, Ordering::SeqCst);
let response_json = if prev == 0 {
json!({
"id": "chatcmpl-clmock-1",
"object": "chat.completion",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "```lua\nprint(\"broken\"; -- syntax err\n```"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 5,
"total_tokens": 15
}
})
} else {
json!({
"id": "chatcmpl-clmock-2",
"object": "chat.completion",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "```lua\nprint(\"fixed\")\n```"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 20,
"completion_tokens": 10,
"total_tokens": 30
}
})
};
(
StatusCode::OK,
[(header::CONTENT_TYPE, "application/json")],
response_json.to_string(),
)
}
pub async fn spawn_compile_loop_openai_mock_server() -> (String, Arc<AtomicUsize>, CancellationToken)
{
let call_count = Arc::new(AtomicUsize::new(0));
let ct = CancellationToken::new();
let state = MockState {
call_count: call_count.clone(),
};
let router = Router::new()
.route("/chat/completions", post(chat_completions_handler))
.with_state(state);
let listener = tokio::net::TcpListener::bind("127.0.0.1:0")
.await
.expect("bind ephemeral port for compile_loop openai mock");
let addr = listener.local_addr().expect("local_addr");
let ct_shutdown = ct.clone();
tokio::spawn(async move {
let _ = axum::serve(listener, router)
.with_graceful_shutdown(async move { ct_shutdown.cancelled_owned().await })
.await;
});
(format!("http://{addr}"), call_count, ct)
}