Files
ft/CodexOutput/upserver/KVGameLoadTests/CP2ARuntimeSelectionTests.cs
Liubing\LB 6813da149a feat(upserver): 保存M2客户端KV迁移至本人统计写检查点
保存独立KV用户服务、固定会话存档与原业务接线,以及C3d资料缓存和C3e本人统计写。
保留原PF/AGG路线、业务公式和时序;榜单读取、好友资料、公告、匹配及首场景闸仍待完成。

验证:ServiceHost两条件各33组646断言;Unity CLI Loop编译0错误269警告。
包含相关方案、评审裁决、测试及状态文档。未操作服务器、正式账号或打包。
2026-09-30 19:19:05 +08:00

52 lines
2.8 KiB
C#

using System.Reflection;
static partial class Tests
{
static async Task RunCP2ARuntimeSelectionTests()
{
await Run("CP2A-backend-capture-and-approved-existing-cohort", CP2ASelection);
}
static Task CP2ASelection()
{
// A wrong branch, a widened cohort, or a mutable backend would break this boundary.
var selectionType = typeof(Tests).Assembly.GetType("game.PlayerDataStorage.PlayerKVRuntimeSelection");
Check(selectionType != null, "formal runtime selection must exist in the real production assembly");
var capture = selectionType.GetMethod("Capture", BindingFlags.Static | BindingFlags.NonPublic);
Check(capture != null, "capture API");
object Take(string backend, string api, bool agg) => capture.Invoke(null, new object[] { backend, api, agg });
var pf = Take(null, "http://unused.invalid/", false);
Check((bool)selectionType.GetProperty("IsKV", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(pf) == false,
"missing backend keeps PF default without applying KV URL rules");
var kv = Take("kv", "http://s101.local:18080/", false);
Check((bool)selectionType.GetProperty("IsKV", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(kv),
"exact KV backend and address admitted");
var admit = selectionType.GetMethod("TryAdmitPlayer", BindingFlags.Instance | BindingFlags.NonPublic);
Check(admit != null, "cohort admission API");
bool Allowed(string id, out string canonical)
{
var args = new object[] { id, null };
var accepted = (bool)admit.Invoke(kv, args);
canonical = (string)args[1];
return accepted;
}
Check(Allowed("00101", out var first) && first == "101", "101 uses canonical decimal identity");
Check(Allowed("102", out var second) && second == "102", "102 admitted");
foreach (var id in new[] { "100", "103", "0", "-101", "+101", " 101", "", "9223372036854775808" })
Check(!Allowed(id, out _), "unapproved or malformed account denied: " + id);
foreach (var bad in new[] {
new object[] { "KV", "http://s101.local:18080/", false },
new object[] { "other", "http://s101.local:18080/", false },
new object[] { "kv", "http://s101.local:18080", false },
new object[] { "kv", "http://other.local:18080/", false },
new object[] { "kv", "http://s101.local:18080/", true }
})
{
try { capture.Invoke(null, bad); throw new InvalidOperationException("invalid capture accepted"); }
catch (TargetInvocationException ex) when (ex.InnerException is ArgumentException or InvalidOperationException)
{ Check(true, "invalid capture rejected"); }
}
return Task.CompletedTask;
}
}