Skip to content

Commit a258f4f

Browse files
Add tests for stasis_broadcast modules
Tests parallel broadcast to multiple ARI applications and verifies first-claim-wins semantics, CallBroadcast and CallClaimed event generation, and proper channel control transfer to the claiming app. The test: - Originates a channel that calls StasisBroadcast() - Verifies CallBroadcast event is sent to multiple apps - Claims the channel via POST /ari/events/claim - Verifies CallClaimed event is sent - Confirms channel enters Stasis in the claiming app"
1 parent 3877a8f commit a258f4f

File tree

4 files changed

+194
-0
lines changed

4 files changed

+194
-0
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[default]
2+
exten => s,1,NoOp()
3+
same => n,Answer()
4+
same => n,Stasis(testsuite)
5+
same => n,Hangup()
6+
7+
exten => trigger,1,NoOp()
8+
same => n,Answer()
9+
same => n,Stasis(testsuite-app1)
10+
same => n,Hangup()
11+
12+
exten => broadcast,1,NoOp()
13+
same => n,StasisBroadcast(timeout=1000,app_filter=^testsuite-.*)
14+
same => n,GotoIf($["${BROADCAST_WINNER}"=""]?no_route)
15+
same => n,Stasis(${BROADCAST_WINNER})
16+
same => n,Hangup()
17+
same => n(no_route),NoOp(No application claimed)
18+
same => n,Hangup()
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
"""
2+
Stasis broadcast test callbacks
3+
4+
Tests that channels can be broadcast to multiple ARI applications
5+
and that first-claim-wins semantics work correctly.
6+
"""
7+
8+
import logging
9+
10+
LOGGER = logging.getLogger(__name__)
11+
12+
channel_id = None
13+
broadcast_received = False
14+
claim_received = False
15+
trigger_channel_id = None
16+
17+
18+
def on_trigger_start(ari, event, test_object):
19+
"""Handle the initial trigger channel entering Stasis - start the test"""
20+
global trigger_channel_id
21+
22+
LOGGER.info("Trigger channel entered Stasis: %s" % event)
23+
trigger_channel_id = event['channel']['id']
24+
25+
# Originate the channel that will be broadcast via AMI
26+
LOGGER.info("Originating broadcast channel via AMI")
27+
ami = test_object.ami[0]
28+
ami.originate(
29+
channel='Local/broadcast@default',
30+
application='Echo',
31+
timeout=30000
32+
).addErrback(test_object.handle_originate_failure)
33+
34+
# Don't delete the trigger channel yet - keep it alive until broadcast completes
35+
36+
return True
37+
38+
39+
def on_broadcast(ari, event, test_object):
40+
"""Handle CallBroadcast event"""
41+
global channel_id, broadcast_received
42+
43+
LOGGER.info("CallBroadcast received for app %s: %s" % (event['application'], event))
44+
broadcast_received = True
45+
channel_id = event['channel']['id']
46+
47+
# Each app that receives the broadcast tries to claim - first one wins
48+
LOGGER.info("Attempting to claim channel %s for application %s" % (channel_id, event['application']))
49+
try:
50+
ari.post('events/claim', channelId=channel_id, application=event['application'])
51+
LOGGER.info("Claim succeeded for %s" % event['application'])
52+
except Exception as e:
53+
# 404 or 409 is expected if another app claimed first - this is normal!
54+
LOGGER.info("Claim failed for %s (expected if another app won): %s" % (event['application'], e))
55+
56+
return True
57+
58+
59+
def on_claimed(ari, event, test_object):
60+
"""Handle CallClaimed event"""
61+
global claim_received
62+
63+
LOGGER.info("CallClaimed received: %s" % event)
64+
claim_received = True
65+
66+
return True
67+
68+
69+
def on_start(ari, event, test_object):
70+
"""Handle StasisStart event - channel entered Stasis after being claimed"""
71+
global channel_id, claim_received, broadcast_received, trigger_channel_id
72+
73+
LOGGER.info("StasisStart received: %s" % event)
74+
channel_id = event['channel']['id']
75+
76+
# Test passes when we get StasisStart after broadcast and claim
77+
if broadcast_received and claim_received:
78+
LOGGER.info("SUCCESS: Channel entered Stasis after being claimed")
79+
# Delete both the broadcast channel and the trigger channel
80+
try:
81+
ari.delete('channels', channel_id)
82+
except Exception as e:
83+
LOGGER.info("Broadcast channel delete failed (may have already hung up): %s" % e)
84+
try:
85+
if trigger_channel_id:
86+
ari.delete('channels', trigger_channel_id)
87+
except Exception as e:
88+
LOGGER.info("Trigger channel delete failed (may have already hung up): %s" % e)
89+
test_object.set_passed(True)
90+
test_object.stop_reactor()
91+
else:
92+
LOGGER.error("StasisStart received but broadcast/claim not complete")
93+
test_object.set_passed(False)
94+
test_object.stop_reactor()
95+
96+
return True
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
testinfo:
2+
summary: 'Test Stasis broadcast and claim functionality'
3+
description: |
4+
Tests that channels can be broadcast to multiple ARI applications
5+
and that first-claim-wins semantics work correctly. Verifies
6+
CallBroadcast and CallClaimed events are generated.
7+
8+
test-modules:
9+
add-test-to-search-path: True
10+
test-object:
11+
config-section: test-object-config
12+
typename: 'ari.AriTestObject'
13+
modules:
14+
-
15+
config-section: ari-config
16+
typename: 'ari.WebSocketEventModule'
17+
18+
test-object-config:
19+
apps: testsuite,testsuite-app1,testsuite-app2
20+
stop-on-end: True
21+
test-iterations:
22+
-
23+
channel: 'Local/s@default'
24+
application: 'Echo'
25+
26+
ari-config:
27+
apps: testsuite,testsuite-app1,testsuite-app2
28+
events:
29+
-
30+
conditions:
31+
match:
32+
type: 'StasisStart'
33+
application: 'testsuite'
34+
args: []
35+
channel:
36+
name: 'Local/s@default.*'
37+
count: 1
38+
callback:
39+
module: stasis_broadcast_test
40+
method: on_trigger_start
41+
-
42+
conditions:
43+
match:
44+
type: 'CallBroadcast'
45+
count: 2
46+
callback:
47+
module: stasis_broadcast_test
48+
method: on_broadcast
49+
-
50+
conditions:
51+
match:
52+
type: 'CallClaimed'
53+
count: 3
54+
callback:
55+
module: stasis_broadcast_test
56+
method: on_claimed
57+
-
58+
conditions:
59+
match:
60+
type: 'StasisStart'
61+
application: 'testsuite-app1'
62+
count: 1
63+
callback:
64+
module: stasis_broadcast_test
65+
method: on_start
66+
67+
properties:
68+
dependencies:
69+
- python: 'autobahn.websocket'
70+
- python: 'requests'
71+
- python: 'twisted'
72+
- python: 'starpy'
73+
- asterisk: 'res_ari_channels'
74+
- asterisk: 'res_ari_events'
75+
- asterisk: 'res_stasis_broadcast'
76+
- asterisk: 'app_stasis_broadcast'
77+
tags:
78+
- ARI
79+
- stasis

tests/rest_api/tests.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ tests:
1919
- dir: 'message'
2020
- dir: 'external_interaction'
2121
- test: 'move'
22+
- test: 'stasis_broadcast'
2223

0 commit comments

Comments
 (0)