feat: make LegacyParityApp playout live-only

This commit is contained in:
2026-07-28 14:52:59 +09:00
parent 11d3849933
commit ea96a08ad5
23 changed files with 456 additions and 270 deletions

View File

@@ -2,6 +2,7 @@ using System.Security;
using System.Text;
using MBN_STOCK_WEBVIEW.Playout.Configuration;
using MBN_STOCK_WEBVIEW.Playout.Diagnostics;
using MBN_STOCK_WEBVIEW.Playout.Safety;
namespace MBN_STOCK_WEBVIEW.Playout.Tests;
@@ -42,12 +43,18 @@ public sealed class DevelopmentLiveLaunchBootstrapTests
}
[Fact]
public void ResolveLaunchArguments_RejectsMissingOrAdditionalProcessArguments()
public void ResolveLaunchArguments_DistinguishesNormalLaunchFromAdditionalArguments()
{
Assert.Null(DevelopmentLiveLaunchBootstrap.ResolveLaunchArguments("", ["app.exe"]));
Assert.Null(DevelopmentLiveLaunchBootstrap.ResolveLaunchArguments(
var additionalArguments = DevelopmentLiveLaunchBootstrap.ResolveLaunchArguments(
"",
["app.exe", DevelopmentLiveLaunchBootstrap.ExactLaunchArgument, "extra"]));
["app.exe", DevelopmentLiveLaunchBootstrap.ExactLaunchArgument, "extra"]);
Assert.NotNull(additionalArguments);
Assert.NotEqual(string.Empty, additionalArguments);
Assert.NotEqual(
DevelopmentLiveLaunchBootstrap.ExactLaunchArgument,
additionalArguments);
}
[Fact]
@@ -65,13 +72,11 @@ public sealed class DevelopmentLiveLaunchBootstrapTests
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData("--development-live ")]
[InlineData(" --development-live")]
[InlineData("--DEVELOPMENT-LIVE")]
[InlineData("--development-live --another-option")]
public void NonExactArgument_SkipsWithoutReadingOrMutating(string? arguments)
public void NonExactArgument_IsRejectedWithoutReadingOrMutating(string? arguments)
{
var platform = FakePlatform.Valid();
@@ -81,37 +86,56 @@ public sealed class DevelopmentLiveLaunchBootstrapTests
"ignored.json",
platform);
Assert.Equal(DevelopmentLiveBootstrapStatus.Skipped, result.Status);
Assert.Equal("development-live-not-requested", result.Code);
Assert.Equal(DevelopmentLiveBootstrapStatus.Rejected, result.Status);
Assert.Equal("development-live-arguments-invalid", result.Code);
Assert.False(result.IsLaunchAllowed);
Assert.True(result.IsRequested);
Assert.Equal(0, platform.ReadCount);
Assert.Empty(platform.Environment);
}
[Fact]
public void ReleaseBuild_SkipsExactArgumentWithoutReadingOrMutating()
[Theory]
[InlineData(true)]
[InlineData(false)]
public void GateALaunch_BypassesOrdinaryAuthorizationWithoutEnvironmentMutation(
bool isDebugBuild)
{
var platform = FakePlatform.Valid();
platform.Environment[
PlayoutLaunchAuthorization.GateACapabilityEnvironmentVariable] =
new string('A', 64);
var originalEnvironment = platform.Environment.ToArray();
var result = DevelopmentLiveLaunchBootstrap.TryApply(
DevelopmentLiveLaunchBootstrap.ExactLaunchArgument,
isDebugBuild: false,
"ignored.json",
null,
isDebugBuild,
"must-not-be-read.json",
platform);
Assert.Equal(DevelopmentLiveBootstrapStatus.Skipped, result.Status);
Assert.Equal("development-live-debug-only", result.Code);
Assert.Equal(DevelopmentLiveBootstrapStatus.GateABypassed, result.Status);
Assert.Equal("development-live-gate-a-bypassed", result.Code);
Assert.True(result.IsLaunchAllowed);
Assert.False(result.IsRequested);
Assert.Equal(0, platform.ReadCount);
Assert.Empty(platform.Environment);
Assert.Equal(originalEnvironment, platform.Environment);
}
[Fact]
public void DebugExactArgumentAndStrictFile_ApplyAllProcessGates()
[Theory]
[InlineData(null, true)]
[InlineData("", true)]
[InlineData("--development-live", true)]
[InlineData(null, false)]
[InlineData("", false)]
[InlineData("--development-live", false)]
public void NormalOrExactLaunchAndStrictFile_ApplyAllProcessGates(
string? arguments,
bool isDebugBuild)
{
var platform = FakePlatform.Valid();
var result = DevelopmentLiveLaunchBootstrap.TryApply(
DevelopmentLiveLaunchBootstrap.ExactLaunchArgument,
isDebugBuild: true,
arguments,
isDebugBuild,
"authorization.json",
platform);
@@ -188,6 +212,25 @@ public sealed class DevelopmentLiveLaunchBootstrapTests
Assert.Empty(platform.Environment);
}
[Fact]
public void InvalidAuthorizationFileRead_IsUnavailableAndDoesNotMutateEnvironment()
{
var platform = FakePlatform.Valid();
platform.ReadException = new InvalidDataException("sensitive authorization bytes");
var result = DevelopmentLiveLaunchBootstrap.TryApply(
null,
isDebugBuild: false,
"sensitive authorization path",
platform);
Assert.Equal(DevelopmentLiveBootstrapStatus.Rejected, result.Status);
Assert.Equal("development-live-authorization-unavailable", result.Code);
Assert.False(result.IsLaunchAllowed);
Assert.DoesNotContain("sensitive", result.Code, StringComparison.OrdinalIgnoreCase);
Assert.Empty(platform.Environment);
}
[Fact]
public void EnvironmentFailure_DisarmsEveryPreviousProcessValue()
{
@@ -196,7 +239,8 @@ public sealed class DevelopmentLiveLaunchBootstrapTests
InstalledK3dInteropMetadata.ApprovedNativeSha256EnvironmentVariable] = "old-native";
platform.Environment[
InstalledK3dInteropMetadata.ApprovedSha256EnvironmentVariable] = "old-interop";
platform.Environment[DevelopmentLiveLaunchBootstrap.ModeEnvironmentVariable] = "DryRun";
platform.Environment[DevelopmentLiveLaunchBootstrap.ModeEnvironmentVariable] =
"stale-mode";
platform.Environment[PlayoutOptionsLoader.LiveAuthorizationEnvironmentVariable] =
"old-authorization";
platform.FailOnceOnSetName = DevelopmentLiveLaunchBootstrap.ModeEnvironmentVariable;

View File

@@ -256,7 +256,7 @@ public sealed class PlayoutOptionsLoaderTests
}
[Fact]
public void LoadDevelopmentLive_RejectsNonCanonicalJsonAndAllowlistExpansion()
public void LoadDevelopmentLive_RejectsWrongModeNonCanonicalJsonAndAllowlistExpansion()
{
using var environment = ClearedEnvironment();
var canonical = DevelopmentLiveBaseJson();
@@ -265,12 +265,16 @@ public sealed class PlayoutOptionsLoaderTests
string[] invalidConfigurations =
[
canonical.Replace(
"\"mode\": \"Live\"",
"\"mode\": \"DryRun\"",
"\"Mode\": \"DryRun\"",
StringComparison.Ordinal),
canonical.Replace(
"\"mode\": \"DryRun\"",
"\"mode\": \"DryRun\", \"mode\": \"DryRun\"",
"\"mode\": \"Live\"",
"\"Mode\": \"Live\"",
StringComparison.Ordinal),
canonical.Replace(
"\"mode\": \"Live\"",
"\"mode\": \"Live\", \"mode\": \"Live\"",
StringComparison.Ordinal),
canonical.Replace(
"\"port\": 31001",
@@ -586,7 +590,7 @@ public sealed class PlayoutOptionsLoaderTests
private static string DevelopmentLiveBaseJson() =>
"""
{
"mode": "DryRun",
"mode": "Live",
"host": "127.0.0.1",
"port": 31001,
"tcpMode": 1,