using game.PlayerDataStorage; static partial class Tests { static async Task RunEventBaseSchemaTests() { await Run("M05-frozen-time-conflicts-and-whole-replacement",() => { var raw=new Dictionary { ["LimitedTimeEventDic"]="{\"1\":{\"id\":2,\"duration\":60.123456789,\"startTime\":\"2026-09-23T00:00:00.0000000Z\"},\"3\":{\"id\":4,\"duration\":60,\"startTime\":\"2026-09-22T00:00:00.0000000Z\"}}", ["FixedTimeEventDic"]="{\"5\":{\"id\":6,\"endTime\":\"raw\"}}", ["ETDropAfterDropDic"]="{\"1\":2,\"3\":4}",["RankTargetCount"]="{\"5\":6,\"7\":8}", ["WheelLevelCount"]="{\"9\":10}",["TurntableRoll"]="11",["WheelOperateCount"]="12",["AllWheelOperateCount"]="13",["WheelLevelFreeCount"]="14",["PVPToken"]="15" }; var modern=new Dictionary{["ct"]="1:2",["rt"]="5:6"}; var a=PlayerKVSaveSchema.DecodeEventBase(raw,modern,SchemaSnapshot(),new()); Check(a!=null && a.Limited.Count==1 && a.Limited[1].Id==2 && a.Limited[1].Duration==60.123456789 && a.Fixed[5].id==6,"frozen filter double precision"); var offsetSnapshot=new PlayerKVSchemaSnapshot("offset",Array.Empty(),Array.Empty(),new Dictionary(),System.Globalization.CultureInfo.InvariantCulture, SchemaSnapshot().NowUtc,17,globalTimeOffset:9); var boundary=new Dictionary { ["LimitedTimeEventDic"]="{\"1\":{\"id\":2,\"duration\":60,\"startTime\":\"2026-09-22T15:00:00.0000000Z\"}}" }; Check(PlayerKVSaveSchema.DecodeEventBase(boundary,new Dictionary(),offsetSnapshot,new()).Limited.Count==1 && PlayerKVSaveSchema.DecodeEventBase(boundary,new Dictionary(),SchemaSnapshot(),new()).Limited.Count==0,"captured game time offset used"); Check(a.Target.Count==1 && a.Target[1]==2 && a.Rank.Count==1 && a.Rank[5]==6 && a.Wheel[9]==10,"whole new map wins"); Check(a.Coupon==11 && a.Operate==12 && a.AllOperate==13 && a.Free==14 && a.PvpToken==15,"all scalars"); foreach(var key in raw.Keys) Check(PlayerKVSaveSchema.DecodeEventBase(new Dictionary(raw){[key]="null"},modern,SchemaSnapshot(),new())==null,"each bad key rejected"); modern["ct"]="1:99"; var e=new PlayerKVSchemaDiagnostics(); Check(PlayerKVSaveSchema.DecodeEventBase(raw,modern,SchemaSnapshot(),e)==null && e.Count(5,PlayerKVSchemaError.ConflictingRepresentations)==1,"overlap mismatch fails"); modern["ct"]=""; modern["rt"]=""; a=PlayerKVSaveSchema.DecodeEventBase(raw,modern,SchemaSnapshot(),new()); Check(a.Target.Count==0 && a.Rank.Count==0,"empty new replaces old"); foreach(var invalid in new[]{"1:2,01:2","1:2,","null",null}) { modern["ct"]=invalid; Check(PlayerKVSaveSchema.DecodeEventBase(raw,modern,SchemaSnapshot(),new())==null,"bad compact fails"); } a=PlayerKVSaveSchema.DecodeEventBase(new Dictionary(),new Dictionary(),SchemaSnapshot(),new()); Check(a.Limited.Count==0 && a.Fixed.Count==0 && a.Target.Count==0 && a.Rank.Count==0 && a.Wheel.Count==0 && a.Coupon==17 && a.Operate==0 && a.AllOperate==0 && a.Free==0 && a.PvpToken==0,"missing captured config default"); Check(PlayerKVSaveSchema.DecodeEventBase(new Dictionary{["LimitedTimeEventDic"]="{\"1\":{\"id\":2,\"duration\":-1,\"startTime\":\"bad\"}}"},new Dictionary(),SchemaSnapshot(),new())==null,"expired candidate malformed time fails"); return Task.CompletedTask; }); } }