Skip to content

Commit

Permalink
Update SipServiceClient grants. Add new SipServiceClient integration …
Browse files Browse the repository at this point in the history
…tests
  • Loading branch information
pabloFuente committed Jan 26, 2025
1 parent 7f06890 commit 3c8ea9c
Show file tree
Hide file tree
Showing 4 changed files with 282 additions and 7 deletions.
1 change: 1 addition & 0 deletions LivekitApi.Tests/IngressServiceClient.Test.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public async Task Create_Ingress_Url()
ParticipantName = "ingress-name",
InputType = IngressInput.UrlInput,
Url = url,
Enabled = true,
}
);
Assert.NotNull(ingress.IngressId);
Expand Down
42 changes: 41 additions & 1 deletion LivekitApi.Tests/ServiceClientFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public class ServiceClientFixture : IDisposable
private const string LIVEKIT_SERVER_IMAGE = "livekit/livekit-server:latest";
private const string LIVEKIT_EGRESS_IMAGE = "livekit/egress:latest";
private const string LIVEKIT_INGRESS_IMAGE = "livekit/ingress:latest";
private const string LIVEKIT_SIP_IMAGE = "livekit/sip:latest";
private const string LIVEKIT_CLI_IMAGE = "livekit/livekit-cli:latest";
private const string REDIS_IMAGE = "redis:latest";

Expand Down Expand Up @@ -52,10 +53,24 @@ public class ServiceClientFixture : IDisposable
http_relay_port: 9090
health_port: 9091";

private string sipYaml =
@"api_key: "
+ TEST_API_KEY
+ @"
api_secret: "
+ TEST_API_SECRET
+ @"
ws_url: {WS_URL}
redis:
address: {REDIS_ADDRESS}
sip_port: 5060
rtp_port: 10000-20000";

private IContainer redisContainer;
private IContainer livekitServerContainer;
private IContainer egressContainer;
private IContainer ingressContainer;
private IContainer sipContainer;

public ServiceClientFixture()
{
Expand Down Expand Up @@ -136,7 +151,28 @@ public ServiceClientFixture()
)
)
.Build();
Task.WaitAll(egressContainer.StartAsync(), ingressContainer.StartAsync());
// Sip
sipYaml = sipYaml
.Replace("{WS_URL}", "ws://" + livekitServerContainer.IpAddress + ":7880")
.Replace("{REDIS_ADDRESS}", redisContainer.IpAddress + ":6379");
sipContainer = new ContainerBuilder()
.WithImage(LIVEKIT_SIP_IMAGE)
.WithName("sip")
.WithAutoRemove(true)
.WithEnvironment("SIP_CONFIG_BODY", sipYaml)
.WithPortBinding(5060, true)
.DependsOn(redisContainer)
.DependsOn(livekitServerContainer)
.WithWaitStrategy(
Wait.ForUnixContainer().UntilMessageIsLogged(".*sip signaling listening on.*")
)
.Build();

Task.WaitAll(
egressContainer.StartAsync(),
ingressContainer.StartAsync(),
sipContainer.StartAsync()
);
}

public void Dispose()
Expand All @@ -158,6 +194,10 @@ public void Dispose()
{
tasks.Add(ingressContainer.DisposeAsync().AsTask());
}
if (sipContainer != null)
{
tasks.Add(sipContainer.DisposeAsync().AsTask());
}
Task.WhenAll(tasks).Wait();
}

Expand Down
239 changes: 235 additions & 4 deletions LivekitApi.Tests/SipServiceClient.Test.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
using Google.Protobuf;
using Google.Protobuf.Collections;
using Xunit;
using Xunit.Abstractions;

namespace Livekit.Server.Sdk.Dotnet.Test
{
[Collection("Integration tests")]
public class SipServiceClientTest
{
private ServiceClientFixture fixture;
private readonly ITestOutputHelper output;

public SipServiceClientTest(ServiceClientFixture fixture)
public SipServiceClientTest(ServiceClientFixture fixture, ITestOutputHelper output)
{
this.fixture = fixture;
this.output = output;
}

private SipServiceClient sipClient = new SipServiceClient(
ServiceClientFixture.TEST_HTTP_URL,
ServiceClientFixture.TEST_API_KEY,
ServiceClientFixture.TEST_API_SECRET
);
private readonly RoomServiceClient roomClient = new RoomServiceClient(
ServiceClientFixture.TEST_HTTP_URL,
ServiceClientFixture.TEST_API_KEY,
ServiceClientFixture.TEST_API_SECRET
);

[Fact]
[Trait("Category", "Integration")]
Expand Down Expand Up @@ -67,6 +77,23 @@ public async Task Create_Sip_Inbound_Trunk()
Assert.Equal(request.Trunk.AllowedNumbers, response.AllowedNumbers);
}

[Fact]
[Trait("Category", "Integration")]
[Trait("Category", "SipService")]
public async Task Create_Sip_Inbound_Trunk_Exceptions()
{
Twirp.Exception ex = await Assert.ThrowsAsync<Twirp.Exception>(
async () =>
await sipClient.CreateSIPInboundTrunk(
new CreateSIPInboundTrunkRequest { Trunk = new SIPInboundTrunkInfo { } }
)
);
Assert.Equal(
"for security, one of the fields must be set: AuthUsername+AuthPassword, AllowedAddresses or Numbers",
ex.Message
);
}

[Fact]
[Trait("Category", "Integration")]
[Trait("Category", "SipService")]
Expand All @@ -92,13 +119,50 @@ public async Task Create_Sip_Outbound_Trunk()
Assert.Equal(request.Trunk.AuthPassword, response.AuthPassword);
}

[Fact]
[Trait("Category", "Integration")]
[Trait("Category", "SipService")]
public async Task Create_Sip_Outbound_Trunk_Exceptions()
{
Twirp.Exception ex = await Assert.ThrowsAsync<Twirp.Exception>(
async () =>
await sipClient.CreateSIPOutboundTrunk(
new CreateSIPOutboundTrunkRequest { Trunk = new SIPOutboundTrunkInfo { } }
)
);
Assert.Equal("no trunk numbers specified", ex.Message);
ex = await Assert.ThrowsAsync<Twirp.Exception>(
async () =>
await sipClient.CreateSIPOutboundTrunk(
new CreateSIPOutboundTrunkRequest
{
Trunk = new SIPOutboundTrunkInfo { Address = "my-test-trunk.com" },
}
)
);
Assert.Equal("no trunk numbers specified", ex.Message);
ex = await Assert.ThrowsAsync<Twirp.Exception>(
async () =>
await sipClient.CreateSIPOutboundTrunk(
new CreateSIPOutboundTrunkRequest
{
Trunk = new SIPOutboundTrunkInfo { Numbers = { "+111", "+222" } },
}
)
);
Assert.Equal("no outbound address specified", ex.Message);
}

[Fact]
[Trait("Category", "Integration")]
[Trait("Category", "SipService")]
public async Task Get_Sip_Inbound_Trunk()
{
var inboundTrunk = await sipClient.CreateSIPInboundTrunk(
new CreateSIPInboundTrunkRequest { Trunk = new SIPInboundTrunkInfo { } }
new CreateSIPInboundTrunkRequest
{
Trunk = new SIPInboundTrunkInfo { Numbers = { "+111", "+222" } },
}
);
var getRequest = new GetSIPInboundTrunkRequest { SipTrunkId = inboundTrunk.SipTrunkId };
var response = await sipClient.GetSIPInboundTrunk(getRequest);
Expand All @@ -112,7 +176,14 @@ public async Task Get_Sip_Inbound_Trunk()
public async Task Get_Sip_Outound_Trunk()
{
var outboundTrunk = await sipClient.CreateSIPOutboundTrunk(
new CreateSIPOutboundTrunkRequest { Trunk = new SIPOutboundTrunkInfo { } }
new CreateSIPOutboundTrunkRequest
{
Trunk = new SIPOutboundTrunkInfo
{
Numbers = { "+111", "+222" },
Address = "my-test-trunk.com",
},
}
);
var getRequest = new GetSIPOutboundTrunkRequest
{
Expand All @@ -129,7 +200,10 @@ public async Task Get_Sip_Outound_Trunk()
public async Task Delete_Sip_Trunk()
{
var trunk = await sipClient.CreateSIPInboundTrunk(
new CreateSIPInboundTrunkRequest { Trunk = new SIPInboundTrunkInfo { } }
new CreateSIPInboundTrunkRequest
{
Trunk = new SIPInboundTrunkInfo { Numbers = { "+111", "+222" } },
}
);
var allTrunks = await sipClient.ListSIPInboundTrunk(new ListSIPInboundTrunkRequest { });
Assert.Contains(allTrunks.Items, t => t.SipTrunkId == trunk.SipTrunkId);
Expand Down Expand Up @@ -185,5 +259,162 @@ public async Task Dispatch_Rule()
r => r.SipDispatchRuleId == dispatchRule.SipDispatchRuleId
);
}

[Fact]
[Trait("Category", "Integration")]
[Trait("Category", "SipService")]
public async Task Create_Sip_Participant()
{
Twirp.Exception ex = await Assert.ThrowsAsync<Twirp.Exception>(
async () =>
await sipClient.CreateSIPParticipant(
new CreateSIPParticipantRequest { SipTrunkId = "non-existing-trunk" }
)
);
Assert.Equal("requested sip trunk does not exist", ex.Message);

SIPOutboundTrunkInfo trunk = await sipClient.CreateSIPOutboundTrunk(
new CreateSIPOutboundTrunkRequest
{
Trunk = new SIPOutboundTrunkInfo
{
Name = "Demo outbound trunk",
Address = "my-test-trunk.com",
Numbers = { "+1234567890" },
AuthUsername = "username",
AuthPassword = "password",
},
}
);

CreateSIPParticipantRequest request = new CreateSIPParticipantRequest
{
SipTrunkId = "trunk",
};

ex = await Assert.ThrowsAsync<Twirp.Exception>(
async () =>
await sipClient.CreateSIPParticipant(
new CreateSIPParticipantRequest { SipTrunkId = "non-existing-trunk" }
)
);
Assert.Equal("requested sip trunk does not exist", ex.Message);

request.SipTrunkId = trunk.SipTrunkId;

ex = await Assert.ThrowsAsync<Twirp.Exception>(
async () => await sipClient.CreateSIPParticipant(request)
);
Assert.Equal("call-to number must be set", ex.Message);

request.SipCallTo = "+3333";

ex = await Assert.ThrowsAsync<Twirp.Exception>(
async () => await sipClient.CreateSIPParticipant(request)
);
Assert.Equal("room name must be set", ex.Message);

request.RoomName = TestConstants.ROOM_NAME;

ex = await Assert.ThrowsAsync<Twirp.Exception>(
async () => await sipClient.CreateSIPParticipant(request)
);
Assert.Equal("update room failed: identity cannot be empty", ex.Message);

request.ParticipantIdentity = TestConstants.PARTICIPANT_IDENTITY;

request.ParticipantName = "Test Caller";
request.SipNumber = "+1111";
request.RingingTimeout = new Google.Protobuf.WellKnownTypes.Duration { Seconds = 10 };
request.PlayDialtone = true;
request.ParticipantMetadata = "meta";
request.ParticipantAttributes.Add("extra", "1");
request.MediaEncryption = SIPMediaEncryption.SipMediaEncryptRequire;
request.MaxCallDuration = new Google.Protobuf.WellKnownTypes.Duration { Seconds = 99 };
request.KrispEnabled = true;
request.IncludeHeaders = SIPHeaderOptions.SipAllHeaders;
request.Dtmf = "1234#";
request.HidePhoneNumber = true;
request.Headers.Add("X-A", "A");

SIPParticipantInfo sipParticipantInfo = await sipClient.CreateSIPParticipant(request);

Assert.NotNull(sipParticipantInfo);
Assert.Equal(TestConstants.PARTICIPANT_IDENTITY, request.ParticipantIdentity);
Assert.Equal(TestConstants.ROOM_NAME, request.RoomName);

var sipParticipant = await roomClient.GetParticipant(
new RoomParticipantIdentity
{
Room = TestConstants.ROOM_NAME,
Identity = TestConstants.PARTICIPANT_IDENTITY,
}
);
Assert.NotNull(sipParticipant);
Assert.Equal(TestConstants.PARTICIPANT_IDENTITY, sipParticipant.Identity);
Assert.Equal("Test Caller", sipParticipant.Name);
Assert.Equal("meta", sipParticipant.Metadata);
Assert.Equal("1", sipParticipant.Attributes["extra"]);
}

[Fact]
[Trait("Category", "Integration")]
[Trait("Category", "SipService")]
public async Task Transfer_Sip_Participant()
{
var trunk = await sipClient.CreateSIPOutboundTrunk(
new CreateSIPOutboundTrunkRequest
{
Trunk = new SIPOutboundTrunkInfo
{
Name = "Demo outbound trunk",
Address = "my-test-trunk.com",
Numbers = { "+1234567890" },
AuthUsername = "username",
AuthPassword = "password",
},
}
);

var request = new CreateSIPParticipantRequest
{
SipTrunkId = trunk.SipTrunkId,
SipCallTo = "+3333",
RoomName = TestConstants.ROOM_NAME,
ParticipantIdentity = TestConstants.PARTICIPANT_IDENTITY,
ParticipantName = "Test Caller",
SipNumber = "+1111",
};

SIPParticipantInfo sipParticipantInfo = await sipClient.CreateSIPParticipant(request);

var transferRequest = new TransferSIPParticipantRequest { };

Twirp.Exception ex = await Assert.ThrowsAsync<Twirp.Exception>(
async () => await sipClient.TransferSIPParticipant(transferRequest)
);
Assert.Equal("Missing room name", ex.Message);

transferRequest.RoomName = TestConstants.ROOM_NAME;

ex = await Assert.ThrowsAsync<Twirp.Exception>(
async () => await sipClient.TransferSIPParticipant(transferRequest)
);
Assert.Equal("Missing participant identity", ex.Message);

transferRequest.ParticipantIdentity = TestConstants.PARTICIPANT_IDENTITY;
transferRequest.TransferTo = "+14155550100";
transferRequest.PlayDialtone = false;

ex = await Assert.ThrowsAsync<Twirp.Exception>(
async () => await sipClient.TransferSIPParticipant(transferRequest)
);
Assert.Equal("can't transfer non established call", ex.Message);

ex = await Assert.ThrowsAsync<Twirp.Exception>(
async () => await sipClient.TransferSIPParticipant(transferRequest)
);
Assert.Equal("participant does not exist", ex.Message);
}
}
}
Loading

0 comments on commit 3c8ea9c

Please sign in to comment.