import json from fastapi.testclient import TestClient from app.services.ticketing_chatops_templates import ( TICKETING_CHATOPS_SCHEMA_VERSION, WORKFLOW_EVENT_TYPES, get_ticketing_chatops_templates, validate_workflow_template_bundle, ) from app.services.webhook_events import EVENT_ALERT_RESOLVED, EVENT_COMPLIANCE_DROP def _string_values(value): if isinstance(value, dict): for item in value.values(): yield from _string_values(item) elif isinstance(value, list): for item in value: yield from _string_values(item) elif isinstance(value, str): yield value def test_ticketing_chatops_bundle_is_versioned_and_complete(): """Workflow templates cover every ticketing/chatops event type.""" bundle = get_ticketing_chatops_templates() assert bundle["schema_version"] == TICKETING_CHATOPS_SCHEMA_VERSION assert sorted(bundle["event_types"]) == sorted(WORKFLOW_EVENT_TYPES) assert validate_workflow_template_bundle(bundle) == [] assert bundle["event_workflow_mappings"][EVENT_COMPLIANCE_DROP]["severity"] == "high" assert ( bundle["event_workflow_mappings"][EVENT_ALERT_RESOLVED]["ticket_action"] == "resolve_or_comment" ) def test_ticketing_chatops_templates_have_no_embedded_credentials(): """Examples must be safe to publish without leaking destination credentials.""" bundle = get_ticketing_chatops_templates() encoded_values = json.dumps(list(_string_values(bundle))).lower() for forbidden in ["xoxb-", "ghp_", "jira_pat", "incoming_webhook", "bearer "]: assert forbidden not in encoded_values def test_ticketing_chatops_validation_reports_template_drift(): """Validator explains missing required workflow and destination fields.""" bundle = get_ticketing_chatops_templates() bundle["schema_version"] = "unexpected" bundle["event_types"] = [EVENT_COMPLIANCE_DROP] bundle["event_workflow_mappings"][EVENT_COMPLIANCE_DROP].pop("dedupe_key_template") bundle["event_workflow_mappings"][EVENT_COMPLIANCE_DROP].pop("owner") bundle["event_workflow_mappings"][EVENT_COMPLIANCE_DROP].pop("ticket_action") bundle["payload_templates"].pop("slack") errors = validate_workflow_template_bundle(bundle) assert "schema_version must be dmarq.workflow.template.v1" in errors assert "event_types must match supported workflow events" in errors assert f"{EVENT_COMPLIANCE_DROP} is missing a dedupe key template" in errors assert f"{EVENT_COMPLIANCE_DROP} is missing an owner" in errors assert f"{EVENT_COMPLIANCE_DROP} is missing a ticket action" in errors assert "missing payload template: slack" in errors def test_ticketing_chatops_templates_return_independent_copies(): """Mutating one returned bundle does not alter future callers.""" bundle = get_ticketing_chatops_templates() bundle["payload_templates"]["jira"]["operation"] = "mutated" fresh_bundle = get_ticketing_chatops_templates() assert fresh_bundle["payload_templates"]["jira"]["operation"] == "create_or_update_issue" def test_ticketing_chatops_endpoint_returns_workflow_templates( authed_client: TestClient, ): """Administrators can fetch issue and chat message templates.""" response = authed_client.get("/api/v1/integrations/ticketing-chatops/templates") assert response.status_code == 200 body = response.json() assert body["schema_version"] == TICKETING_CHATOPS_SCHEMA_VERSION assert set(body["payload_templates"]) == { "jira", "github", "slack", "microsoft_teams", } assert body["payload_templates"]["jira"]["operation"] == "create_or_update_issue" assert body["payload_templates"]["slack"]["thread_key"] == "{dedupe_key}" def test_ticketing_chatops_endpoint_requires_admin_auth(client: TestClient): """Workflow templates use the same admin boundary as integration settings.""" response = client.get("/api/v1/integrations/ticketing-chatops/templates") assert response.status_code == 401