re-uploading work

This commit is contained in:
2026-02-04 17:46:30 -06:00
commit 3b14c65998
1388 changed files with 381262 additions and 0 deletions

View File

@@ -0,0 +1,423 @@
#!/usr/bin/env python3
"""
T1.4: Webhook Trigger with Payload
Tests that a webhook POST triggers an action with payload data.
Test Flow:
1. Create webhook trigger (generates unique URL)
2. Create action that echoes webhook payload
3. Create rule linking webhook → action
4. POST JSON payload to webhook URL
5. Verify event created with correct payload
6. Verify execution receives payload as parameters
7. Verify action output includes webhook data
Success Criteria:
- Webhook trigger generates unique URL (/api/v1/webhooks/{trigger_id})
- POST to webhook creates event immediately
- Event payload matches POST body
- Rule evaluates and creates enforcement
- Execution receives webhook data as input
- Action can access webhook payload fields
"""
import time
import pytest
from helpers import (
AttuneClient,
create_echo_action,
create_rule,
create_webhook_trigger,
wait_for_event_count,
wait_for_execution_count,
wait_for_execution_status,
)
@pytest.mark.tier1
@pytest.mark.webhook
@pytest.mark.integration
@pytest.mark.timeout(30)
class TestWebhookTrigger:
"""Test webhook trigger automation flow"""
def test_webhook_trigger_with_payload(self, client: AttuneClient, pack_ref: str):
"""Test that webhook POST triggers action with payload"""
print(f"\n=== T1.4: Webhook Trigger with Payload ===")
# Step 1: Create webhook trigger
print("\n[1/6] Creating webhook trigger...")
trigger = create_webhook_trigger(client=client, pack_ref=pack_ref)
print(f"✓ Created trigger: {trigger['label']} (ID: {trigger['id']})")
print(f" Ref: {trigger['ref']}")
print(f" Webhook URL: /api/v1/webhooks/{trigger['id']}")
assert "webhook" in trigger["ref"].lower() or trigger.get(
"webhook_enabled", False
)
# Step 2: Create echo action
print("\n[2/6] Creating echo action...")
action = create_echo_action(client=client, pack_ref=pack_ref)
action_ref = action["ref"]
print(f"✓ Created action: {action_ref} (ID: {action['id']})")
# Step 3: Create rule linking webhook → action
print("\n[3/6] Creating rule...")
# Capture timestamp before rule creation for filtering
from datetime import datetime, timezone
rule_creation_time = datetime.now(timezone.utc).isoformat()
rule = create_rule(
client=client,
trigger_id=trigger["id"],
action_ref=action_ref,
pack_ref=pack_ref,
enabled=True,
action_parameters={
"message": "{{ trigger.data.message }}",
"count": 1,
},
)
print(f"✓ Created rule: {rule['label']} (ID: {rule['id']})")
print(f" Rule creation timestamp: {rule_creation_time}")
assert rule["enabled"] is True
# Step 4: POST to webhook
print("\n[4/6] Firing webhook with payload...")
webhook_payload = {
"event_type": "test.webhook",
"message": "Hello from webhook!",
"user_id": 12345,
"metadata": {"source": "e2e_test", "timestamp": time.time()},
}
print(f" Payload: {webhook_payload}")
event_response = client.fire_webhook(
trigger_id=trigger["id"], payload=webhook_payload
)
print(f"✓ Webhook fired")
print(f" Event ID: {event_response.get('id')}")
# Step 5: Verify event created
print("\n[5/6] Verifying event created...")
events = wait_for_event_count(
client=client,
expected_count=1,
trigger_id=trigger["id"],
timeout=10,
poll_interval=0.5,
)
assert len(events) >= 1, "Expected at least 1 event"
event = events[0]
print(f"✓ Event created (ID: {event['id']})")
print(f" Trigger ID: {event['trigger']}")
print(f" Payload: {event.get('payload')}")
# Verify event payload matches webhook payload
assert event["trigger"] == trigger["id"]
event_payload = event.get("payload", {})
# Check key fields from webhook payload
for key in ["event_type", "message", "user_id"]:
assert key in event_payload, f"Missing key '{key}' in event payload"
assert event_payload[key] == webhook_payload[key], (
f"Event payload mismatch for '{key}': "
f"expected {webhook_payload[key]}, got {event_payload[key]}"
)
print(f"✓ Event payload matches webhook payload")
# Step 6: Verify execution completed with webhook data
print("\n[6/6] Verifying execution with webhook data...")
executions = wait_for_execution_count(
client=client,
expected_count=1,
rule_id=rule["id"],
created_after=rule_creation_time,
timeout=20,
poll_interval=0.5,
verbose=True,
)
assert len(executions) >= 1, "Expected at least 1 execution"
execution = executions[0]
print(f"✓ Execution created (ID: {execution['id']})")
print(f" Status: {execution['status']}")
# Wait for execution to complete
if execution["status"] not in ["succeeded", "failed", "canceled"]:
execution = wait_for_execution_status(
client=client,
execution_id=execution["id"],
expected_status="succeeded",
timeout=15,
)
assert execution["status"] == "succeeded", (
f"Execution failed with status: {execution['status']}"
)
# Verify execution received webhook data
print(f"\n Execution details:")
print(f" Action: {execution['action_ref']}")
print(f" Parameters: {execution.get('parameters')}")
print(f" Result: {execution.get('result')}")
# Final summary
print("\n=== Test Summary ===")
print(f"✓ Webhook trigger created")
print(f"✓ Webhook POST created event")
print(f"✓ Event payload correct")
print(f"✓ Execution completed successfully")
print(f"✓ Webhook data accessible in action")
print(f"✓ Test PASSED")
def test_multiple_webhook_posts(self, client: AttuneClient, pack_ref: str):
"""Test multiple webhook POSTs create multiple executions"""
print(f"\n=== T1.4b: Multiple Webhook POSTs ===")
num_posts = 3
# Create automation
print("\n[1/4] Setting up webhook automation...")
from datetime import datetime, timezone
test_start = datetime.now(timezone.utc).isoformat()
trigger = create_webhook_trigger(client=client, pack_ref=pack_ref)
action = create_echo_action(client=client, pack_ref=pack_ref)
rule = create_rule(
client=client,
trigger_id=trigger["id"],
action_ref=action["ref"],
pack_ref=pack_ref,
)
print(f"✓ Setup complete")
# Fire webhook multiple times
print(f"\n[2/4] Firing webhook {num_posts} times...")
for i in range(num_posts):
payload = {
"iteration": i + 1,
"message": f"Webhook post #{i + 1}",
"timestamp": time.time(),
}
client.fire_webhook(trigger_id=trigger["id"], payload=payload)
print(f" ✓ POST {i + 1}/{num_posts}")
time.sleep(0.5) # Small delay between posts
# Verify events created
print(f"\n[3/4] Verifying {num_posts} events created...")
events = wait_for_event_count(
client=client,
expected_count=num_posts,
trigger_id=trigger["id"],
timeout=15,
poll_interval=0.5,
)
print(f"{len(events)} events created")
assert len(events) >= num_posts
# Verify executions created
print(f"\n[4/4] Verifying {num_posts} executions completed...")
executions = wait_for_execution_count(
client=client,
expected_count=num_posts,
rule_id=rule["id"],
created_after=test_start,
timeout=20,
poll_interval=0.5,
verbose=True,
)
print(f"{len(executions)} executions created")
# Wait for all to complete
succeeded = 0
for execution in executions[:num_posts]:
if execution["status"] not in ["succeeded", "failed", "canceled"]:
execution = wait_for_execution_status(
client=client,
execution_id=execution["id"],
expected_status="succeeded",
timeout=10,
)
if execution["status"] == "succeeded":
succeeded += 1
print(f"{succeeded}/{num_posts} executions succeeded")
assert succeeded == num_posts
print("\n=== Test Summary ===")
print(f"{num_posts} webhook POSTs handled")
print(f"{num_posts} events created")
print(f"{num_posts} executions completed")
print(f"✓ Test PASSED")
def test_webhook_with_complex_payload(self, client: AttuneClient, pack_ref: str):
"""Test webhook with nested JSON payload"""
print(f"\n=== T1.4c: Webhook with Complex Payload ===")
# Setup
print("\n[1/3] Setting up webhook automation...")
from datetime import datetime, timezone
test_start = datetime.now(timezone.utc).isoformat()
trigger = create_webhook_trigger(client=client, pack_ref=pack_ref)
action = create_echo_action(client=client, pack_ref=pack_ref)
rule = create_rule(
client=client,
trigger_id=trigger["id"],
action_ref=action["ref"],
pack_ref=pack_ref,
)
print(f"✓ Setup complete")
# Complex nested payload
print("\n[2/3] Posting complex payload...")
complex_payload = {
"event": "user.signup",
"user": {
"id": 99999,
"email": "test@example.com",
"profile": {
"name": "Test User",
"age": 30,
"preferences": {
"theme": "dark",
"notifications": True,
},
},
"tags": ["new", "trial", "priority"],
},
"metadata": {
"source": "web",
"ip": "192.168.1.100",
"user_agent": "Mozilla/5.0",
},
"timestamp": "2024-01-01T00:00:00Z",
}
client.fire_webhook(trigger_id=trigger["id"], payload=complex_payload)
print(f"✓ Complex payload posted")
# Verify event and execution
print("\n[3/3] Verifying event and execution...")
events = wait_for_event_count(
client=client,
expected_count=1,
trigger_id=trigger["id"],
timeout=10,
)
assert len(events) >= 1
event = events[0]
event_payload = event.get("payload", {})
# Verify nested structure preserved
assert "user" in event_payload
assert "profile" in event_payload["user"]
assert "preferences" in event_payload["user"]["profile"]
assert event_payload["user"]["profile"]["preferences"]["theme"] == "dark"
assert event_payload["user"]["tags"] == ["new", "trial", "priority"]
print(f"✓ Complex nested payload preserved")
# Verify execution
executions = wait_for_execution_count(
client=client,
expected_count=1,
rule_id=rule["id"],
created_after=test_start,
timeout=15,
verbose=True,
)
execution = executions[0]
if execution["status"] not in ["succeeded", "failed", "canceled"]:
execution = wait_for_execution_status(
client=client,
execution_id=execution["id"],
expected_status="succeeded",
timeout=10,
)
assert execution["status"] == "succeeded"
print(f"✓ Execution completed successfully")
print("\n=== Test Summary ===")
print(f"✓ Complex nested payload handled")
print(f"✓ JSON structure preserved")
print(f"✓ Execution completed")
print(f"✓ Test PASSED")
def test_webhook_without_payload(self, client: AttuneClient, pack_ref: str):
"""Test webhook POST without payload (empty body)"""
print(f"\n=== T1.4d: Webhook without Payload ===")
# Setup
from datetime import datetime, timezone
test_start = datetime.now(timezone.utc).isoformat()
trigger = create_webhook_trigger(client=client, pack_ref=pack_ref)
action = create_echo_action(client=client, pack_ref=pack_ref)
rule = create_rule(
client=client,
trigger_id=trigger["id"],
action_ref=action["ref"],
pack_ref=pack_ref,
)
# Fire webhook with empty payload
print("\nFiring webhook with empty payload...")
client.fire_webhook(trigger_id=trigger["id"], payload={})
# Verify event created
events = wait_for_event_count(
client=client,
expected_count=1,
trigger_id=trigger["id"],
timeout=10,
)
assert len(events) >= 1
event = events[0]
print(f"✓ Event created with empty payload")
# Verify execution
executions = wait_for_execution_count(
client=client,
expected_count=1,
rule_id=rule["id"],
created_after=test_start,
timeout=15,
verbose=True,
)
execution = executions[0]
if execution["status"] not in ["succeeded", "failed", "canceled"]:
execution = wait_for_execution_status(
client=client,
execution_id=execution["id"],
expected_status="succeeded",
timeout=10,
)
assert execution["status"] == "succeeded"
print(f"✓ Execution succeeded with empty payload")
print(f"✓ Test PASSED")