336 lines
10 KiB
Python
336 lines
10 KiB
Python
"""
|
|
T3.2: Timer Cancellation Test
|
|
|
|
Tests that disabling a rule stops timer from executing, and re-enabling
|
|
resumes executions.
|
|
|
|
Priority: LOW
|
|
Duration: ~15 seconds
|
|
"""
|
|
|
|
import time
|
|
|
|
import pytest
|
|
from helpers.client import AttuneClient
|
|
from helpers.fixtures import create_echo_action, create_interval_timer, unique_ref
|
|
from helpers.polling import wait_for_execution_count
|
|
|
|
|
|
@pytest.mark.tier3
|
|
@pytest.mark.timer
|
|
@pytest.mark.rules
|
|
def test_timer_cancellation_via_rule_disable(client: AttuneClient, test_pack):
|
|
"""
|
|
Test that disabling a rule stops timer executions.
|
|
|
|
Flow:
|
|
1. Create interval timer (every 3 seconds)
|
|
2. Wait for 2 executions
|
|
3. Disable rule
|
|
4. Wait 10 seconds
|
|
5. Verify no new executions occurred
|
|
"""
|
|
print("\n" + "=" * 80)
|
|
print("T3.2a: Timer Cancellation via Rule Disable Test")
|
|
print("=" * 80)
|
|
|
|
pack_ref = test_pack["ref"]
|
|
|
|
# Step 1: Create interval timer and action
|
|
print("\n[STEP 1] Creating interval timer (every 3 seconds)...")
|
|
trigger_ref = f"cancel_timer_{unique_ref()}"
|
|
|
|
trigger_response = create_interval_timer(
|
|
client=client,
|
|
pack_ref=pack_ref,
|
|
trigger_ref=trigger_ref,
|
|
interval=3,
|
|
)
|
|
|
|
print(f"✓ Interval timer created: {trigger_ref}")
|
|
print(f" Interval: 3 seconds")
|
|
|
|
# Step 2: Create action and rule
|
|
print("\n[STEP 2] Creating action and rule...")
|
|
action_ref = create_echo_action(
|
|
client=client,
|
|
pack_ref=pack_ref,
|
|
message="Timer tick",
|
|
suffix="_cancel",
|
|
)
|
|
|
|
rule_data = {
|
|
"name": f"Timer Cancellation Test Rule {unique_ref()}",
|
|
"trigger": trigger_ref,
|
|
"action": action_ref,
|
|
"enabled": True,
|
|
}
|
|
|
|
rule_response = client.create_rule(rule_data)
|
|
rule_id = rule_response["id"]
|
|
print(f"✓ Rule created: {rule_id}")
|
|
print(f" Status: enabled")
|
|
|
|
# Step 3: Wait for 2 executions
|
|
print("\n[STEP 3] Waiting for 2 timer executions...")
|
|
wait_for_execution_count(
|
|
client=client,
|
|
action_ref=action_ref,
|
|
expected_count=2,
|
|
timeout=15,
|
|
operator=">=",
|
|
)
|
|
|
|
executions_before_disable = client.list_executions(action=action_ref)
|
|
print(f"✓ {len(executions_before_disable)} executions occurred")
|
|
|
|
# Step 4: Disable rule
|
|
print("\n[STEP 4] Disabling rule...")
|
|
update_data = {"enabled": False}
|
|
client.update_rule(rule_id, update_data)
|
|
print(f"✓ Rule disabled: {rule_id}")
|
|
|
|
# Step 5: Wait and verify no new executions
|
|
print("\n[STEP 5] Waiting 10 seconds to verify no new executions...")
|
|
time.sleep(10)
|
|
|
|
executions_after_disable = client.list_executions(action=action_ref)
|
|
new_executions = len(executions_after_disable) - len(executions_before_disable)
|
|
|
|
print(f" Executions before disable: {len(executions_before_disable)}")
|
|
print(f" Executions after disable: {len(executions_after_disable)}")
|
|
print(f" New executions: {new_executions}")
|
|
|
|
if new_executions == 0:
|
|
print(f"✓ No new executions (timer successfully stopped)")
|
|
else:
|
|
print(f"⚠ {new_executions} new execution(s) occurred after disable")
|
|
|
|
# Summary
|
|
print("\n" + "=" * 80)
|
|
print("TIMER CANCELLATION TEST SUMMARY")
|
|
print("=" * 80)
|
|
print(f"✓ Timer created: {trigger_ref} (3 second interval)")
|
|
print(f"✓ Rule disabled after {len(executions_before_disable)} executions")
|
|
print(f"✓ New executions after disable: {new_executions}")
|
|
|
|
if new_executions == 0:
|
|
print("\n✅ TIMER CANCELLATION WORKING!")
|
|
else:
|
|
print("\n⚠️ Timer may still be firing after rule disable")
|
|
|
|
print("=" * 80)
|
|
|
|
# Allow some tolerance for in-flight executions (1 execution max)
|
|
assert new_executions <= 1, (
|
|
f"Expected 0-1 new executions after disable, got {new_executions}"
|
|
)
|
|
|
|
|
|
@pytest.mark.tier3
|
|
@pytest.mark.timer
|
|
@pytest.mark.rules
|
|
def test_timer_resume_after_re_enable(client: AttuneClient, test_pack):
|
|
"""
|
|
Test that re-enabling a disabled rule resumes timer executions.
|
|
"""
|
|
print("\n" + "=" * 80)
|
|
print("T3.2b: Timer Resume After Re-enable Test")
|
|
print("=" * 80)
|
|
|
|
pack_ref = test_pack["ref"]
|
|
|
|
# Step 1: Create timer and rule
|
|
print("\n[STEP 1] Creating timer and rule...")
|
|
trigger_ref = f"resume_timer_{unique_ref()}"
|
|
|
|
create_interval_timer(
|
|
client=client,
|
|
pack_ref=pack_ref,
|
|
trigger_ref=trigger_ref,
|
|
interval=3,
|
|
)
|
|
|
|
action_ref = create_echo_action(
|
|
client=client,
|
|
pack_ref=pack_ref,
|
|
message="Resume test",
|
|
suffix="_resume",
|
|
)
|
|
|
|
rule_data = {
|
|
"name": f"Timer Resume Test Rule {unique_ref()}",
|
|
"trigger": trigger_ref,
|
|
"action": action_ref,
|
|
"enabled": True,
|
|
}
|
|
|
|
rule_response = client.create_rule(rule_data)
|
|
rule_id = rule_response["id"]
|
|
print(f"✓ Timer and rule created")
|
|
|
|
# Step 2: Wait for 1 execution
|
|
print("\n[STEP 2] Waiting for initial execution...")
|
|
wait_for_execution_count(
|
|
client=client,
|
|
action_ref=action_ref,
|
|
expected_count=1,
|
|
timeout=10,
|
|
operator=">=",
|
|
)
|
|
print(f"✓ Initial execution confirmed")
|
|
|
|
# Step 3: Disable rule
|
|
print("\n[STEP 3] Disabling rule...")
|
|
client.update_rule(rule_id, {"enabled": False})
|
|
time.sleep(1)
|
|
executions_after_disable = client.list_executions(action=action_ref)
|
|
count_after_disable = len(executions_after_disable)
|
|
print(f"✓ Rule disabled (executions: {count_after_disable})")
|
|
|
|
# Step 4: Wait while disabled
|
|
print("\n[STEP 4] Waiting 6 seconds while disabled...")
|
|
time.sleep(6)
|
|
executions_still_disabled = client.list_executions(action=action_ref)
|
|
count_still_disabled = len(executions_still_disabled)
|
|
increase_while_disabled = count_still_disabled - count_after_disable
|
|
print(f" Executions while disabled: {increase_while_disabled}")
|
|
|
|
# Step 5: Re-enable rule
|
|
print("\n[STEP 5] Re-enabling rule...")
|
|
client.update_rule(rule_id, {"enabled": True})
|
|
print(f"✓ Rule re-enabled")
|
|
|
|
# Step 6: Wait for new executions
|
|
print("\n[STEP 6] Waiting for executions to resume...")
|
|
time.sleep(8)
|
|
|
|
executions_after_enable = client.list_executions(action=action_ref)
|
|
count_after_enable = len(executions_after_enable)
|
|
increase_after_enable = count_after_enable - count_still_disabled
|
|
|
|
print(f" Executions before re-enable: {count_still_disabled}")
|
|
print(f" Executions after re-enable: {count_after_enable}")
|
|
print(f" New executions: {increase_after_enable}")
|
|
|
|
if increase_after_enable >= 1:
|
|
print(f"✓ Timer resumed (new executions after re-enable)")
|
|
else:
|
|
print(f"⚠ Timer did not resume")
|
|
|
|
# Summary
|
|
print("\n" + "=" * 80)
|
|
print("TIMER RESUME TEST SUMMARY")
|
|
print("=" * 80)
|
|
print(f"✓ Timer disabled: verified no new executions")
|
|
print(f"✓ Timer re-enabled: {increase_after_enable} new execution(s)")
|
|
|
|
if increase_after_enable >= 1:
|
|
print("\n✅ TIMER RESUME WORKING!")
|
|
else:
|
|
print("\n⚠️ Timer did not resume after re-enable")
|
|
|
|
print("=" * 80)
|
|
|
|
assert increase_after_enable >= 1, "Timer should resume after re-enable"
|
|
|
|
|
|
@pytest.mark.tier3
|
|
@pytest.mark.timer
|
|
@pytest.mark.rules
|
|
def test_timer_delete_stops_executions(client: AttuneClient, test_pack):
|
|
"""
|
|
Test that deleting a rule stops timer executions permanently.
|
|
"""
|
|
print("\n" + "=" * 80)
|
|
print("T3.2c: Timer Delete Stops Executions Test")
|
|
print("=" * 80)
|
|
|
|
pack_ref = test_pack["ref"]
|
|
|
|
# Step 1: Create timer and rule
|
|
print("\n[STEP 1] Creating timer and rule...")
|
|
trigger_ref = f"delete_timer_{unique_ref()}"
|
|
|
|
create_interval_timer(
|
|
client=client,
|
|
pack_ref=pack_ref,
|
|
trigger_ref=trigger_ref,
|
|
interval=3,
|
|
)
|
|
|
|
action_ref = create_echo_action(
|
|
client=client,
|
|
pack_ref=pack_ref,
|
|
message="Delete test",
|
|
suffix="_delete",
|
|
)
|
|
|
|
rule_data = {
|
|
"name": f"Timer Delete Test Rule {unique_ref()}",
|
|
"trigger": trigger_ref,
|
|
"action": action_ref,
|
|
"enabled": True,
|
|
}
|
|
|
|
rule_response = client.create_rule(rule_data)
|
|
rule_id = rule_response["id"]
|
|
print(f"✓ Timer and rule created")
|
|
|
|
# Step 2: Wait for 1 execution
|
|
print("\n[STEP 2] Waiting for initial execution...")
|
|
wait_for_execution_count(
|
|
client=client,
|
|
action_ref=action_ref,
|
|
expected_count=1,
|
|
timeout=10,
|
|
operator=">=",
|
|
)
|
|
|
|
executions_before_delete = client.list_executions(action=action_ref)
|
|
print(f"✓ Initial executions: {len(executions_before_delete)}")
|
|
|
|
# Step 3: Delete rule
|
|
print("\n[STEP 3] Deleting rule...")
|
|
try:
|
|
client.delete_rule(rule_id)
|
|
print(f"✓ Rule deleted: {rule_id}")
|
|
except Exception as e:
|
|
print(f"⚠ Rule deletion failed: {e}")
|
|
pytest.skip("Rule deletion not available")
|
|
|
|
# Step 4: Wait and verify no new executions
|
|
print("\n[STEP 4] Waiting 10 seconds to verify no new executions...")
|
|
time.sleep(10)
|
|
|
|
executions_after_delete = client.list_executions(action=action_ref)
|
|
new_executions = len(executions_after_delete) - len(executions_before_delete)
|
|
|
|
print(f" Executions before delete: {len(executions_before_delete)}")
|
|
print(f" Executions after delete: {len(executions_after_delete)}")
|
|
print(f" New executions: {new_executions}")
|
|
|
|
if new_executions == 0:
|
|
print(f"✓ No new executions (timer permanently stopped)")
|
|
else:
|
|
print(f"⚠ {new_executions} new execution(s) after rule deletion")
|
|
|
|
# Summary
|
|
print("\n" + "=" * 80)
|
|
print("TIMER DELETE TEST SUMMARY")
|
|
print("=" * 80)
|
|
print(f"✓ Rule deleted: {rule_id}")
|
|
print(f"✓ New executions after delete: {new_executions}")
|
|
|
|
if new_executions == 0:
|
|
print("\n✅ TIMER DELETION STOPS EXECUTIONS!")
|
|
else:
|
|
print("\n⚠️ Timer may still fire after rule deletion")
|
|
|
|
print("=" * 80)
|
|
|
|
# Allow 1 in-flight execution tolerance
|
|
assert new_executions <= 1, (
|
|
f"Expected 0-1 new executions after delete, got {new_executions}"
|
|
)
|