tandem-server 0.6.5

HTTP server for Tandem engine APIs
#[tokio::test]
#[serial_test::serial(incident_monitor_http)]
async fn incident_monitor_deployment_cards_empty_inventory_returns_self_monitoring_card() {
    let state = test_state().await;
    state.set_api_token(Some("tk_admin".to_string())).await;
    let app = app_router(state);

    let payload = post_incident_monitor_deployment_cards(
        app,
        "/incident-monitor/security/deployment-cards",
        json!({}),
        Some(("org-cards", "workspace-cards", "security-admin", "tk_admin")),
    )
    .await;

    let cards = payload["cards"].as_array().expect("deployment cards");
    assert_eq!(cards.len(), 1);
    assert_eq!(
        cards[0]["card_id"],
        json!("tandem:self_monitoring:incident_monitor")
    );
    assert_eq!(cards[0]["system_boundary"], json!("tandem_self_monitoring"));
    assert!(payload["findings"]
        .as_array()
        .expect("findings")
        .iter()
        .any(|finding| finding["rule_id"].as_str()
            == Some("deployment_card_required_field_missing")
            && finding["affected_objects"][0]["field"].as_str() == Some("business_owner")));
    assert_eq!(payload["scope"]["raw_inventory_included"], json!(false));
}

#[tokio::test]
#[serial_test::serial(incident_monitor_http)]
async fn incident_monitor_deployment_cards_generate_inventory_cards_and_markdown() {
    let state = test_state().await;
    state.set_api_token(Some("tk_admin".to_string())).await;
    let workspace = tempfile::tempdir().expect("deployment card workspace");
    let mut automation =
        sample_authority_inventory_automation(workspace.path().display().to_string());
    automation.automation_id = "auto-card-payments".to_string();
    automation.set_tenant_context(&tandem_types::TenantContext::explicit_user_workspace(
        "org-cards",
        "workspace-cards",
        None,
        "security-admin",
    ));
    state
        .put_automation_v2(automation)
        .await
        .expect("deployment card automation");
    state
        .put_incident_monitor_config(crate::IncidentMonitorConfig {
            enabled: true,
            repo: Some("acme/platform".to_string()),
            workspace_root: Some(workspace.path().display().to_string()),
            destinations: vec![crate::IncidentMonitorDestinationConfig {
                destination_id: "audit-webhook".to_string(),
                name: "Audit webhook".to_string(),
                kind: crate::IncidentMonitorDestinationKind::Webhook,
                enabled: true,
                require_approval: true,
                webhook_url: Some("https://audit.example.test/incidents".to_string()),
                webhook_secret_ref: Some("env:INCIDENT_MONITOR_CARD_SECRET".to_string()),
                ..Default::default()
            }],
            routes: vec![crate::IncidentMonitorRouteConfig {
                route_id: "card-route".to_string(),
                name: "Card route".to_string(),
                enabled: true,
                priority: 10,
                destination_ids: vec!["audit-webhook".to_string()],
                match_source_kinds: vec!["external_app".to_string()],
                ..Default::default()
            }],
            monitored_projects: vec![crate::IncidentMonitorMonitoredProject {
                project_id: "payments".to_string(),
                name: "Payments".to_string(),
                repo: "acme/payments".to_string(),
                workspace_root: workspace.path().display().to_string(),
                source_kind: crate::IncidentMonitorSourceKind::ExternalApp,
                enabled: true,
                allowed_destination_ids: vec!["audit-webhook".to_string()],
                default_destination_ids: vec!["audit-webhook".to_string()],
                default_route_tags: vec!["payments".to_string()],
                tenant_id: None,
                workspace_id: None,
                event_schema_version: Some("payments.v1".to_string()),
                redaction_profile: Some("standard".to_string()),
                retention_profile: Some("90d".to_string()),
                ..Default::default()
            }],
            default_destination_ids: vec!["audit-webhook".to_string()],
            ..Default::default()
        })
        .await
        .expect("deployment card config");
    let app = app_router(state);

    let payload = post_incident_monitor_deployment_cards(
        app,
        "/incident-monitor/security/deployment-cards",
        json!({
            "include_raw_inventory": true,
            "defaults": {
                "business_owner": "Security Ops",
                "accountable_team": "AI Governance",
                "autonomy_level": "supervised",
                "data_classification": "internal",
                "approval_protocol": "Human approval before high-risk publish",
                "escalation_protocol": "Page security lead for regulatory or legal risk",
                "review_cadence_days": 30,
                "known_limitations": ["Inventory reflects current Tandem configuration"],
                "residual_risks": ["Operator metadata must be reviewed before production"]
            },
            "metadata": {
                "automation:auto-card-payments": {
                    "intended_purpose": "Coordinate approved payments incident follow-up",
                    "evidence_refs": ["runbook:payments-incident-response"]
                },
                "monitored_source:payments:project": {
                    "intended_purpose": "Monitor customer payment incident events"
                }
            }
        }),
        Some(("org-cards", "workspace-cards", "security-admin", "tk_admin")),
    )
    .await;

    let cards = payload["cards"].as_array().expect("deployment cards");
    assert!(cards.iter().any(|card| {
        card["card_id"].as_str() == Some("automation:auto-card-payments")
            && card["business_owner"].as_str() == Some("Security Ops")
            && card["linked_evidence"]["operator_refs"]
                .as_array()
                .is_some_and(|refs| {
                    refs.iter()
                        .any(|value| value.as_str() == Some("runbook:payments-incident-response"))
                })
    }));
    let source_card = cards
        .iter()
        .find(|card| card["card_id"].as_str() == Some("monitored_source:payments:project"))
        .expect("monitored source deployment card");
    assert_eq!(
        source_card["system_boundary"].as_str(),
        Some("customer_owned_system")
    );
    assert_eq!(
        source_card["authority"]["inventory"]["redaction_profile_present"].as_bool(),
        Some(true)
    );
    assert_eq!(
        source_card["authority"]["inventory"]["retention_profile_present"].as_bool(),
        Some(true)
    );
    assert!(source_card["linked_evidence"]["posture_finding_refs"]
        .as_array()
        .expect("source posture finding refs")
        .iter()
        .any(|value| value
            .as_str()
            .is_some_and(|value| value.starts_with("bpf_"))));
    assert!(payload["counts"]["posture_findings_linked"]
        .as_u64()
        .is_some_and(|count| count > 0));
    assert!(payload["findings"].as_array().expect("findings").is_empty());
    let payload_string = serde_json::to_string(&payload).expect("deployment card json");
    assert!(payload_string.contains("Incident Monitor Deployment Cards"));
    assert!(payload_string.contains("Coordinate approved payments incident follow-up"));
    assert!(!payload_string.contains("INCIDENT_MONITOR_CARD_SECRET"));
    assert!(!payload_string.contains("metadata-must-not-leak"));
    assert_eq!(payload["scope"]["raw_inventory_included"], json!(false));
    assert_eq!(
        payload["scope"]["raw_inventory_request_ignored"],
        json!(true)
    );
}

#[tokio::test]
#[serial_test::serial(incident_monitor_http)]
async fn incident_monitor_deployment_cards_reject_scoped_intake_key() {
    let state = test_state().await;
    state.set_api_token(Some("tk_admin".to_string())).await;
    let app = app_router(state);

    let resp = app
        .oneshot(
            Request::builder()
                .method("POST")
                .uri("/incident-monitor/security/deployment-cards")
                .header("content-type", "application/json")
                .header("x-tandem-token", "tk_admin")
                .header("x-tandem-incident-monitor-intake-key", "tim_card_scoped_key")
                .body(Body::from(json!({}).to_string()))
                .expect("deployment cards scoped intake request"),
        )
        .await
        .expect("deployment cards scoped intake response");
    assert_eq!(resp.status(), StatusCode::FORBIDDEN);
}

async fn post_incident_monitor_deployment_cards(
    app: axum::Router,
    uri: &str,
    input: Value,
    tenant: Option<(&str, &str, &str, &str)>,
) -> Value {
    let mut builder = Request::builder()
        .method("POST")
        .uri(uri)
        .header("content-type", "application/json");
    if let Some((org, workspace, actor, token)) = tenant {
        builder = builder
            .header("x-tandem-org-id", org)
            .header("x-tandem-workspace-id", workspace)
            .header("x-tandem-actor-id", actor)
            .header("x-tandem-token", token);
    }
    let resp = app
        .oneshot(
            builder
                .body(Body::from(input.to_string()))
                .expect("deployment cards request"),
        )
        .await
        .expect("deployment cards response");
    let status = resp.status();
    let body = to_bytes(resp.into_body(), usize::MAX)
        .await
        .expect("deployment cards body");
    assert_eq!(status, StatusCode::OK, "{}", String::from_utf8_lossy(&body));
    serde_json::from_slice(&body).expect("deployment cards json")
}