mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-06 08:18:58 +00:00
Add 8 integration tests: HTTP lifecycle, SSE events, sub-pipeline, manager loop, graph merge, real LLM
- Change server registry_factory to accept Arc<dyn Interviewer> so WaitHumanHandler shares the same WebInterviewer as the REST API endpoints - Add full HTTP lifecycle tests: approve-and-complete flow + cancel flow - Add SSE event stream content parsing test with frame-level verification - Add sub-pipeline E2E test through the engine with context propagation - Add manager loop E2E test with SimulatingChildObserver - Add graph merge E2E test verifying module prefixing and execution ordering - Add 3 real LLM tests (#[ignore]) using claude-haiku via AnthropicAdapter - Add dotenvy and http-body-util dev dependencies Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a6d5579d70
commit
08d76b93de
4 changed files with 1141 additions and 5 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -136,7 +136,9 @@ dependencies = [
|
|||
"axum",
|
||||
"chrono",
|
||||
"coding-agent-loop",
|
||||
"dotenvy",
|
||||
"futures",
|
||||
"http-body-util",
|
||||
"nom",
|
||||
"rand",
|
||||
"serde",
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ tempfile = "3"
|
|||
axum = "0.8"
|
||||
tower = "0.5"
|
||||
tokio-stream = { workspace = true, features = ["sync"] }
|
||||
http-body-util = "0.1"
|
||||
dotenvy.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use crate::engine::{PipelineEngine, RunConfig};
|
|||
use crate::event::{EventEmitter, PipelineEvent};
|
||||
use crate::handler::HandlerRegistry;
|
||||
use crate::interviewer::web::WebInterviewer;
|
||||
use crate::interviewer::{Answer, AnswerValue};
|
||||
use crate::interviewer::{Answer, AnswerValue, Interviewer};
|
||||
|
||||
/// Status of a managed pipeline.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
|
|
@ -52,7 +52,7 @@ struct ManagedPipeline {
|
|||
/// Shared application state for the server.
|
||||
pub struct AppState {
|
||||
pipelines: Mutex<HashMap<String, ManagedPipeline>>,
|
||||
registry_factory: Box<dyn Fn() -> HandlerRegistry + Send + Sync>,
|
||||
registry_factory: Box<dyn Fn(Arc<dyn Interviewer>) -> HandlerRegistry + Send + Sync>,
|
||||
}
|
||||
|
||||
/// Request body for POST /pipelines.
|
||||
|
|
@ -106,8 +106,11 @@ pub fn build_router(state: Arc<AppState>) -> Router {
|
|||
}
|
||||
|
||||
/// Create an `AppState` with the given registry factory.
|
||||
///
|
||||
/// The factory receives the pipeline's `WebInterviewer` so it can wire it
|
||||
/// into handlers that need human-in-the-loop interaction (e.g., `WaitHumanHandler`).
|
||||
pub fn create_app_state(
|
||||
registry_factory: impl Fn() -> HandlerRegistry + Send + Sync + 'static,
|
||||
registry_factory: impl Fn(Arc<dyn Interviewer>) -> HandlerRegistry + Send + Sync + 'static,
|
||||
) -> Arc<AppState> {
|
||||
Arc::new(AppState {
|
||||
pipelines: Mutex::new(HashMap::new()),
|
||||
|
|
@ -142,7 +145,7 @@ async fn start_pipeline(
|
|||
let _ = tx_clone.send(event.clone());
|
||||
});
|
||||
|
||||
let registry = (state.registry_factory)();
|
||||
let registry = (state.registry_factory)(Arc::clone(&interviewer) as Arc<dyn Interviewer>);
|
||||
let engine = PipelineEngine::new(registry, emitter);
|
||||
|
||||
{
|
||||
|
|
@ -360,7 +363,7 @@ mod tests {
|
|||
start -> exit
|
||||
}"#;
|
||||
|
||||
fn test_registry() -> HandlerRegistry {
|
||||
fn test_registry(_interviewer: Arc<dyn crate::interviewer::Interviewer>) -> HandlerRegistry {
|
||||
let mut registry = HandlerRegistry::new(Box::new(StartHandler));
|
||||
registry.register("start", Box::new(StartHandler));
|
||||
registry.register("exit", Box::new(ExitHandler));
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue