Skip to content

Commit 3c8ea9c

Browse files
committed
Update SipServiceClient grants. Add new SipServiceClient integration tests
1 parent 7f06890 commit 3c8ea9c

File tree

4 files changed

+282
-7
lines changed

4 files changed

+282
-7
lines changed

LivekitApi.Tests/IngressServiceClient.Test.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public async Task Create_Ingress_Url()
4747
ParticipantName = "ingress-name",
4848
InputType = IngressInput.UrlInput,
4949
Url = url,
50+
Enabled = true,
5051
}
5152
);
5253
Assert.NotNull(ingress.IngressId);

LivekitApi.Tests/ServiceClientFixture.cs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public class ServiceClientFixture : IDisposable
2121
private const string LIVEKIT_SERVER_IMAGE = "livekit/livekit-server:latest";
2222
private const string LIVEKIT_EGRESS_IMAGE = "livekit/egress:latest";
2323
private const string LIVEKIT_INGRESS_IMAGE = "livekit/ingress:latest";
24+
private const string LIVEKIT_SIP_IMAGE = "livekit/sip:latest";
2425
private const string LIVEKIT_CLI_IMAGE = "livekit/livekit-cli:latest";
2526
private const string REDIS_IMAGE = "redis:latest";
2627

@@ -52,10 +53,24 @@ public class ServiceClientFixture : IDisposable
5253
http_relay_port: 9090
5354
health_port: 9091";
5455

56+
private string sipYaml =
57+
@"api_key: "
58+
+ TEST_API_KEY
59+
+ @"
60+
api_secret: "
61+
+ TEST_API_SECRET
62+
+ @"
63+
ws_url: {WS_URL}
64+
redis:
65+
address: {REDIS_ADDRESS}
66+
sip_port: 5060
67+
rtp_port: 10000-20000";
68+
5569
private IContainer redisContainer;
5670
private IContainer livekitServerContainer;
5771
private IContainer egressContainer;
5872
private IContainer ingressContainer;
73+
private IContainer sipContainer;
5974

6075
public ServiceClientFixture()
6176
{
@@ -136,7 +151,28 @@ public ServiceClientFixture()
136151
)
137152
)
138153
.Build();
139-
Task.WaitAll(egressContainer.StartAsync(), ingressContainer.StartAsync());
154+
// Sip
155+
sipYaml = sipYaml
156+
.Replace("{WS_URL}", "ws://" + livekitServerContainer.IpAddress + ":7880")
157+
.Replace("{REDIS_ADDRESS}", redisContainer.IpAddress + ":6379");
158+
sipContainer = new ContainerBuilder()
159+
.WithImage(LIVEKIT_SIP_IMAGE)
160+
.WithName("sip")
161+
.WithAutoRemove(true)
162+
.WithEnvironment("SIP_CONFIG_BODY", sipYaml)
163+
.WithPortBinding(5060, true)
164+
.DependsOn(redisContainer)
165+
.DependsOn(livekitServerContainer)
166+
.WithWaitStrategy(
167+
Wait.ForUnixContainer().UntilMessageIsLogged(".*sip signaling listening on.*")
168+
)
169+
.Build();
170+
171+
Task.WaitAll(
172+
egressContainer.StartAsync(),
173+
ingressContainer.StartAsync(),
174+
sipContainer.StartAsync()
175+
);
140176
}
141177

142178
public void Dispose()
@@ -158,6 +194,10 @@ public void Dispose()
158194
{
159195
tasks.Add(ingressContainer.DisposeAsync().AsTask());
160196
}
197+
if (sipContainer != null)
198+
{
199+
tasks.Add(sipContainer.DisposeAsync().AsTask());
200+
}
161201
Task.WhenAll(tasks).Wait();
162202
}
163203

LivekitApi.Tests/SipServiceClient.Test.cs

Lines changed: 235 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,32 @@
11
using Google.Protobuf;
2+
using Google.Protobuf.Collections;
3+
using Xunit;
4+
using Xunit.Abstractions;
25

36
namespace Livekit.Server.Sdk.Dotnet.Test
47
{
58
[Collection("Integration tests")]
69
public class SipServiceClientTest
710
{
811
private ServiceClientFixture fixture;
12+
private readonly ITestOutputHelper output;
913

10-
public SipServiceClientTest(ServiceClientFixture fixture)
14+
public SipServiceClientTest(ServiceClientFixture fixture, ITestOutputHelper output)
1115
{
1216
this.fixture = fixture;
17+
this.output = output;
1318
}
1419

1520
private SipServiceClient sipClient = new SipServiceClient(
1621
ServiceClientFixture.TEST_HTTP_URL,
1722
ServiceClientFixture.TEST_API_KEY,
1823
ServiceClientFixture.TEST_API_SECRET
1924
);
25+
private readonly RoomServiceClient roomClient = new RoomServiceClient(
26+
ServiceClientFixture.TEST_HTTP_URL,
27+
ServiceClientFixture.TEST_API_KEY,
28+
ServiceClientFixture.TEST_API_SECRET
29+
);
2030

2131
[Fact]
2232
[Trait("Category", "Integration")]
@@ -67,6 +77,23 @@ public async Task Create_Sip_Inbound_Trunk()
6777
Assert.Equal(request.Trunk.AllowedNumbers, response.AllowedNumbers);
6878
}
6979

80+
[Fact]
81+
[Trait("Category", "Integration")]
82+
[Trait("Category", "SipService")]
83+
public async Task Create_Sip_Inbound_Trunk_Exceptions()
84+
{
85+
Twirp.Exception ex = await Assert.ThrowsAsync<Twirp.Exception>(
86+
async () =>
87+
await sipClient.CreateSIPInboundTrunk(
88+
new CreateSIPInboundTrunkRequest { Trunk = new SIPInboundTrunkInfo { } }
89+
)
90+
);
91+
Assert.Equal(
92+
"for security, one of the fields must be set: AuthUsername+AuthPassword, AllowedAddresses or Numbers",
93+
ex.Message
94+
);
95+
}
96+
7097
[Fact]
7198
[Trait("Category", "Integration")]
7299
[Trait("Category", "SipService")]
@@ -92,13 +119,50 @@ public async Task Create_Sip_Outbound_Trunk()
92119
Assert.Equal(request.Trunk.AuthPassword, response.AuthPassword);
93120
}
94121

122+
[Fact]
123+
[Trait("Category", "Integration")]
124+
[Trait("Category", "SipService")]
125+
public async Task Create_Sip_Outbound_Trunk_Exceptions()
126+
{
127+
Twirp.Exception ex = await Assert.ThrowsAsync<Twirp.Exception>(
128+
async () =>
129+
await sipClient.CreateSIPOutboundTrunk(
130+
new CreateSIPOutboundTrunkRequest { Trunk = new SIPOutboundTrunkInfo { } }
131+
)
132+
);
133+
Assert.Equal("no trunk numbers specified", ex.Message);
134+
ex = await Assert.ThrowsAsync<Twirp.Exception>(
135+
async () =>
136+
await sipClient.CreateSIPOutboundTrunk(
137+
new CreateSIPOutboundTrunkRequest
138+
{
139+
Trunk = new SIPOutboundTrunkInfo { Address = "my-test-trunk.com" },
140+
}
141+
)
142+
);
143+
Assert.Equal("no trunk numbers specified", ex.Message);
144+
ex = await Assert.ThrowsAsync<Twirp.Exception>(
145+
async () =>
146+
await sipClient.CreateSIPOutboundTrunk(
147+
new CreateSIPOutboundTrunkRequest
148+
{
149+
Trunk = new SIPOutboundTrunkInfo { Numbers = { "+111", "+222" } },
150+
}
151+
)
152+
);
153+
Assert.Equal("no outbound address specified", ex.Message);
154+
}
155+
95156
[Fact]
96157
[Trait("Category", "Integration")]
97158
[Trait("Category", "SipService")]
98159
public async Task Get_Sip_Inbound_Trunk()
99160
{
100161
var inboundTrunk = await sipClient.CreateSIPInboundTrunk(
101-
new CreateSIPInboundTrunkRequest { Trunk = new SIPInboundTrunkInfo { } }
162+
new CreateSIPInboundTrunkRequest
163+
{
164+
Trunk = new SIPInboundTrunkInfo { Numbers = { "+111", "+222" } },
165+
}
102166
);
103167
var getRequest = new GetSIPInboundTrunkRequest { SipTrunkId = inboundTrunk.SipTrunkId };
104168
var response = await sipClient.GetSIPInboundTrunk(getRequest);
@@ -112,7 +176,14 @@ public async Task Get_Sip_Inbound_Trunk()
112176
public async Task Get_Sip_Outound_Trunk()
113177
{
114178
var outboundTrunk = await sipClient.CreateSIPOutboundTrunk(
115-
new CreateSIPOutboundTrunkRequest { Trunk = new SIPOutboundTrunkInfo { } }
179+
new CreateSIPOutboundTrunkRequest
180+
{
181+
Trunk = new SIPOutboundTrunkInfo
182+
{
183+
Numbers = { "+111", "+222" },
184+
Address = "my-test-trunk.com",
185+
},
186+
}
116187
);
117188
var getRequest = new GetSIPOutboundTrunkRequest
118189
{
@@ -129,7 +200,10 @@ public async Task Get_Sip_Outound_Trunk()
129200
public async Task Delete_Sip_Trunk()
130201
{
131202
var trunk = await sipClient.CreateSIPInboundTrunk(
132-
new CreateSIPInboundTrunkRequest { Trunk = new SIPInboundTrunkInfo { } }
203+
new CreateSIPInboundTrunkRequest
204+
{
205+
Trunk = new SIPInboundTrunkInfo { Numbers = { "+111", "+222" } },
206+
}
133207
);
134208
var allTrunks = await sipClient.ListSIPInboundTrunk(new ListSIPInboundTrunkRequest { });
135209
Assert.Contains(allTrunks.Items, t => t.SipTrunkId == trunk.SipTrunkId);
@@ -185,5 +259,162 @@ public async Task Dispatch_Rule()
185259
r => r.SipDispatchRuleId == dispatchRule.SipDispatchRuleId
186260
);
187261
}
262+
263+
[Fact]
264+
[Trait("Category", "Integration")]
265+
[Trait("Category", "SipService")]
266+
public async Task Create_Sip_Participant()
267+
{
268+
Twirp.Exception ex = await Assert.ThrowsAsync<Twirp.Exception>(
269+
async () =>
270+
await sipClient.CreateSIPParticipant(
271+
new CreateSIPParticipantRequest { SipTrunkId = "non-existing-trunk" }
272+
)
273+
);
274+
Assert.Equal("requested sip trunk does not exist", ex.Message);
275+
276+
SIPOutboundTrunkInfo trunk = await sipClient.CreateSIPOutboundTrunk(
277+
new CreateSIPOutboundTrunkRequest
278+
{
279+
Trunk = new SIPOutboundTrunkInfo
280+
{
281+
Name = "Demo outbound trunk",
282+
Address = "my-test-trunk.com",
283+
Numbers = { "+1234567890" },
284+
AuthUsername = "username",
285+
AuthPassword = "password",
286+
},
287+
}
288+
);
289+
290+
CreateSIPParticipantRequest request = new CreateSIPParticipantRequest
291+
{
292+
SipTrunkId = "trunk",
293+
};
294+
295+
ex = await Assert.ThrowsAsync<Twirp.Exception>(
296+
async () =>
297+
await sipClient.CreateSIPParticipant(
298+
new CreateSIPParticipantRequest { SipTrunkId = "non-existing-trunk" }
299+
)
300+
);
301+
Assert.Equal("requested sip trunk does not exist", ex.Message);
302+
303+
request.SipTrunkId = trunk.SipTrunkId;
304+
305+
ex = await Assert.ThrowsAsync<Twirp.Exception>(
306+
async () => await sipClient.CreateSIPParticipant(request)
307+
);
308+
Assert.Equal("call-to number must be set", ex.Message);
309+
310+
request.SipCallTo = "+3333";
311+
312+
ex = await Assert.ThrowsAsync<Twirp.Exception>(
313+
async () => await sipClient.CreateSIPParticipant(request)
314+
);
315+
Assert.Equal("room name must be set", ex.Message);
316+
317+
request.RoomName = TestConstants.ROOM_NAME;
318+
319+
ex = await Assert.ThrowsAsync<Twirp.Exception>(
320+
async () => await sipClient.CreateSIPParticipant(request)
321+
);
322+
Assert.Equal("update room failed: identity cannot be empty", ex.Message);
323+
324+
request.ParticipantIdentity = TestConstants.PARTICIPANT_IDENTITY;
325+
326+
request.ParticipantName = "Test Caller";
327+
request.SipNumber = "+1111";
328+
request.RingingTimeout = new Google.Protobuf.WellKnownTypes.Duration { Seconds = 10 };
329+
request.PlayDialtone = true;
330+
request.ParticipantMetadata = "meta";
331+
request.ParticipantAttributes.Add("extra", "1");
332+
request.MediaEncryption = SIPMediaEncryption.SipMediaEncryptRequire;
333+
request.MaxCallDuration = new Google.Protobuf.WellKnownTypes.Duration { Seconds = 99 };
334+
request.KrispEnabled = true;
335+
request.IncludeHeaders = SIPHeaderOptions.SipAllHeaders;
336+
request.Dtmf = "1234#";
337+
request.HidePhoneNumber = true;
338+
request.Headers.Add("X-A", "A");
339+
340+
SIPParticipantInfo sipParticipantInfo = await sipClient.CreateSIPParticipant(request);
341+
342+
Assert.NotNull(sipParticipantInfo);
343+
Assert.Equal(TestConstants.PARTICIPANT_IDENTITY, request.ParticipantIdentity);
344+
Assert.Equal(TestConstants.ROOM_NAME, request.RoomName);
345+
346+
var sipParticipant = await roomClient.GetParticipant(
347+
new RoomParticipantIdentity
348+
{
349+
Room = TestConstants.ROOM_NAME,
350+
Identity = TestConstants.PARTICIPANT_IDENTITY,
351+
}
352+
);
353+
Assert.NotNull(sipParticipant);
354+
Assert.Equal(TestConstants.PARTICIPANT_IDENTITY, sipParticipant.Identity);
355+
Assert.Equal("Test Caller", sipParticipant.Name);
356+
Assert.Equal("meta", sipParticipant.Metadata);
357+
Assert.Equal("1", sipParticipant.Attributes["extra"]);
358+
}
359+
360+
[Fact]
361+
[Trait("Category", "Integration")]
362+
[Trait("Category", "SipService")]
363+
public async Task Transfer_Sip_Participant()
364+
{
365+
var trunk = await sipClient.CreateSIPOutboundTrunk(
366+
new CreateSIPOutboundTrunkRequest
367+
{
368+
Trunk = new SIPOutboundTrunkInfo
369+
{
370+
Name = "Demo outbound trunk",
371+
Address = "my-test-trunk.com",
372+
Numbers = { "+1234567890" },
373+
AuthUsername = "username",
374+
AuthPassword = "password",
375+
},
376+
}
377+
);
378+
379+
var request = new CreateSIPParticipantRequest
380+
{
381+
SipTrunkId = trunk.SipTrunkId,
382+
SipCallTo = "+3333",
383+
RoomName = TestConstants.ROOM_NAME,
384+
ParticipantIdentity = TestConstants.PARTICIPANT_IDENTITY,
385+
ParticipantName = "Test Caller",
386+
SipNumber = "+1111",
387+
};
388+
389+
SIPParticipantInfo sipParticipantInfo = await sipClient.CreateSIPParticipant(request);
390+
391+
var transferRequest = new TransferSIPParticipantRequest { };
392+
393+
Twirp.Exception ex = await Assert.ThrowsAsync<Twirp.Exception>(
394+
async () => await sipClient.TransferSIPParticipant(transferRequest)
395+
);
396+
Assert.Equal("Missing room name", ex.Message);
397+
398+
transferRequest.RoomName = TestConstants.ROOM_NAME;
399+
400+
ex = await Assert.ThrowsAsync<Twirp.Exception>(
401+
async () => await sipClient.TransferSIPParticipant(transferRequest)
402+
);
403+
Assert.Equal("Missing participant identity", ex.Message);
404+
405+
transferRequest.ParticipantIdentity = TestConstants.PARTICIPANT_IDENTITY;
406+
transferRequest.TransferTo = "+14155550100";
407+
transferRequest.PlayDialtone = false;
408+
409+
ex = await Assert.ThrowsAsync<Twirp.Exception>(
410+
async () => await sipClient.TransferSIPParticipant(transferRequest)
411+
);
412+
Assert.Equal("can't transfer non established call", ex.Message);
413+
414+
ex = await Assert.ThrowsAsync<Twirp.Exception>(
415+
async () => await sipClient.TransferSIPParticipant(transferRequest)
416+
);
417+
Assert.Equal("participant does not exist", ex.Message);
418+
}
188419
}
189420
}

0 commit comments

Comments
 (0)