TASK 4 ship step 7: one-click go live + private-only enforcement (req 8) — the Go Live dialog is locked to Private (GoLiveViewModel.Visibility is a get-only string, no dropdown; settings' dead Default Visibility dropdown + Visibilities/DefaultStreamVisibility removed); YouTubeStreamService.CreateBroadcast always sends privacyStatus='private' and gained an injectable HttpClient? http = null seam; BeginGoLive fires CreateBroadcastAsync which stashes _currentBroadcastId for TASK 5's bind/transition (failure → StreamStatus.Error via AppLog, never a crash) alongside the frame pump, and StopStream clears it; the REC sign shows a dark-red PRIVATE badge next to REC whenever the live stream is private (IsLivePrivate) — the broadcast-insert integration test asserts the request body carries privacyStatus private (RecordingHandler seam) + a no-session guard test — 173 tests passing, 0 warnings
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using Xunit;
|
||||
using ytLive.Models;
|
||||
using ytLive.Services;
|
||||
|
||||
namespace ytLive.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// The one integration test for ship step 7 (private-only go live): the
|
||||
/// broadcast-insert request must carry privacyStatus "private" — the dialog is
|
||||
/// locked to Private and the service must never send anything else.
|
||||
/// </summary>
|
||||
public class YouTubeStreamServiceTests
|
||||
{
|
||||
private sealed class RecordingHandler : HttpMessageHandler
|
||||
{
|
||||
public string? LastBody;
|
||||
public string ResponseBody = """{"id":"BC123","snippet":{"title":"t"}}""";
|
||||
|
||||
protected override Task<HttpResponseMessage> SendAsync(
|
||||
HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
LastBody = request.Content?.ReadAsStringAsync().GetAwaiter().GetResult();
|
||||
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{
|
||||
Content = new StringContent(ResponseBody, Encoding.UTF8, "application/json"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static YouTubeAuthService CreateAuth() => new("test-id", "test-secret");
|
||||
|
||||
[Fact]
|
||||
public async Task CreateBroadcast_Always_Sends_Private_PrivacyStatus()
|
||||
{
|
||||
var handler = new RecordingHandler();
|
||||
var auth = CreateAuth();
|
||||
auth.SetSession(new YouTubeChannel
|
||||
{
|
||||
AccessToken = "acc-123",
|
||||
TokenExpiry = DateTime.UtcNow.AddHours(1),
|
||||
});
|
||||
var service = new YouTubeStreamService(auth, new HttpClient(handler));
|
||||
|
||||
var id = await service.CreateBroadcast("Test Stream", "A description", DateTime.UtcNow);
|
||||
|
||||
Assert.Equal("BC123", id);
|
||||
Assert.NotNull(handler.LastBody);
|
||||
Assert.Contains("\"privacyStatus\":\"private\"", handler.LastBody);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateBroadcast_Without_Session_Returns_Null()
|
||||
{
|
||||
var handler = new RecordingHandler();
|
||||
var auth = CreateAuth(); // no session
|
||||
var service = new YouTubeStreamService(auth, new HttpClient(handler));
|
||||
|
||||
var id = await service.CreateBroadcast("Test Stream", "A description", DateTime.UtcNow);
|
||||
|
||||
Assert.Null(id);
|
||||
Assert.Null(handler.LastBody); // never reached the API
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user