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>
This commit is contained in:
parent
56ca115f50
commit
5e48831d6d
20 changed files with 1368 additions and 16 deletions
54
src/PostIt/PostIt.Tests/CallSignalingClientTests.cs
Normal file
54
src/PostIt/PostIt.Tests/CallSignalingClientTests.cs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
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));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue