using System; using System.IO; using Xunit; using ytLive.Services.Entitlement; using ytLive.ViewModels; namespace ytLive.Tests; /// /// The entitlement seam (2026-11-01): one question — "may this session emit Pro output?" — with a /// Steam DLC rail, a Polar rail to come, and a fail-closed default. /// /// The rule that must never break: **no configured rail means the free profile**. A missing /// DLC id, a missing Steam client or a thrown native load all resolve to false; none of them may /// crash the app or quietly grant Pro. /// // Shares the static MainViewModel.LayoutPathOverride with the sibling class; without a // shared collection xUnit runs them in parallel and the two view models fight over one temp DB. [Collection("MainViewModelLayoutPath")] public class EntitlementTests { [Fact] public void No_configured_dlc_id_reads_as_no_entitlement() { // The state before the Steamworks app exists: pure false, no native call attempted. Assert.False(SteamEntitlement.NotConfigured().IsPro); } [Fact] public void A_steam_entitlement_without_a_client_is_the_free_tier_not_a_crash() { // No steam_appid.txt in the test host and no client running: Init fails, we report false. var entitlement = new SteamEntitlement(1234567u); Assert.False(entitlement.IsPro); } [Fact] public void The_fake_reports_what_a_test_sets() { var fake = new FakeEntitlement(true); Assert.True(fake.IsPro); fake.IsPro = false; Assert.False(fake.IsPro); } [Fact] public void The_view_model_takes_its_answer_from_the_rail() { var tempDb = Path.Combine(Path.GetTempPath(), $"llamacasty-entitlement-{Guid.NewGuid():N}.db"); MainViewModel.LayoutPathOverride = tempDb; try { var vm = new MainViewModel(); // Default rail is NotConfigured → free output. Assert.False(vm.HasProOutput); vm.EntitlementOverride = new FakeEntitlement(true); Assert.True(vm.HasProOutput); vm.EntitlementOverride = new FakeEntitlement(false); Assert.False(vm.HasProOutput); } finally { MainViewModel.LayoutPathOverride = null; try { File.Delete(tempDb); } catch (IOException) { } } } }