yavsc/src/PostIt/PostIt.Tests/CallSignalingClientTests.cs
Paul Schneider 5e48831d6d
feat(calls): add consent-based signaling to existing ChatHub
Wire shared PostIt call signaling, account-derived relay policy and temporary TURN credentials. Keep calls disabled pending native Android/Linux media integration. Document experimental Android crash and laboratory removal.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-26 01:00:19 +00:00

54 lines
2.8 KiB
C#

using System.Text.Json;
using Microsoft.AspNetCore.SignalR.Client;
using PostIt.Services;
using Yavsc.Abstract.Chat.Calls;
namespace PostIt.Tests;
public sealed class CallSignalingClientTests
{
[Fact]
public async Task All_operations_fail_explicitly_without_a_connected_chat_transport()
{
await using var connection = new HubConnectionBuilder().WithUrl("http://127.0.0.1:1/chathub").Build();
using var client = new CallSignalingClient(connection);
var id = Guid.NewGuid();
var ct = TestContext.Current.CancellationToken;
await Assert.ThrowsAsync<InvalidOperationException>(() => client.InviteAsync("General", "bob", ct));
await Assert.ThrowsAsync<InvalidOperationException>(() => client.AcceptAsync(id, ct));
await Assert.ThrowsAsync<InvalidOperationException>(() => client.EndAsync(id, ct));
await Assert.ThrowsAsync<InvalidOperationException>(() => client.SendAsync(id, new("offer", "sdp"), ct));
}
[Fact]
public async Task Disposing_call_subscriptions_does_not_dispose_the_chat_connection()
{
await using var connection = new HubConnectionBuilder().WithUrl("http://127.0.0.1:1/chathub").Build();
var client = new CallSignalingClient(connection);
client.Dispose();
client.Dispose();
await Assert.ThrowsAsync<ObjectDisposedException>(() => client.AcceptAsync(Guid.NewGuid(), TestContext.Current.CancellationToken));
Assert.Equal(HubConnectionState.Disconnected, connection.State);
using var replacement = new CallSignalingClient(connection);
await Assert.ThrowsAsync<InvalidOperationException>(() => replacement.InviteAsync("General", "bob", TestContext.Current.CancellationToken));
}
[Fact]
public void Shared_contracts_round_trip_with_signalr_web_json_naming()
{
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web);
var call = new CallSession(Guid.NewGuid(), "General", "alice", "bob", true,
DateTimeOffset.Parse("2026-10-26T01:00:00Z"));
var ready = new CallAccepted(call, [new(["turns:turn.example.test:5349"], "expires:nonce", "credential")]);
var json = JsonSerializer.Serialize(ready, options);
var read = JsonSerializer.Deserialize<CallAccepted>(json, options);
Assert.NotNull(read);
Assert.Equal(call, read.Session);
Assert.Equal(ready.IceServers[0].Urls, read.IceServers[0].Urls);
Assert.Equal(ready.IceServers[0].Username, read.IceServers[0].Username);
Assert.Equal(ready.IceServers[0].Credential, read.IceServers[0].Credential);
var signal = new CallSignal("candidate", Candidate: "candidate:1 1 udp 100 192.0.2.1 5000 typ relay",
SdpMid: "video", SdpMLineIndex: 1);
Assert.Equal(signal, JsonSerializer.Deserialize<CallSignal>(JsonSerializer.Serialize(signal, options), options));
}
}