mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-02 02:13:49 +00:00
Step 4 of the legacy executor deletion, third commit: the legacy event
API and every reader of it go, so that the next commits can delete the
event log, its reducer and the types beneath them.
The API:
- `GET /runs/{id}/events` pages the run stream only
(`PaginatedRunStreamList` by `after`); the legacy `since_seq`,
`before_seq` and `order` cursors, the `oneOf` envelope, the legacy
`EventEnvelope`, `PaginatedEventList`, `RunEvent`, `EventSeq`,
`AppendEventResponse` and `RunEventDetailResponse` schemas,
`POST /runs/{id}/events`, `GET /runs/{id}/events/{seq}` and
`GET /runs/{id}/stages/{stageId}/events` are deleted. `GET
/runs/{id}/attach` and `GET /attach` frame `RunStreamItem`s only.
- The Rust and TypeScript clients regenerate; the removed models leave
the TypeScript package.
The readers:
- `fabro-client` drops the legacy run event listing, tail and attach
methods and `RunEventStream`; `list_run_stream_until` bounds a stream
read.
- `fabro-tool`'s `fabro_run_events` lists, searches and details the run
stream: `after` is the exclusive `stream_seq` cursor, `event_id` the
item's id, filters match the item's name and `recorded_at`.
- `fabro-dump` writes the stream to `events.jsonl`; `fabro dump` reads
it.
- The CLI's progress renderer keeps only what the run stream drives:
the legacy event conversion, the sandbox and setup displays and their
styles go. `fabro system events` prints stream items.
- The server's demo mode folds its agent fixture straight into the
session projection and answers the attach stub with a stream item;
the demo stage events endpoint is gone.
- The web app: every run is a Petri run. The legacy event hooks,
renderer props, stage popover summary, run phases derivation and
live-event payload handling are deleted or ported to `RunStreamItem`;
toasts and board refreshes read the stream's platform records.
- Tests: the legacy API round trips and pagination tests are deleted;
the CLI's MCP, attach and system event mocks serve stream pages; the
CLI test helpers read stream items.
Still failing until the later commits: the CLI tests seeded through
`POST /runs/{id}/events`, the server tests over the legacy store, and
the legacy type tests.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
123 lines
4 KiB
Rust
123 lines
4 KiB
Rust
use fabro_test::{fabro_snapshot, test_context};
|
|
use httpmock::MockServer;
|
|
|
|
#[test]
|
|
fn help() {
|
|
let context = test_context!();
|
|
let mut cmd = context.command();
|
|
cmd.args(["system", "events", "--help"]);
|
|
fabro_snapshot!(context.filters(), cmd, @"
|
|
success: true
|
|
exit_code: 0
|
|
----- stdout -----
|
|
Stream run events from the server
|
|
|
|
Usage: fabro system events [OPTIONS]
|
|
|
|
Options:
|
|
--json Output as JSON [env: FABRO_JSON=]
|
|
--storage-dir <STORAGE_DIR> Local storage directory (default: ~/.fabro/storage) [env: FABRO_STORAGE_DIR=]
|
|
--debug Enable DEBUG-level logging (default is INFO) [env: FABRO_DEBUG=]
|
|
--server <SERVER> Fabro server target: http(s) URL or absolute Unix socket path [env: FABRO_SERVER=]
|
|
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
|
|
--run-id <RUN_IDS> Filter by run ID (repeatable)
|
|
--quiet Suppress non-essential output [env: FABRO_QUIET=]
|
|
--verbose Enable verbose output [env: FABRO_VERBOSE=]
|
|
-h, --help Print help
|
|
----- stderr -----
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn system_events_renders_text_lines_from_sse_payloads() {
|
|
let context = test_context!();
|
|
let server = MockServer::start();
|
|
let run_id = crate::support::unique_run_id();
|
|
// One `RunStreamItem`: the platform record that ended the run.
|
|
let payload = serde_json::json!({
|
|
"run_id": run_id,
|
|
"stream_seq": 7,
|
|
"kind": "platform",
|
|
"id": "7",
|
|
"recorded_at": 1_775_390_400_000_u64,
|
|
"item": {
|
|
"seq": 7,
|
|
"recorded_at": 1_775_390_400_000_u64,
|
|
"record": {
|
|
"kind": "run.lifecycle",
|
|
"transition": "succeeded",
|
|
"status": { "kind": "succeeded", "reason": "completed" }
|
|
}
|
|
}
|
|
});
|
|
let attach_mock = server.mock(|when, then| {
|
|
when.method("GET")
|
|
.path("/api/v1/attach")
|
|
.query_param("run_id", run_id.as_str());
|
|
then.status(200)
|
|
.header("Content-Type", "text/event-stream")
|
|
.body(format!("data: {payload}\n\n"));
|
|
});
|
|
|
|
let output = context
|
|
.command()
|
|
.args([
|
|
"system",
|
|
"events",
|
|
"--server",
|
|
&format!("{}/api/v1", server.base_url()),
|
|
"--run-id",
|
|
&run_id,
|
|
])
|
|
.output()
|
|
.expect("command should run");
|
|
|
|
assert!(output.status.success(), "system events failed");
|
|
let stdout = String::from_utf8(output.stdout).expect("stdout should be UTF-8");
|
|
assert_eq!(
|
|
stdout.trim(),
|
|
format!("2026-04-05T12:00:00+00:00 {} run.lifecycle", &run_id[..12])
|
|
);
|
|
attach_mock.assert();
|
|
}
|
|
|
|
#[test]
|
|
fn system_events_json_emits_raw_sse_payloads() {
|
|
let context = test_context!();
|
|
let server = MockServer::start();
|
|
let run_id = crate::support::unique_run_id();
|
|
let payload = serde_json::json!({
|
|
"payload": {
|
|
"ts": "2026-04-05T12:00:00Z",
|
|
"run_id": run_id,
|
|
"event": "run.completed",
|
|
}
|
|
});
|
|
let attach_mock = server.mock(|when, then| {
|
|
when.method("GET")
|
|
.path("/api/v1/attach")
|
|
.query_param("run_id", run_id.as_str());
|
|
then.status(200)
|
|
.header("Content-Type", "text/event-stream")
|
|
.body(format!("data: {payload}\n\n"));
|
|
});
|
|
|
|
let output = context
|
|
.command()
|
|
.args([
|
|
"--json",
|
|
"system",
|
|
"events",
|
|
"--server",
|
|
&format!("{}/api/v1", server.base_url()),
|
|
"--run-id",
|
|
&run_id,
|
|
])
|
|
.output()
|
|
.expect("command should run");
|
|
|
|
assert!(output.status.success(), "system events failed");
|
|
let stdout = String::from_utf8(output.stdout).expect("stdout should be UTF-8");
|
|
assert_eq!(stdout.trim(), payload.to_string());
|
|
attach_mock.assert();
|
|
}
|