Skip to content

Commit 7a15574

Browse files
committed
feat: add toJson() to all Event subclasses and fix NotifyEvent constructor inconsistency
1 parent f04fb9e commit 7a15574

38 files changed

Lines changed: 261 additions & 11 deletions

packages/webtrit_signaling/AGENTS.md

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,16 @@ or disconnect.
6666
All extend `WebtritSignalingException`:
6767

6868
- `WebtritSignalingErrorException` — server returned error code
69-
- `WebtritSignalingDisconnectedException`socket closed unexpectedly
70-
- `WebtritSignalingTransactionTimeoutException`no response within timeout
71-
- `WebtritSignalingKeepaliveTransactionTimeoutException`keepalive timed out
69+
- `WebtritSignalingDisconnectedException``execute()` called after `disconnect()` (subscription already nil)
70+
- `WebtritSignalingUnknownMessageException`unrecognised inbound message kind
71+
- `WebtritSignalingUnknownResponseException`response with no matching transaction
7272
- `WebtritSignalingBadStateException` — API misuse (e.g. calling `listen()` twice)
73+
- `WebtritSignalingTransactionException` ← abstract base for transaction errors
74+
- `WebtritSignalingTransactionTimeoutException` — no response within 10 s timeout
75+
- `WebtritSignalingKeepaliveTransactionTimeoutException` — keepalive timed out
76+
- `WebtritSignalingTransactionUnavailableException` — transaction slot unavailable
77+
- `WebtritSignalingTransactionTerminateException` ← abstract base for terminations
78+
- `WebtritSignalingTransactionTerminateByDisconnectException` — in-flight transaction cancelled because socket closed
7379

7480
## Constraints
7581

@@ -78,13 +84,38 @@ All extend `WebtritSignalingException`:
7884
- Callback API, not streams. `listen()` is single-use.
7985
- Outbound JSON: requests implement `toJson()` — do not bypass.
8086

87+
## Static Helpers
88+
89+
```dart
90+
WebtritSignalingClient.generateTransactionId() // random ID for Request.transaction
91+
WebtritSignalingClient.generateCallId() // random 24-char call ID
92+
WebtritSignalingClient.buildTenantUrl(base, id) // appends /tenant/{id} (@visibleForTesting)
93+
WebtritSignalingClient.subprotocol // 'webtrit-protocol'
94+
WebtritSignalingClient.defaultExecuteTransactionTimeoutDuration // 10 s
95+
```
96+
8197
## Test Patterns
8298

99+
### Unit tests
100+
83101
- JSON fixtures in `test/src/events/event_jsons.dart` and `error_event_jsons.dart`.
84102
- `MockWebSocketChannel` + `MockWebSocketSink` via `mocktail`; `StreamController` injects server messages.
85103
- `fake_async` for deterministic keepalive/timeout tests.
86104
- `FakeWebtritSignalingClient` in `test/src/factories/` for higher-level tests.
87105

106+
### Live tests (`test/live_call_test.dart`, tag `live`)
107+
108+
Two-client strategy — `clientA` (caller) and `clientB` (callee) connect to a real server.
109+
Callee answers programmatically via `AcceptRequest` (no auto-answer on the test server).
110+
111+
Key patterns:
112+
113+
- Pre-register all `awaitEvent<T>()` futures **before** sending any request to avoid missing events on a broadcast stream.
114+
- Fetch fresh session tokens in each `setUp` — WebTrit tokens are invalidated when the WebSocket session closes.
115+
- Use `force=false` in `connect()``tearDown` disconnects cleanly so no stale sessions exist.
116+
- `_LiveClient.awaitEvent<T>` wraps the broadcast stream with a `Completer`; fails immediately on `CallErrorEvent / LineErrorEvent / SessionErrorEvent`.
117+
- Cleanup after tests where server behaviour is non-deterministic (multi-endpoint SIP accounts) uses `try/catch` around `HangupRequest` instead of awaiting `HangupEvent`.
118+
88119
## Commands
89120

90121
```bash
@@ -93,6 +124,14 @@ dart test test/webtrit_signaling_test.dart
93124
dart test --name "buildTenantUrl"
94125
dart analyze
95126
dart format .
127+
128+
# Live integration tests (requires real server credentials)
129+
WEBTRIT_APP_TEST_CUSTOM_CORE_URL=<host> \
130+
WEBTRIT_APP_TEST_EMAIL_CREDENTIAL=<caller> \
131+
WEBTRIT_APP_TEST_EMAIL_VERIFY_CREDENTIAL=<caller-password> \
132+
WEBTRIT_APP_TEST_CALLEE_CREDENTIAL=<callee> \
133+
WEBTRIT_APP_TEST_CALLEE_VERIFY_CREDENTIAL=<callee-password> \
134+
dart test test/live_call_test.dart --tags live --reporter expanded
96135
```
97136

98137
Connection endpoint: `wss://<host>/signaling/v1?token=<token>[&force=<true|false>]`

packages/webtrit_signaling/lib/src/events/call/accepted_event.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ class AcceptedEvent extends CallEvent {
1919

2020
static const typeValue = 'accepted';
2121

22+
@override
23+
Map<String, dynamic> toJson() => {
24+
...callBaseJson(typeValue),
25+
if (callee != null) 'callee': callee,
26+
if (isFocus != null) 'is_focus': isFocus,
27+
if (jsep != null) 'jsep': jsep,
28+
};
29+
2230
factory AcceptedEvent.fromJson(Map<String, dynamic> json) {
2331
final eventTypeValue = json[Event.typeKey];
2432
if (eventTypeValue != typeValue) {

packages/webtrit_signaling/lib/src/events/call/accepting_event.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ class AcceptingEvent extends CallEvent {
55

66
static const typeValue = 'accepting';
77

8+
@override
9+
Map<String, dynamic> toJson() => callBaseJson(typeValue);
10+
811
factory AcceptingEvent.fromJson(Map<String, dynamic> json) {
912
final eventTypeValue = json[Event.typeKey];
1013
if (eventTypeValue != typeValue) {

packages/webtrit_signaling/lib/src/events/call/call_error_event.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ class CallErrorEvent extends CallEvent implements ErrorEvent {
1717
@override
1818
List<Object?> get props => [...super.props, code, reason];
1919

20+
@override
21+
Map<String, dynamic> toJson() => {...callBaseJson(ErrorEvent.typeValue), 'code': code, 'reason': reason};
22+
2023
static CallErrorEvent? tryFromJson(Map<String, dynamic> json) {
2124
final eventTypeValue = json[Event.typeKey];
2225
if (eventTypeValue != ErrorEvent.typeValue) {

packages/webtrit_signaling/lib/src/events/call/calling_event.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ class CallingEvent extends CallEvent {
55

66
static const typeValue = 'calling';
77

8+
@override
9+
Map<String, dynamic> toJson() => callBaseJson(typeValue);
10+
811
factory CallingEvent.fromJson(Map<String, dynamic> json) {
912
final eventTypeValue = json[Event.typeKey];
1013
if (eventTypeValue != typeValue) {

packages/webtrit_signaling/lib/src/events/call/declining_event.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ class DecliningEvent extends CallEvent {
1717

1818
static const typeValue = 'declining';
1919

20+
@override
21+
Map<String, dynamic> toJson() => {...callBaseJson(typeValue), 'code': code, if (referId != null) 'refer_id': referId};
22+
2023
factory DecliningEvent.fromJson(Map<String, dynamic> json) {
2124
final eventTypeValue = json[Event.typeKey];
2225
if (eventTypeValue != typeValue) {

packages/webtrit_signaling/lib/src/events/call/hangingup_event.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ class HangingupEvent extends CallEvent {
55

66
static const typeValue = 'hangingup';
77

8+
@override
9+
Map<String, dynamic> toJson() => callBaseJson(typeValue);
10+
811
factory HangingupEvent.fromJson(Map<String, dynamic> json) {
912
final eventTypeValue = json[Event.typeKey];
1013
if (eventTypeValue != typeValue) {

packages/webtrit_signaling/lib/src/events/call/hangup_event.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ class HangupEvent extends CallEvent {
1717

1818
static const typeValue = 'hangup';
1919

20+
@override
21+
Map<String, dynamic> toJson() => {...callBaseJson(typeValue), 'code': code, 'reason': reason};
22+
2023
factory HangupEvent.fromJson(Map<String, dynamic> json) {
2124
final eventTypeValue = json[Event.typeKey];
2225
if (eventTypeValue != typeValue) {

packages/webtrit_signaling/lib/src/events/call/holding_event.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ class HoldingEvent extends CallEvent {
55

66
static const typeValue = 'holding';
77

8+
@override
9+
Map<String, dynamic> toJson() => callBaseJson(typeValue);
10+
811
factory HoldingEvent.fromJson(Map<String, dynamic> json) {
912
final eventTypeValue = json[Event.typeKey];
1013
if (eventTypeValue != typeValue) {

packages/webtrit_signaling/lib/src/events/call/incoming_call_event.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ class IncomingCallEvent extends CallEvent {
3636

3737
static const typeValue = 'incoming_call';
3838

39+
@override
40+
Map<String, dynamic> toJson() => {
41+
...callBaseJson(typeValue),
42+
'callee': callee,
43+
'caller': caller,
44+
if (callerDisplayName != null) 'caller_display_name': callerDisplayName,
45+
if (referredBy != null) 'referred_by': referredBy,
46+
if (replaceCallId != null) 'replace_call_id': replaceCallId,
47+
if (isFocus != null) 'is_focus': isFocus,
48+
if (jsep != null) 'jsep': jsep,
49+
};
50+
3951
factory IncomingCallEvent.fromJson(Map<String, dynamic> json) {
4052
final eventTypeValue = json[Event.typeKey];
4153
if (eventTypeValue != typeValue) {

0 commit comments

Comments
 (0)