use crate::harness::ExecutionPolicy;
use crate::{RunContext, RunMetadata};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use super::command::{CommandExecutor, CommandOutput, CommandRequest, CommandStatus};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TestRunRequest {
pub command: CommandRequest,
pub name: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct VerificationResult {
pub name: String,
pub passed: bool,
pub summary: String,
pub output: Option<CommandOutput>,
pub metadata: RunMetadata,
}
#[async_trait]
pub trait TestRunner: Send + Sync {
async fn run(
&self,
request: TestRunRequest,
policy: &ExecutionPolicy,
context: &RunContext,
) -> Result<VerificationResult, TestRunError>;
}
#[derive(Debug, Clone)]
pub struct CommandTestRunner<C> {
commands: C,
}
impl<C> CommandTestRunner<C> {
pub fn new(commands: C) -> Self {
Self { commands }
}
}
#[async_trait]
impl<C> TestRunner for CommandTestRunner<C>
where
C: CommandExecutor,
{
async fn run(
&self,
request: TestRunRequest,
policy: &ExecutionPolicy,
context: &RunContext,
) -> Result<VerificationResult, TestRunError> {
let output = self
.commands
.execute(request.command, policy, context)
.await
.map_err(|error| TestRunError::Command {
message: error.to_string(),
})?;
let passed = matches!(output.status, CommandStatus::Exited { code: 0 });
let summary = if passed {
format!("{} passed", request.name)
} else {
format!("{} failed: {:?}", request.name, output.status)
};
Ok(VerificationResult {
name: request.name,
passed,
summary,
output: Some(output),
metadata: RunMetadata::new(),
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, Serialize, Deserialize)]
#[non_exhaustive]
pub enum TestRunError {
#[error("test command error: {message}")]
Command {
message: String,
},
}