mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-01 02:04:24 +00:00
A workflow version names its engine with `engine = "petri"` in the `[workflow]` table of `workflow.toml`, and `[server.execution] engine` (`FABRO_SERVER_ENGINE`, `--engine`) defaults it for every version that names none. The choice, with what Petri admitted (the lowered root graph and its children by blob and digest), is recorded on the run spec as `RunEngine`, carried on `run.created`, and replayed into the projection. A legacy run's spec omits the field, so existing specs decode unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
124 lines
4 KiB
Rust
124 lines
4 KiB
Rust
use fabro_types::test_support;
|
||
mod auth_harness;
|
||
mod auth_tokens;
|
||
|
||
use assert_cmd::Command;
|
||
pub(crate) use auth_harness::{
|
||
RealAuthHarness, TEST_DEV_TOKEN, complete_login_via_browser, expire_saved_access_token,
|
||
no_redirect_browser_client, run_detached, saved_auth_entry, seed_dev_token_auth,
|
||
};
|
||
pub(crate) use auth_tokens::{TEST_SESSION_SECRET, issue_test_github_jwt, issue_test_worker_jwt};
|
||
use fabro_store::EventEnvelope;
|
||
use fabro_test::{EnvVars, TestContext, preserve_coverage_env};
|
||
use fabro_types::{Graph, RunId, RunSpec, WorkflowSettings};
|
||
|
||
pub(crate) fn run_output_filters(context: &TestContext) -> Vec<(String, String)> {
|
||
let mut filters = context.filters();
|
||
filters.push((r"\b\d+ms\b".to_string(), "[TIME]".to_string()));
|
||
filters.push((
|
||
r"(?m)^(Graph: ).+$".to_string(),
|
||
"${1}[GRAPH_PATH]".to_string(),
|
||
));
|
||
filters
|
||
}
|
||
|
||
pub(crate) fn fatal_error_line(stderr: &[u8]) -> String {
|
||
let stderr = String::from_utf8_lossy(stderr);
|
||
console::strip_ansi_codes(&stderr)
|
||
.lines()
|
||
.rev()
|
||
.find_map(|line| {
|
||
line.strip_prefix("error: ")
|
||
.or_else(|| line.trim_start().strip_prefix("× "))
|
||
.map(ToOwned::to_owned)
|
||
})
|
||
.expect("stderr should contain a fatal error line")
|
||
}
|
||
|
||
pub(crate) fn unique_run_id() -> String {
|
||
RunId::new().to_string()
|
||
}
|
||
|
||
pub(crate) fn run_projection_json(run_id: &str, status: &serde_json::Value) -> serde_json::Value {
|
||
let run_id = run_id.parse::<RunId>().expect("test run id should parse");
|
||
let spec = RunSpec {
|
||
run_id,
|
||
settings: WorkflowSettings::default(),
|
||
graph: Graph::new("Remote Workflow"),
|
||
graph_source: None,
|
||
workflow_slug: Some("remote-workflow".to_string()),
|
||
workflow_version_id: None,
|
||
target: None,
|
||
automation: None,
|
||
source_directory: Some("/srv/repo".to_string()),
|
||
labels: std::collections::HashMap::default(),
|
||
provenance: test_support::test_run_provenance(),
|
||
definition_blob: None,
|
||
spec_blob: None,
|
||
git: None,
|
||
fork_source_ref: None,
|
||
engine: fabro_types::RunEngine::Legacy,
|
||
};
|
||
|
||
serde_json::json!({
|
||
"spec": serde_json::to_value(spec).expect("run spec should serialize"),
|
||
"start": null,
|
||
"status": status,
|
||
"status_updated_at": "2026-04-05T12:00:01Z",
|
||
"last_event_at": "2026-04-05T12:00:01Z",
|
||
"pending_control": null,
|
||
"checkpoints": [],
|
||
"conclusion": null,
|
||
"sandbox": null,
|
||
"pull_request": null,
|
||
"superseded_by": null,
|
||
"pending_interviews": {},
|
||
"stages": {}
|
||
})
|
||
}
|
||
|
||
pub(crate) fn parse_event_envelopes(response: &serde_json::Value) -> Vec<EventEnvelope> {
|
||
response["data"]
|
||
.as_array()
|
||
.expect("event list response should contain a data array")
|
||
.iter()
|
||
.cloned()
|
||
.map(serde_json::from_value)
|
||
.collect::<Result<Vec<_>, _>>()
|
||
.expect("wire event envelope list should parse")
|
||
}
|
||
|
||
pub(crate) struct LightweightCli {
|
||
home_dir: tempfile::TempDir,
|
||
}
|
||
|
||
impl LightweightCli {
|
||
pub(crate) fn new() -> Self {
|
||
Self {
|
||
home_dir: tempfile::tempdir().expect("temp home dir should exist"),
|
||
}
|
||
}
|
||
|
||
pub(crate) fn home(&self) -> &std::path::Path {
|
||
self.home_dir.path()
|
||
}
|
||
|
||
#[expect(
|
||
clippy::disallowed_methods,
|
||
reason = "Lightweight CLI test harness reconstructs a minimal process env for subprocesses."
|
||
)]
|
||
pub(crate) fn command(&self) -> Command {
|
||
let mut cmd = Command::new(env!("CARGO_BIN_EXE_fabro"));
|
||
cmd.env_clear();
|
||
preserve_coverage_env!(cmd);
|
||
if let Some(path) = std::env::var_os(EnvVars::PATH) {
|
||
cmd.env(EnvVars::PATH, path);
|
||
}
|
||
cmd.env(EnvVars::HOME, self.home_dir.path());
|
||
cmd.env(EnvVars::NO_COLOR, "1");
|
||
cmd.env(EnvVars::FABRO_NO_UPGRADE_CHECK, "true")
|
||
.env(EnvVars::FABRO_HTTP_PROXY_POLICY, "disabled");
|
||
cmd.current_dir(self.home_dir.path());
|
||
cmd
|
||
}
|
||
}
|