Private broadcasts end like recordings: Enter keeps, Escape deletes (KeepBroadcastDialog). Privacy read from YouTube; delete = videos.delete (fallback liveBroadcasts.delete on 404). Refs: https://developers.google.com/youtube/v3/docs/videos/delete https://developers.google.com/youtube/v3/live/docs/liveBroadcasts/delete (Unit: private broadcast close-out)
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using Xunit;
|
||||
using ytLive.Models;
|
||||
using ytLive.Services;
|
||||
using ytLive.ViewModels;
|
||||
|
||||
namespace ytLive.Tests;
|
||||
|
||||
/// <summary>Creator ruling 2026-10-09: a PRIVATE broadcast ends like a recording — Enter keeps
|
||||
/// it on YouTube, Escape deletes it.</summary>
|
||||
public class PrivateBroadcastCloseOutTests
|
||||
{
|
||||
private sealed class Handler : HttpMessageHandler
|
||||
{
|
||||
public string Privacy = "private";
|
||||
public readonly List<string> Requests = new();
|
||||
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken ct)
|
||||
{
|
||||
Requests.Add($"{request.Method.Method} {request.RequestUri!.PathAndQuery}");
|
||||
if (request.Method == HttpMethod.Delete)
|
||||
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NoContent));
|
||||
var body = "{\"items\":[{\"id\":\"BC1\",\"status\":{\"privacyStatus\":\"" + Privacy + "\",\"lifeCycleStatus\":\"complete\"}}]}";
|
||||
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{
|
||||
Content = new StringContent(body, Encoding.UTF8, "application/json"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static YouTubeStreamService Service(Handler handler)
|
||||
{
|
||||
var auth = new YouTubeAuthService("test-id", "test-secret");
|
||||
auth.SetSession(new YouTubeChannel { AccessToken = "acc", TokenExpiry = DateTime.UtcNow.AddHours(1) });
|
||||
return new YouTubeStreamService(auth, new HttpClient(handler));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Private_Broadcast_Escape_Deletes_Enter_Keeps_Public_Is_Never_Asked()
|
||||
{
|
||||
// Escape / Delete → the video is deleted from YouTube.
|
||||
var h = new Handler();
|
||||
var (outcome, error) = await MainViewModel.ClosePrivateBroadcastAsync(Service(h), "BC1", askKeep: () => false);
|
||||
Assert.Equal(MainViewModel.PrivateBroadcastOutcome.Deleted, outcome);
|
||||
Assert.Null(error);
|
||||
Assert.Contains(h.Requests, r => r.StartsWith("DELETE /youtube/v3/videos?id=BC1"));
|
||||
|
||||
// Enter / Keep → nothing is deleted.
|
||||
h = new Handler();
|
||||
(outcome, _) = await MainViewModel.ClosePrivateBroadcastAsync(Service(h), "BC1", askKeep: () => true);
|
||||
Assert.Equal(MainViewModel.PrivateBroadcastOutcome.Kept, outcome);
|
||||
Assert.DoesNotContain(h.Requests, r => r.StartsWith("DELETE"));
|
||||
|
||||
// Not private (changed mid-stream) → no prompt, nothing deleted.
|
||||
h = new Handler { Privacy = "public" };
|
||||
var asked = false;
|
||||
(outcome, _) = await MainViewModel.ClosePrivateBroadcastAsync(Service(h), "BC1", askKeep: () => asked = true);
|
||||
Assert.Equal(MainViewModel.PrivateBroadcastOutcome.NotPrivate, outcome);
|
||||
Assert.False(asked);
|
||||
Assert.DoesNotContain(h.Requests, r => r.StartsWith("DELETE"));
|
||||
|
||||
// The prompt itself: Enter = Keep (IsDefault), Escape = Delete (IsCancel).
|
||||
XNamespace x = "http://schemas.microsoft.com/winfx/2006/xaml";
|
||||
var doc = XDocument.Load(SourceAssert.RepoPath("KeepBroadcastDialog.xaml"));
|
||||
XElement Named(string name) => doc.Descendants().Single(e => (string?)e.Attribute(x + "Name") == name);
|
||||
Assert.Equal("True", (string?)Named("KeepBroadcastButton").Attribute("IsDefault"));
|
||||
Assert.Equal("True", (string?)Named("DeleteBroadcastButton").Attribute("IsCancel"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user