using System.Text.Json;
using Xunit;
using ytLive.Services;
namespace ytLive.Tests;
///
/// Broadcast-binding diagnostics (2026-10-04). Two test streams proved the RTMP push
/// is healthy — ffmpeg delivered 101,229 KiB video + 1,419 KiB audio over 112 seconds
/// with no socket error — yet the broadcast never left `ready` and close-out returned
/// 403 `invalidTransition`. Google's docs say enableAutoStart fires "when you
/// start streaming video on the bound live stream", so the one thing worth asserting
/// about the request we send is that we know which stream we think we are bound to.
///
public class BroadcastBindingTests
{
/// The comparison that decides whether the broadcast is even watching the
/// feed we are pushing. A reusable stream happily accepts bytes for an unbound
/// broadcast, so ingest succeeding proves nothing on its own — a MISMATCH or
/// NO-BINDING would look identical from the RTMP side and would hang forever.
/// "unknown" is the honest answer when the app has no stream id to compare against,
/// so a missing expectation is never silently reported as a match.
[Theory]
[InlineData("stream-abc", "stream-abc", "yes")]
[InlineData("stream-abc", "stream-xyz", "MISMATCH")]
[InlineData(null, "stream-abc", "NO-BINDING")]
[InlineData("", "stream-abc", "NO-BINDING")]
[InlineData("stream-abc", null, "unknown")]
[InlineData("stream-abc", "", "unknown")]
public void BoundMatches_ComparesTheBroadcastAgainstTheStreamWePush(string? bound, string? expected, string verdict)
{
Assert.Equal(verdict, YouTubeStreamService.BoundMatches(bound, expected));
}
/// The regression the first diagnostic shipped with. `liveBroadcasts.list`
/// returns a LIST wrapper, so the broadcast sits at `items[0]`; reading the response
/// root reads the wrapper, every field comes back empty, and the binding check then
/// reports a confident `NO-BINDING` from a parse that never happened. The first real
/// test run produced exactly that — three lines of fabricated alarm. `FirstItem` must
/// descend, and must return `default` (not throw, not an empty object) when there is
/// genuinely no item, so the caller can report "unread" instead of guessing.
[Theory]
[InlineData("{\"kind\":\"youtube#liveBroadcastListResponse\",\"items\":[{\"id\":\"bc1\",\"status\":{\"lifeCycleStatus\":\"ready\"}}]}", "bc1")]
[InlineData("{\"items\":[]}", null)]
[InlineData("{\"kind\":\"youtube#liveBroadcastListResponse\"}", null)]
public void FirstItem_DescendsIntoTheListWrapper_AndReportsAbsenceHonestly(string listJson, string? expectedId)
{
using var doc = JsonDocument.Parse(listJson);
var item = YouTubeStreamService.FirstItem(doc.RootElement);
if (expectedId == null)
{
Assert.Equal(JsonValueKind.Undefined, item.ValueKind);
return;
}
Assert.Equal(expectedId, item.GetProperty("id").GetString());
}
}