Files
metin-launcher/tests/Metin2Launcher.Tests/M2PackFormatTests.cs
Jan Nedbal 0e95171e50 test: cover runtime key, release formats and telemetry
adds ~60 new tests across RuntimeKey parsing, EnvVarKeyDelivery, the
legacy and m2pack formats, ReleaseFormatFactory dispatch, manifest
loader tolerance of unknown top-level fields, orchestrator wiring and
the ClientAppliedReporter (disabled-by-default, success, 5xx, timeout,
connection refused). The telemetry tests spin up an in-process
HttpListener helper — no new NuGet dependency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 21:10:48 +02:00

132 lines
4.4 KiB
C#

using Metin2Launcher.Formats;
using Metin2Launcher.Manifest;
using Metin2Launcher.Runtime;
using Xunit;
using ManifestDto = Metin2Launcher.Manifest.Manifest;
namespace Metin2Launcher.Tests;
public class M2PackFormatTests
{
private const string ValidHex = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
private static ManifestDto SampleManifest() => new()
{
Version = "2026.04.14-1",
Format = "m2pack",
Launcher = new ManifestLauncherEntry { Path = "Metin2Launcher.exe", Sha256 = "x", Size = 1 },
Files =
{
new ManifestFile { Path = "pack/item.m2p", Sha256 = "h1", Size = 1000 },
new ManifestFile { Path = "runtime-key.json", Sha256 = "h2", Size = 200 },
},
};
private static string WriteRuntimeKey(string dir, string keyId = "2026.04.14-1")
{
var path = Path.Combine(dir, M2PackFormat.RuntimeKeyFileName);
File.WriteAllText(path, $$"""
{
"key_id": "{{keyId}}",
"master_key_hex": "{{ValidHex}}",
"sign_pubkey_hex": "{{ValidHex}}"
}
""");
return path;
}
[Fact]
public void Name_is_m2pack()
{
Assert.Equal("m2pack", new M2PackFormat().Name);
}
[Fact]
public void FilterApplicable_includes_m2p_and_runtime_key()
{
var filtered = new M2PackFormat().FilterApplicable(SampleManifest(), "windows");
Assert.Equal(2, filtered.Count);
Assert.Contains(filtered, f => f.Path.EndsWith(".m2p"));
Assert.Contains(filtered, f => f.Path == "runtime-key.json");
}
[Fact]
public void OnApplied_loads_runtime_key_when_present()
{
var tmp = Path.Combine(Path.GetTempPath(), "m2p-" + Guid.NewGuid());
Directory.CreateDirectory(tmp);
try
{
WriteRuntimeKey(tmp);
var outcome = new ReleaseOutcome();
new M2PackFormat().OnApplied(
tmp,
new LoadedManifest(Array.Empty<byte>(), Array.Empty<byte>(), SampleManifest()),
outcome);
Assert.NotNull(outcome.RuntimeKey);
Assert.Equal("2026.04.14-1", outcome.RuntimeKey!.KeyId);
Assert.Equal(ValidHex, outcome.RuntimeKey.MasterKeyHex);
}
finally { Directory.Delete(tmp, true); }
}
[Fact]
public void OnApplied_tolerates_missing_runtime_key()
{
var tmp = Path.Combine(Path.GetTempPath(), "m2p-" + Guid.NewGuid());
Directory.CreateDirectory(tmp);
try
{
var outcome = new ReleaseOutcome();
new M2PackFormat().OnApplied(
tmp,
new LoadedManifest(Array.Empty<byte>(), Array.Empty<byte>(), SampleManifest()),
outcome);
Assert.Null(outcome.RuntimeKey); // warn-logged, not thrown
}
finally { Directory.Delete(tmp, true); }
}
[Fact]
public void OnApplied_throws_on_malformed_runtime_key()
{
var tmp = Path.Combine(Path.GetTempPath(), "m2p-" + Guid.NewGuid());
Directory.CreateDirectory(tmp);
try
{
File.WriteAllText(Path.Combine(tmp, M2PackFormat.RuntimeKeyFileName), "{ not valid }");
var outcome = new ReleaseOutcome();
Assert.ThrowsAny<Exception>(() =>
new M2PackFormat().OnApplied(
tmp,
new LoadedManifest(Array.Empty<byte>(), Array.Empty<byte>(), SampleManifest()),
outcome));
}
finally { Directory.Delete(tmp, true); }
}
[Fact]
public void Launcher_never_opens_m2p_archive()
{
// Guard test: the format must not touch the .m2p file itself. We assert
// by constructing a manifest whose .m2p entry points at a file that
// does not exist on disk and would throw if opened — OnApplied must
// ignore it entirely and only touch runtime-key.json.
var tmp = Path.Combine(Path.GetTempPath(), "m2p-" + Guid.NewGuid());
Directory.CreateDirectory(tmp);
try
{
WriteRuntimeKey(tmp);
// Intentionally no pack/item.m2p on disk.
var outcome = new ReleaseOutcome();
new M2PackFormat().OnApplied(
tmp,
new LoadedManifest(Array.Empty<byte>(), Array.Empty<byte>(), SampleManifest()),
outcome);
Assert.NotNull(outcome.RuntimeKey);
}
finally { Directory.Delete(tmp, true); }
}
}