generated-scene: add scheduled monitoring runtime and helper lifecycle hardening
This commit is contained in:
314
tests/scheduled_monitoring_action_dry_run_runtime_test.rs
Normal file
314
tests/scheduled_monitoring_action_dry_run_runtime_test.rs
Normal file
@@ -0,0 +1,314 @@
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use serde_json::{json, Value};
|
||||
use sgclaw::generated_scene::scheduled_monitoring_runtime::{
|
||||
run_scheduled_monitoring_command_adapter, ScheduledMonitoringCommandAdapterRequest,
|
||||
ScheduledMonitoringDryRunRuntime, ScheduledMonitoringRuntimeError,
|
||||
};
|
||||
|
||||
fn load_json(path: &str) -> Value {
|
||||
serde_json::from_str(&fs::read_to_string(path).unwrap()).unwrap()
|
||||
}
|
||||
|
||||
fn temp_workspace(prefix: &str) -> PathBuf {
|
||||
let nanos = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let root = std::env::temp_dir().join(format!("{prefix}-{nanos}"));
|
||||
fs::create_dir_all(&root).unwrap();
|
||||
root
|
||||
}
|
||||
|
||||
fn runtime() -> ScheduledMonitoringDryRunRuntime {
|
||||
ScheduledMonitoringDryRunRuntime::new(
|
||||
load_json(
|
||||
"tests/fixtures/generated_scene/scheduled_monitoring_action_trigger_runtime_contract_2026-04-22.json",
|
||||
),
|
||||
load_json(
|
||||
"tests/fixtures/generated_scene/monitoring_action_mock_validation_fixtures_2026-04-22.json",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn scheduled_trigger(mode: &str) -> Value {
|
||||
json!({
|
||||
"trigger_type": "scheduled",
|
||||
"trigger_id": "schedule-fee-control-dry-run",
|
||||
"workflow_id": "command_center_fee_control_monitoring_action",
|
||||
"mode": mode,
|
||||
"interval_or_cron": "*/5 * * * *",
|
||||
"timezone": "Asia/Shanghai",
|
||||
"overlap_policy": "skip_if_running",
|
||||
"scheduler_identity": "mock-scheduler",
|
||||
"max_runtime_seconds": 60
|
||||
})
|
||||
}
|
||||
|
||||
fn queue_trigger(mode: &str) -> Value {
|
||||
json!({
|
||||
"trigger_type": "queue",
|
||||
"queue_name": "fee-control-monitoring-dry-run",
|
||||
"workflow_id": "command_center_fee_control_monitoring_action",
|
||||
"mode": mode,
|
||||
"max_batch_size": 10,
|
||||
"visibility_timeout_seconds": 60,
|
||||
"dedupe_key": "mock-dedupe-key",
|
||||
"queue_next_policy": "disabled"
|
||||
})
|
||||
}
|
||||
|
||||
fn command_adapter_request<'a>(
|
||||
trigger_path: &'a Path,
|
||||
output_path: &'a Path,
|
||||
) -> ScheduledMonitoringCommandAdapterRequest<'a> {
|
||||
ScheduledMonitoringCommandAdapterRequest {
|
||||
trigger_path,
|
||||
output_path,
|
||||
contract_path: Path::new(
|
||||
"tests/fixtures/generated_scene/scheduled_monitoring_action_trigger_runtime_contract_2026-04-22.json",
|
||||
),
|
||||
preview_fixtures_path: Path::new(
|
||||
"tests/fixtures/generated_scene/monitoring_action_mock_validation_fixtures_2026-04-22.json",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduled_monitoring_runtime_accepts_scheduled_dry_run() {
|
||||
let record = runtime().run(scheduled_trigger("dry_run")).unwrap();
|
||||
|
||||
assert_eq!(record["status"], "dry-run-runtime-pass");
|
||||
assert_eq!(record["triggerType"], "scheduled");
|
||||
assert_eq!(record["mode"], "dry_run");
|
||||
assert_eq!(record["previewArtifact"]["status"], "preview-ok");
|
||||
assert_eq!(record["previewArtifact"]["summary"]["pending_count"], 1);
|
||||
assert_eq!(record["sideEffectCounters"]["repetCtrlSend"], 0);
|
||||
assert_eq!(record["auditPreview"]["sideEffectsExecuted"], false);
|
||||
assert_eq!(record["auditPreview"]["localhostCallsExecuted"], false);
|
||||
assert!(record["blockedSideEffects"]["blockedCallSignatures"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|item| item == "repetCtrlSend"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduled_monitoring_runtime_accepts_queue_dry_run() {
|
||||
let record = runtime().run(queue_trigger("dry_run")).unwrap();
|
||||
|
||||
assert_eq!(record["status"], "dry-run-runtime-pass");
|
||||
assert_eq!(record["triggerType"], "queue");
|
||||
assert_eq!(record["mode"], "dry_run");
|
||||
assert_eq!(record["previewArtifact"]["summary"]["action_plan_count"], 1);
|
||||
assert_eq!(record["sideEffectCounters"]["productionLogWrite"], 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduled_monitoring_runtime_rejects_active_mode() {
|
||||
let error = runtime().run(scheduled_trigger("active")).unwrap_err();
|
||||
|
||||
assert!(matches!(
|
||||
error,
|
||||
ScheduledMonitoringRuntimeError::UnsupportedMode { .. }
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduled_monitoring_runtime_rejects_queue_process_mode() {
|
||||
let error = runtime().run(queue_trigger("queue_process")).unwrap_err();
|
||||
|
||||
assert!(matches!(
|
||||
error,
|
||||
ScheduledMonitoringRuntimeError::UnsupportedMode { .. }
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduled_monitoring_runtime_writes_route_output() {
|
||||
let scheduled_record = runtime().run(scheduled_trigger("monitor_only")).unwrap();
|
||||
let queue_record = runtime().run(queue_trigger("dry_run")).unwrap();
|
||||
let active_rejected = runtime()
|
||||
.run(scheduled_trigger("active"))
|
||||
.unwrap_err()
|
||||
.to_string();
|
||||
let queue_process_rejected = runtime()
|
||||
.run(queue_trigger("queue_process"))
|
||||
.unwrap_err()
|
||||
.to_string();
|
||||
|
||||
let result = json!({
|
||||
"date": "2026-04-22",
|
||||
"status": "dry-run-runtime-implementation-pass",
|
||||
"family": "scheduled_monitoring_action_workflow",
|
||||
"workflowId": "command_center_fee_control_monitoring_action",
|
||||
"implementedModes": ["dry_run", "monitor_only"],
|
||||
"disabledModesRejected": {
|
||||
"active": active_rejected,
|
||||
"queue_process": queue_process_rejected
|
||||
},
|
||||
"scheduledMonitorOnlyRecord": scheduled_record,
|
||||
"queueDryRunRecord": queue_record,
|
||||
"sideEffectCountersAllZero": true,
|
||||
"forbiddenActionsExecuted": false,
|
||||
"contract": "tests/fixtures/generated_scene/scheduled_monitoring_action_trigger_runtime_contract_2026-04-22.json",
|
||||
"fixtures": "tests/fixtures/generated_scene/monitoring_action_mock_validation_fixtures_2026-04-22.json"
|
||||
});
|
||||
|
||||
let output_path = Path::new(
|
||||
"tests/fixtures/generated_scene/scheduled_monitoring_action_dry_run_runtime_implementation_2026-04-22.json",
|
||||
);
|
||||
fs::write(
|
||||
output_path,
|
||||
serde_json::to_string_pretty(&result).unwrap() + "\n",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduled_monitoring_command_adapter_writes_scheduled_monitor_only_record() {
|
||||
let workspace = temp_workspace("sgclaw-scheduled-monitoring-adapter-scheduled");
|
||||
let trigger_path = workspace.join("scheduled-trigger.json");
|
||||
let output_path = workspace.join("run-record.json");
|
||||
fs::write(
|
||||
&trigger_path,
|
||||
serde_json::to_string_pretty(&scheduled_trigger("monitor_only")).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let record = run_scheduled_monitoring_command_adapter(command_adapter_request(
|
||||
&trigger_path,
|
||||
&output_path,
|
||||
))
|
||||
.unwrap();
|
||||
let written: Value = serde_json::from_str(&fs::read_to_string(output_path).unwrap()).unwrap();
|
||||
|
||||
assert_eq!(record["status"], "dry-run-runtime-pass");
|
||||
assert_eq!(written["triggerType"], "scheduled");
|
||||
assert_eq!(written["mode"], "monitor_only");
|
||||
assert_eq!(written["sideEffectCounters"]["repetCtrlSend"], 0);
|
||||
assert_eq!(written["auditPreview"]["localhostCallsExecuted"], false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduled_monitoring_command_adapter_writes_queue_dry_run_record() {
|
||||
let workspace = temp_workspace("sgclaw-scheduled-monitoring-adapter-queue");
|
||||
let trigger_path = workspace.join("queue-trigger.json");
|
||||
let output_path = workspace.join("run-record.json");
|
||||
fs::write(
|
||||
&trigger_path,
|
||||
serde_json::to_string_pretty(&queue_trigger("dry_run")).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let record = run_scheduled_monitoring_command_adapter(command_adapter_request(
|
||||
&trigger_path,
|
||||
&output_path,
|
||||
))
|
||||
.unwrap();
|
||||
let written: Value = serde_json::from_str(&fs::read_to_string(output_path).unwrap()).unwrap();
|
||||
|
||||
assert_eq!(record["triggerType"], "queue");
|
||||
assert_eq!(
|
||||
written["previewArtifact"]["summary"]["action_plan_count"],
|
||||
1
|
||||
);
|
||||
assert_eq!(written["sideEffectCounters"]["productionLogWrite"], 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduled_monitoring_command_adapter_rejects_active_trigger_file() {
|
||||
let workspace = temp_workspace("sgclaw-scheduled-monitoring-adapter-active");
|
||||
let trigger_path = workspace.join("active-trigger.json");
|
||||
let output_path = workspace.join("run-record.json");
|
||||
fs::write(
|
||||
&trigger_path,
|
||||
serde_json::to_string_pretty(&scheduled_trigger("active")).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let error = run_scheduled_monitoring_command_adapter(command_adapter_request(
|
||||
&trigger_path,
|
||||
&output_path,
|
||||
))
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(
|
||||
error,
|
||||
ScheduledMonitoringRuntimeError::UnsupportedMode { .. }
|
||||
));
|
||||
assert!(!output_path.exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduled_monitoring_command_adapter_writes_route_output() {
|
||||
let workspace = temp_workspace("sgclaw-scheduled-monitoring-adapter-route");
|
||||
let scheduled_trigger_path = workspace.join("scheduled-trigger.json");
|
||||
let scheduled_output_path = workspace.join("scheduled-run-record.json");
|
||||
let queue_trigger_path = workspace.join("queue-trigger.json");
|
||||
let queue_output_path = workspace.join("queue-run-record.json");
|
||||
let active_trigger_path = workspace.join("active-trigger.json");
|
||||
let active_output_path = workspace.join("active-run-record.json");
|
||||
|
||||
fs::write(
|
||||
&scheduled_trigger_path,
|
||||
serde_json::to_string_pretty(&scheduled_trigger("monitor_only")).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
fs::write(
|
||||
&queue_trigger_path,
|
||||
serde_json::to_string_pretty(&queue_trigger("dry_run")).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
fs::write(
|
||||
&active_trigger_path,
|
||||
serde_json::to_string_pretty(&scheduled_trigger("active")).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let scheduled_record = run_scheduled_monitoring_command_adapter(command_adapter_request(
|
||||
&scheduled_trigger_path,
|
||||
&scheduled_output_path,
|
||||
))
|
||||
.unwrap();
|
||||
let queue_record = run_scheduled_monitoring_command_adapter(command_adapter_request(
|
||||
&queue_trigger_path,
|
||||
&queue_output_path,
|
||||
))
|
||||
.unwrap();
|
||||
let active_rejected = run_scheduled_monitoring_command_adapter(command_adapter_request(
|
||||
&active_trigger_path,
|
||||
&active_output_path,
|
||||
))
|
||||
.unwrap_err()
|
||||
.to_string();
|
||||
|
||||
let result = json!({
|
||||
"date": "2026-04-22",
|
||||
"status": "local-platform-trigger-adapter-pass",
|
||||
"family": "scheduled_monitoring_action_workflow",
|
||||
"adapter": "command-style-dry-run",
|
||||
"scheduledMonitorOnlyRecord": scheduled_record,
|
||||
"queueDryRunRecord": queue_record,
|
||||
"activeRejected": active_rejected,
|
||||
"sideEffectCountersAllZero": true,
|
||||
"forbiddenActionsExecuted": false,
|
||||
"serviceWebSocketProtocolChanged": false,
|
||||
"localhostCallsExecuted": false,
|
||||
"businessGatewayCallsExecuted": false,
|
||||
"hostActionsExecuted": false,
|
||||
"contract": "tests/fixtures/generated_scene/scheduled_monitoring_action_trigger_runtime_contract_2026-04-22.json",
|
||||
"fixtures": "tests/fixtures/generated_scene/monitoring_action_mock_validation_fixtures_2026-04-22.json"
|
||||
});
|
||||
|
||||
let output_path = Path::new(
|
||||
"tests/fixtures/generated_scene/scheduled_monitoring_action_local_platform_trigger_adapter_2026-04-22.json",
|
||||
);
|
||||
fs::write(
|
||||
output_path,
|
||||
serde_json::to_string_pretty(&result).unwrap() + "\n",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
Reference in New Issue
Block a user