Files
attune/tests/e2e/tier3/test_t3_15_inquiry_notifications.py
2026-02-04 17:46:30 -06:00

406 lines
14 KiB
Python

"""
T3.15: Inquiry Creation Notifications Test
Tests that the notifier service sends real-time notifications when inquiries are created.
Validates notification delivery for human-in-the-loop approval workflows.
Priority: MEDIUM
Duration: ~20 seconds
"""
import time
from typing import Any, Dict
import pytest
from helpers.client import AttuneClient
from helpers.fixtures import create_webhook_trigger, unique_ref
from helpers.polling import (
wait_for_execution_count,
wait_for_inquiry_count,
)
@pytest.mark.tier3
@pytest.mark.notifications
@pytest.mark.inquiry
@pytest.mark.websocket
def test_inquiry_creation_notification(client: AttuneClient, test_pack):
"""
Test that inquiry creation triggers notification.
Flow:
1. Create webhook trigger and inquiry action
2. Create rule
3. Trigger webhook
4. Verify inquiry is created
5. Validate inquiry notification metadata
"""
print("\n" + "=" * 80)
print("T3.15.1: Inquiry Creation Notification")
print("=" * 80)
pack_ref = test_pack["ref"]
# Step 1: Create webhook trigger
print("\n[STEP 1] Creating webhook trigger...")
trigger_ref = f"inquiry_notify_webhook_{unique_ref()}"
trigger = create_webhook_trigger(
client=client,
pack_ref=pack_ref,
trigger_ref=trigger_ref,
description="Webhook for inquiry notification test",
)
print(f"✓ Created trigger: {trigger['ref']}")
# Step 2: Create inquiry action
print("\n[STEP 2] Creating inquiry action...")
action_ref = f"inquiry_notify_action_{unique_ref()}"
action_payload = {
"ref": action_ref,
"pack": pack_ref,
"name": "Inquiry Action for Notification",
"description": "Creates inquiry to test notifications",
"runner_type": "inquiry",
"parameters": {
"question": {
"type": "string",
"description": "Question to ask",
"required": True,
},
"choices": {
"type": "array",
"description": "Available choices",
"required": False,
},
},
"enabled": True,
}
action_response = client.post("/actions", json=action_payload)
assert action_response.status_code == 201, (
f"Failed to create action: {action_response.text}"
)
action = action_response.json()["data"]
print(f"✓ Created inquiry action: {action['ref']}")
# Step 3: Create rule with inquiry action
print("\n[STEP 3] Creating rule...")
rule_ref = f"inquiry_notify_rule_{unique_ref()}"
rule_payload = {
"ref": rule_ref,
"pack": pack_ref,
"trigger": trigger["ref"],
"action": action["ref"],
"enabled": True,
"parameters": {
"question": "Do you approve this request?",
"choices": ["approve", "deny"],
},
}
rule_response = client.post("/rules", json=rule_payload)
assert rule_response.status_code == 201, (
f"Failed to create rule: {rule_response.text}"
)
rule = rule_response.json()["data"]
print(f"✓ Created rule: {rule['ref']}")
# Step 4: Trigger webhook to create inquiry
print("\n[STEP 4] Triggering webhook to create inquiry...")
webhook_url = f"/webhooks/{trigger['ref']}"
test_payload = {
"message": "Request for approval",
"timestamp": time.time(),
}
webhook_response = client.post(webhook_url, json=test_payload)
assert webhook_response.status_code == 200, (
f"Webhook trigger failed: {webhook_response.text}"
)
print(f"✓ Webhook triggered successfully")
# Step 5: Wait for inquiry creation
print("\n[STEP 5] Waiting for inquiry creation...")
wait_for_inquiry_count(client, expected_count=1, timeout=10)
inquiries = client.get("/inquiries").json()["data"]
assert len(inquiries) == 1, f"Expected 1 inquiry, got {len(inquiries)}"
inquiry = inquiries[0]
print(f"✓ Inquiry created: {inquiry['id']}")
# Step 6: Validate inquiry notification metadata
print("\n[STEP 6] Validating inquiry notification metadata...")
assert inquiry["status"] == "pending", (
f"Expected pending status, got {inquiry['status']}"
)
assert "created" in inquiry, "Inquiry missing created timestamp"
assert "updated" in inquiry, "Inquiry missing updated timestamp"
assert inquiry["execution_id"] is not None, "Inquiry should be linked to execution"
print(f"✓ Inquiry notification metadata validated")
print(f" - Inquiry ID: {inquiry['id']}")
print(f" - Status: {inquiry['status']}")
print(f" - Execution ID: {inquiry['execution_id']}")
print(f" - Created: {inquiry['created']}")
print("\n✅ Test passed: Inquiry creation notification flow validated")
@pytest.mark.tier3
@pytest.mark.notifications
@pytest.mark.inquiry
@pytest.mark.websocket
def test_inquiry_response_notification(client: AttuneClient, test_pack):
"""
Test that inquiry response triggers notification.
Flow:
1. Create inquiry via webhook trigger
2. Wait for inquiry creation
3. Respond to inquiry
4. Verify notification for inquiry response
"""
print("\n" + "=" * 80)
print("T3.15.2: Inquiry Response Notification")
print("=" * 80)
pack_ref = test_pack["ref"]
# Step 1: Create webhook trigger
print("\n[STEP 1] Creating webhook trigger...")
trigger_ref = f"inquiry_resp_webhook_{unique_ref()}"
trigger = create_webhook_trigger(
client=client,
pack_ref=pack_ref,
trigger_ref=trigger_ref,
description="Webhook for inquiry response test",
)
print(f"✓ Created trigger: {trigger['ref']}")
# Step 2: Create inquiry action
print("\n[STEP 2] Creating inquiry action...")
action_ref = f"inquiry_resp_action_{unique_ref()}"
action_payload = {
"ref": action_ref,
"pack": pack_ref,
"name": "Inquiry Response Action",
"description": "Creates inquiry for response test",
"runner_type": "inquiry",
"parameters": {
"question": {
"type": "string",
"description": "Question to ask",
"required": True,
},
},
"enabled": True,
}
action_response = client.post("/actions", json=action_payload)
assert action_response.status_code == 201, (
f"Failed to create action: {action_response.text}"
)
action = action_response.json()["data"]
print(f"✓ Created inquiry action: {action['ref']}")
# Step 3: Create rule
print("\n[STEP 3] Creating rule...")
rule_ref = f"inquiry_resp_rule_{unique_ref()}"
rule_payload = {
"ref": rule_ref,
"pack": pack_ref,
"trigger": trigger["ref"],
"action": action["ref"],
"enabled": True,
"parameters": {
"question": "Approve deployment to production?",
},
}
rule_response = client.post("/rules", json=rule_payload)
assert rule_response.status_code == 201, (
f"Failed to create rule: {rule_response.text}"
)
rule = rule_response.json()["data"]
print(f"✓ Created rule: {rule['ref']}")
# Step 4: Trigger webhook to create inquiry
print("\n[STEP 4] Triggering webhook...")
webhook_url = f"/webhooks/{trigger['ref']}"
webhook_response = client.post(webhook_url, json={"request": "deploy"})
assert webhook_response.status_code == 200
print(f"✓ Webhook triggered")
# Step 5: Wait for inquiry creation
print("\n[STEP 5] Waiting for inquiry creation...")
wait_for_inquiry_count(client, expected_count=1, timeout=10)
inquiries = client.get("/inquiries").json()["data"]
inquiry = inquiries[0]
inquiry_id = inquiry["id"]
print(f"✓ Inquiry created: {inquiry_id}")
# Step 6: Respond to inquiry
print("\n[STEP 6] Responding to inquiry...")
response_payload = {
"response": "approved",
"comment": "Deployment approved by test",
}
response = client.post(f"/inquiries/{inquiry_id}/respond", json=response_payload)
assert response.status_code == 200, f"Failed to respond: {response.text}"
print(f"✓ Inquiry response submitted")
# Step 7: Verify inquiry status updated
print("\n[STEP 7] Verifying inquiry status update...")
time.sleep(2) # Allow notification processing
updated_inquiry = client.get(f"/inquiries/{inquiry_id}").json()["data"]
assert updated_inquiry["status"] == "responded", (
f"Expected responded status, got {updated_inquiry['status']}"
)
assert updated_inquiry["response"] is not None, "Inquiry should have response data"
print(f"✓ Inquiry response notification metadata validated")
print(f" - Inquiry ID: {inquiry_id}")
print(f" - Status: {updated_inquiry['status']}")
print(f" - Response received: {updated_inquiry['response'] is not None}")
print(f" - Updated: {updated_inquiry['updated']}")
print("\n✅ Test passed: Inquiry response notification flow validated")
@pytest.mark.tier3
@pytest.mark.notifications
@pytest.mark.inquiry
@pytest.mark.websocket
def test_inquiry_timeout_notification(client: AttuneClient, test_pack):
"""
Test that inquiry timeout triggers notification.
Flow:
1. Create inquiry with short timeout
2. Wait for timeout to occur
3. Verify notification for inquiry timeout
"""
print("\n" + "=" * 80)
print("T3.15.3: Inquiry Timeout Notification")
print("=" * 80)
pack_ref = test_pack["ref"]
# Step 1: Create webhook trigger
print("\n[STEP 1] Creating webhook trigger...")
trigger_ref = f"inquiry_timeout_webhook_{unique_ref()}"
trigger = create_webhook_trigger(
client=client,
pack_ref=pack_ref,
trigger_ref=trigger_ref,
description="Webhook for inquiry timeout test",
)
print(f"✓ Created trigger: {trigger['ref']}")
# Step 2: Create inquiry action with short timeout
print("\n[STEP 2] Creating inquiry action with timeout...")
action_ref = f"inquiry_timeout_action_{unique_ref()}"
action_payload = {
"ref": action_ref,
"pack": pack_ref,
"name": "Timeout Inquiry Action",
"description": "Creates inquiry with short timeout",
"runner_type": "inquiry",
"timeout": 3, # 3 second timeout
"parameters": {
"question": {
"type": "string",
"description": "Question to ask",
"required": True,
},
},
"enabled": True,
}
action_response = client.post("/actions", json=action_payload)
assert action_response.status_code == 201, (
f"Failed to create action: {action_response.text}"
)
action = action_response.json()["data"]
print(f"✓ Created inquiry action with 3s timeout: {action['ref']}")
# Step 3: Create rule
print("\n[STEP 3] Creating rule...")
rule_ref = f"inquiry_timeout_rule_{unique_ref()}"
rule_payload = {
"ref": rule_ref,
"pack": pack_ref,
"trigger": trigger["ref"],
"action": action["ref"],
"enabled": True,
"parameters": {
"question": "Quick approval needed!",
},
}
rule_response = client.post("/rules", json=rule_payload)
assert rule_response.status_code == 201, (
f"Failed to create rule: {rule_response.text}"
)
rule = rule_response.json()["data"]
print(f"✓ Created rule: {rule['ref']}")
# Step 4: Trigger webhook
print("\n[STEP 4] Triggering webhook...")
webhook_url = f"/webhooks/{trigger['ref']}"
webhook_response = client.post(webhook_url, json={"urgent": True})
assert webhook_response.status_code == 200
print(f"✓ Webhook triggered")
# Step 5: Wait for inquiry creation
print("\n[STEP 5] Waiting for inquiry creation...")
wait_for_inquiry_count(client, expected_count=1, timeout=10)
inquiries = client.get("/inquiries").json()["data"]
inquiry = inquiries[0]
inquiry_id = inquiry["id"]
print(f"✓ Inquiry created: {inquiry_id}")
# Step 6: Wait for timeout to occur
print("\n[STEP 6] Waiting for inquiry timeout...")
time.sleep(5) # Wait longer than timeout
timed_out_inquiry = client.get(f"/inquiries/{inquiry_id}").json()["data"]
# Verify timeout status
assert timed_out_inquiry["status"] in ["timeout", "expired", "cancelled"], (
f"Expected timeout status, got {timed_out_inquiry['status']}"
)
print(f"✓ Inquiry timeout notification metadata validated")
print(f" - Inquiry ID: {inquiry_id}")
print(f" - Status: {timed_out_inquiry['status']}")
print(f" - Timeout: {action['timeout']}s")
print(f" - Updated: {timed_out_inquiry['updated']}")
print("\n✅ Test passed: Inquiry timeout notification flow validated")
@pytest.mark.tier3
@pytest.mark.notifications
@pytest.mark.inquiry
@pytest.mark.websocket
@pytest.mark.skip(
reason="Requires WebSocket infrastructure for real-time inquiry notifications"
)
def test_websocket_inquiry_notification_delivery(client: AttuneClient, test_pack):
"""
Test actual WebSocket notification delivery for inquiries.
This test is skipped until WebSocket test infrastructure is implemented.
Flow:
1. Connect to WebSocket with auth
2. Subscribe to inquiry notifications
3. Create inquiry via workflow
4. Receive real-time notification
5. Validate notification structure
"""
print("\n" + "=" * 80)
print("T3.15.4: WebSocket Inquiry Notification Delivery")
print("=" * 80)
# This would require WebSocket client infrastructure similar to T3.14.4
# Notifications would include:
# - inquiry.created
# - inquiry.responded
# - inquiry.timeout
# - inquiry.cancelled
pytest.skip("WebSocket client infrastructure not yet implemented")