Files
claw/tests/scheduled_monitoring_action_dry_run_runtime_test.rs

315 lines
11 KiB
Rust

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();
}