94e114bfdf
The record-OR-live ruling (2026-09-01) was documented in ai.md but the 'pills = radios' UI constraint never landed — both pills could be armed and StartSession/BuildEncoderOptions would dual-encode to the YouTube ingest AND a local file at once, streaming a concurrent disk write off the same ingested Kbps (cripples the stream on mid-range chassis; 'the VOD is already the copy'). The pill setters now clear one another (silent mutual clear); the dual-block ffmpeg machinery stays generic and unreachable from the UI by design.
64 lines
2.1 KiB
C#
64 lines
2.1 KiB
C#
using System;
|
|
using System.IO;
|
|
using Microsoft.Data.Sqlite;
|
|
using Xunit;
|
|
using ytLive.Services;
|
|
using ytLive.ViewModels;
|
|
|
|
namespace ytLive.Tests;
|
|
|
|
/// <summary>
|
|
/// Record-OR-live (creator ruling 2026-09-01): the REC and ON-AIR pills are
|
|
/// radio-exclusive — arming one clears the other, so a session can never dual-encode
|
|
/// to both the YouTube ingest and a local file at once (a concurrent disk write
|
|
/// streamed from the same sustained Kbps cripples the stream on mid-range chassis).
|
|
/// The dual-block encoder machinery in FfmpegArgs stays generic; the constraint
|
|
/// lives at the pills.
|
|
/// </summary>
|
|
[Collection("RealApp")]
|
|
public sealed class PillRadioTests
|
|
{
|
|
private readonly RealAppHost _app;
|
|
|
|
public PillRadioTests(RealAppHost app) => _app = app;
|
|
|
|
[Fact]
|
|
public void Arming_One_Output_Pill_Clears_The_Other()
|
|
{
|
|
_app.Run(Run);
|
|
}
|
|
|
|
private void Run()
|
|
{
|
|
var tempDb = Path.Combine(Path.GetTempPath(), $"ytLlive-pillradio-{Guid.NewGuid():N}.db");
|
|
MainViewModel.LayoutPathOverride = tempDb;
|
|
try
|
|
{
|
|
using (var schema = new LayoutStore(tempDb)) { }
|
|
SqliteConnection.ClearAllPools();
|
|
|
|
var vm = new MainViewModel();
|
|
vm.IsConnected = true; // the ON-AIR setter requires a sign-in
|
|
|
|
// Arm REC first, then ON-AIR → REC must clear.
|
|
vm.RecordPillOn = true;
|
|
vm.OnAirPillOn = true;
|
|
Assert.False(vm.RecordPillOn, "arming ON-AIR must clear the REC pill");
|
|
Assert.True(vm.OnAirPillOn);
|
|
|
|
// Arm REC while ON-AIR is lit → ON-AIR must clear.
|
|
vm.RecordPillOn = true;
|
|
Assert.False(vm.OnAirPillOn, "arming REC must clear the ON-AIR pill");
|
|
Assert.True(vm.RecordPillOn);
|
|
|
|
// A single armed pill still makes the Start button actionable.
|
|
Assert.True(vm.CanStartSession);
|
|
}
|
|
finally
|
|
{
|
|
MainViewModel.LayoutPathOverride = null;
|
|
SqliteConnection.ClearAllPools();
|
|
try { File.Delete(tempDb); } catch { /* best-effort cleanup */ }
|
|
}
|
|
}
|
|
} |