use schemars::JsonSchema; use serde::Deserialize; use tracing::instrument; use crate::checks::ssh; use crate::db; use super::PomServer; #[derive(Debug, Deserialize, JsonSchema)] pub struct RunTestsParams { /// Target name to run tests on pub target: String, /// Optional filter to run specific tests pub filter: Option, } #[derive(Debug, Deserialize, JsonSchema)] pub struct TestHistoryParams { /// Filter by target name pub target: Option, /// Number of results to return (default 10) pub limit: Option, } #[derive(Debug, Deserialize, JsonSchema)] pub struct LastTestOutputParams { /// Target name to get output for pub target: String, } impl PomServer { #[instrument(skip_all)] pub async fn run_tests_impl( &self, params: RunTestsParams, ) -> crate::error::Result { let target = self.config.get_target(¶ms.target).ok_or_else(|| { crate::error::PomError::Config(format!("Unknown target: {}", params.target)) })?; let tests_config = target.tests.as_ref().ok_or_else(|| { crate::error::PomError::Config(format!( "Target '{}' has no test configuration", params.target )) })?; let run = ssh::run_tests(¶ms.target, tests_config, params.filter.as_deref()).await; let run_id = db::insert_test_run(&self.pool, &run).await?; // Store per-test details and detect regressions if !run.summary.details.is_empty() { db::insert_test_details(&self.pool, run_id, &run.summary.details).await?; } let regressions = db::get_test_regressions(&self.pool, ¶ms.target, run_id) .await .unwrap_or_default(); // Return summary without raw_output (it can be huge) let summary = serde_json::json!({ "target": run.target, "passed": run.passed, "exit_code": run.exit_code, "duration_secs": run.duration_secs, "started_at": run.started_at, "finished_at": run.finished_at, "filter": run.filter, "summary": run.summary, "regressions": regressions, }); Ok(serde_json::to_string_pretty(&summary)?) } #[instrument(skip_all)] pub async fn test_history_impl( &self, params: TestHistoryParams, ) -> crate::error::Result { let limit = params.limit.unwrap_or(10); let history = db::get_test_history(&self.pool, params.target.as_deref(), limit).await?; if history.is_empty() { return Ok("No test run history.".to_string()); } // Return without raw_output let summaries: Vec = history .iter() .map(|run| { serde_json::json!({ "id": run.id, "target": run.target, "passed": run.passed, "exit_code": run.exit_code, "duration_secs": run.duration_secs, "started_at": run.started_at, "finished_at": run.finished_at, "filter": run.filter, "summary": run.summary, }) }) .collect(); Ok(serde_json::to_string_pretty(&summaries)?) } #[instrument(skip_all)] pub async fn last_test_output_impl( &self, params: LastTestOutputParams, ) -> crate::error::Result { let run = db::get_latest_test_run(&self.pool, ¶ms.target).await?; match run { Some(r) => Ok(r.raw_output), None => Ok(format!("No test runs found for target '{}'", params.target)), } } }