Broadcast Text pull-out (Live screen) + launch geometry 1920x1040/1366x768 with restore
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using Xunit;
|
||||
using ytLive.Models;
|
||||
using ytLive.Services;
|
||||
|
||||
namespace ytLive.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// TASK: broadcast metadata pull-out + launch geometry. One integration slice:
|
||||
/// (1) pull-out edits persist immediately to the Settings store and re-load,
|
||||
/// (2) a real MainWindow restores saved size/position, clamping off-screen or
|
||||
/// below-minimum values into the primary work area, (3) UpdateBroadcast PATCHes
|
||||
/// liveBroadcasts?part=snippet,status with the full editable field set.
|
||||
/// </summary>
|
||||
[Collection("RealApp")]
|
||||
public sealed class BroadcastPullOutTests
|
||||
{
|
||||
public BroadcastPullOutTests(RealAppHost app) => _app = app;
|
||||
|
||||
private readonly RealAppHost _app;
|
||||
|
||||
[Fact]
|
||||
public void Metadata_Persists_WindowRestores_Clamped_And_UpdatePatchesRemote()
|
||||
{
|
||||
_app.Run(Run);
|
||||
}
|
||||
|
||||
private sealed class StubHandler : HttpMessageHandler
|
||||
{
|
||||
public HttpRequestMessage? Request;
|
||||
public string? Body;
|
||||
|
||||
protected override Task<HttpResponseMessage> SendAsync(
|
||||
HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
Request = request;
|
||||
Body = request.Content?.ReadAsStringAsync(cancellationToken).GetAwaiter().GetResult();
|
||||
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{
|
||||
Content = new StringContent("{\"id\":\"bc123\"}", System.Text.Encoding.UTF8, "application/json"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void Run()
|
||||
{
|
||||
var tempDb = System.IO.Path.Combine(System.IO.Path.GetTempPath(), $"ytLlive-pullout-{Guid.NewGuid():N}.db");
|
||||
ViewModels.MainViewModel.LayoutPathOverride = tempDb;
|
||||
MainWindow? window = default;
|
||||
try
|
||||
{
|
||||
using (var schema = new LayoutStore(tempDb)) { }
|
||||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||||
|
||||
var vm = new ViewModels.MainViewModel();
|
||||
|
||||
vm.BroadcastForm.Title = "Hello";
|
||||
vm.BroadcastForm.Description = "World";
|
||||
vm.BroadcastForm.TagsCsv = "a, b ,c";
|
||||
vm.BroadcastForm.Visibility = "public";
|
||||
vm.BroadcastForm.MadeForKids = true;
|
||||
|
||||
using (var reader = new LayoutStore(tempDb))
|
||||
{
|
||||
var meta = reader.LoadBroadcastMetadata();
|
||||
Assert.Equal("Hello", meta.Title);
|
||||
Assert.Equal("World", meta.Description);
|
||||
Assert.Equal("a, b ,c", meta.TagsCsv);
|
||||
Assert.Equal("public", meta.Visibility);
|
||||
Assert.True(meta.MadeForKids);
|
||||
}
|
||||
|
||||
vm.BroadcastForm.Load();
|
||||
Assert.Equal("Hello", vm.BroadcastForm.Title);
|
||||
|
||||
vm.StreamStatus = StreamStatus.Streaming;
|
||||
Assert.True(vm.IsBroadcastMetadataVisible);
|
||||
vm.StreamStatus = StreamStatus.Offline;
|
||||
Assert.False(vm.IsBroadcastMetadataVisible);
|
||||
|
||||
using (var writer = new LayoutStore(tempDb))
|
||||
writer.SaveWindowState(100, 50, 1500, 900, "Normal");
|
||||
|
||||
window = new MainWindow();
|
||||
Assert.Equal(1500, window.Width);
|
||||
Assert.Equal(900, window.Height);
|
||||
Assert.Equal(100, window.Left);
|
||||
Assert.Equal(50, window.Top);
|
||||
|
||||
window.Close();
|
||||
window = null;
|
||||
|
||||
using (var writer = new LayoutStore(tempDb))
|
||||
writer.SaveWindowState(-99999, -99999, 800, 500, "Normal");
|
||||
|
||||
window = new MainWindow();
|
||||
var work = System.Windows.SystemParameters.WorkArea;
|
||||
Assert.Equal(1366, window.Width);
|
||||
Assert.Equal(768, window.Height);
|
||||
Assert.InRange(window.Left, work.Left - window.Width + 120, work.Right - 120);
|
||||
Assert.InRange(window.Top, work.Top, work.Bottom - 80);
|
||||
|
||||
var auth = new YouTubeAuthService("test-client", "test-secret");
|
||||
auth.SetSession(new YouTubeChannel
|
||||
{
|
||||
AccessToken = "token",
|
||||
TokenExpiry = DateTime.UtcNow.AddHours(1),
|
||||
});
|
||||
var stub = new StubHandler();
|
||||
var service = new YouTubeStreamService(auth, new HttpClient(stub));
|
||||
|
||||
var error = service.UpdateBroadcast("bc123", new BroadcastMetadata
|
||||
{
|
||||
Title = "T",
|
||||
Description = "D",
|
||||
TagsCsv = "x,y",
|
||||
Visibility = "Public",
|
||||
MadeForKids = true,
|
||||
ScheduledStartTime = new DateTime(2026, 8, 24, 12, 0, 0, DateTimeKind.Utc),
|
||||
}).GetAwaiter().GetResult();
|
||||
|
||||
Assert.Null(error);
|
||||
Assert.NotNull(stub.Request);
|
||||
Assert.Equal(HttpMethod.Put, stub.Request!.Method);
|
||||
Assert.Equal("https://www.googleapis.com/youtube/v3/liveBroadcasts?part=snippet,status",
|
||||
stub.Request.RequestUri!.ToString());
|
||||
|
||||
using var doc = JsonDocument.Parse(stub.Body!);
|
||||
var root = doc.RootElement;
|
||||
Assert.Equal("bc123", root.GetProperty("id").GetString());
|
||||
Assert.Equal("T", root.GetProperty("snippet").GetProperty("title").GetString());
|
||||
Assert.Equal(2, root.GetProperty("snippet").GetProperty("tags").GetArrayLength());
|
||||
Assert.Equal("public", root.GetProperty("status").GetProperty("privacyStatus").GetString());
|
||||
Assert.True(root.GetProperty("status").GetProperty("selfDeclaredMadeForKids").GetBoolean());
|
||||
Assert.Equal(new DateTime(2026, 8, 24, 12, 0, 0, DateTimeKind.Utc).ToString("o"),
|
||||
root.GetProperty("snippet").GetProperty("scheduledStartTime").GetString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
window?.Close();
|
||||
ViewModels.MainViewModel.LayoutPathOverride = null;
|
||||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||||
try { System.IO.File.Delete(tempDb); } catch { /* best-effort cleanup */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user