114 lines
3.9 KiB
C#
114 lines
3.9 KiB
C#
|
|
using System.IO;
|
||
|
|
using FluentAssertions;
|
||
|
|
using TeamsISO.App.Services;
|
||
|
|
using TeamsISO.App.Tests.Fakes;
|
||
|
|
|
||
|
|
namespace TeamsISO.App.Tests.Services;
|
||
|
|
|
||
|
|
// Tests for the OscBridge.DispatchAsync routing. We construct
|
||
|
|
// OscMessage instances directly (skipping the UDP receive loop) and
|
||
|
|
// assert that the right address resolves to the right controller call.
|
||
|
|
//
|
||
|
|
// The toggle / preset paths require Application.Current.Dispatcher,
|
||
|
|
// which doesn't exist in xUnit's default execution context — those
|
||
|
|
// paths return early on the null check, so we verify the bail rather
|
||
|
|
// than the happy path. The full toggle path is covered in branch 11's
|
||
|
|
// integration test that boots a real dispatcher.
|
||
|
|
public sealed class OscBridgeDispatchTests : IDisposable
|
||
|
|
{
|
||
|
|
private readonly string _tempNotesDir;
|
||
|
|
private readonly string? _previousNotesOverride;
|
||
|
|
|
||
|
|
public OscBridgeDispatchTests()
|
||
|
|
{
|
||
|
|
_tempNotesDir = Path.Combine(Path.GetTempPath(), $"teamsiso-osc-{Guid.NewGuid():N}");
|
||
|
|
_previousNotesOverride = NotesService.DirectoryOverride;
|
||
|
|
NotesService.DirectoryOverride = _tempNotesDir;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Dispose()
|
||
|
|
{
|
||
|
|
NotesService.DirectoryOverride = _previousNotesOverride;
|
||
|
|
try { if (Directory.Exists(_tempNotesDir)) Directory.Delete(_tempNotesDir, recursive: true); }
|
||
|
|
catch { /* best-effort */ }
|
||
|
|
}
|
||
|
|
|
||
|
|
private static (OscBridge Bridge, StubIsoController Controller) NewBridge()
|
||
|
|
{
|
||
|
|
var controller = new StubIsoController();
|
||
|
|
// OscBridge takes Func<MainViewModel?> — returning null exercises
|
||
|
|
// the "no VM yet" graceful path in handlers that need it.
|
||
|
|
var bridge = new OscBridge(controller, () => null, logger: null);
|
||
|
|
return (bridge, controller);
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task RefreshDiscoveryAddress_CallsControllerRefreshDiscovery()
|
||
|
|
{
|
||
|
|
var (bridge, controller) = NewBridge();
|
||
|
|
|
||
|
|
await bridge.DispatchAsync(new OscMessage { Address = "/teamsiso/refresh-discovery" });
|
||
|
|
|
||
|
|
controller.RefreshDiscoveryCalled.Should().BeTrue();
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task UnknownAddress_NoOpsCleanly()
|
||
|
|
{
|
||
|
|
var (bridge, controller) = NewBridge();
|
||
|
|
|
||
|
|
await bridge.DispatchAsync(new OscMessage { Address = "/teamsiso/nope/never" });
|
||
|
|
|
||
|
|
controller.RefreshDiscoveryCalled.Should().BeFalse();
|
||
|
|
controller.EnableCalls.Should().BeEmpty();
|
||
|
|
controller.DisableCalls.Should().BeEmpty();
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task NotesAddress_AppendsViaNotesService()
|
||
|
|
{
|
||
|
|
var (bridge, _) = NewBridge();
|
||
|
|
|
||
|
|
await bridge.DispatchAsync(new OscMessage
|
||
|
|
{
|
||
|
|
Address = "/teamsiso/notes",
|
||
|
|
TypeTag = ",s",
|
||
|
|
Args = new object[] { "tracked through OSC" },
|
||
|
|
});
|
||
|
|
|
||
|
|
File.Exists(NotesService.TodayPath).Should().BeTrue();
|
||
|
|
File.ReadAllText(NotesService.TodayPath).Should().Contain("tracked through OSC");
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task StopAllAddress_NoOpsWhenViewModelIsNull()
|
||
|
|
{
|
||
|
|
// Without a view-model, the stop-all path returns before touching
|
||
|
|
// the controller. The point of this test is to pin that the bail
|
||
|
|
// is clean — no thrown exception, no controller traffic.
|
||
|
|
var (bridge, controller) = NewBridge();
|
||
|
|
|
||
|
|
await bridge.DispatchAsync(new OscMessage { Address = "/teamsiso/stop-all" });
|
||
|
|
|
||
|
|
controller.DisableCalls.Should().BeEmpty();
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task IsoByNameAddress_NoOpsWhenViewModelIsNull()
|
||
|
|
{
|
||
|
|
// /teamsiso/iso "Jane" 1 — verifies the bail when no VM is
|
||
|
|
// wired; doesn't fire EnableIsoAsync. The dispatcher-equipped
|
||
|
|
// version of this round-trip lives in branch 11.
|
||
|
|
var (bridge, controller) = NewBridge();
|
||
|
|
|
||
|
|
await bridge.DispatchAsync(new OscMessage
|
||
|
|
{
|
||
|
|
Address = "/teamsiso/iso",
|
||
|
|
TypeTag = ",sT",
|
||
|
|
Args = new object[] { "Jane", true },
|
||
|
|
});
|
||
|
|
|
||
|
|
controller.EnableCalls.Should().BeEmpty();
|
||
|
|
}
|
||
|
|
}
|