using game.PlayerDataStorage; using Newtonsoft.Json; using Newtonsoft.Json.Linq; static partial class Tests { static async Task RunBuffTriggerSchemaTests() { await Run("M06-four-buff-shapes-strict-and-missing", () => { const string value="{\"SortID\":1,\"sourceID\":2,\"buffID\":3,\"isEnd\":false,\"buffEndTime\":\"2026-09-24T00:00:00.0000000Z\"}"; var raw=new Dictionary { ["BuffDataList"]="{\"7\":"+value+"}",["TargetBuffDataList"]="["+value+"]", ["FishDailyBuff"]=value,["GlobalBuffDataList"]="{\"7\":"+value+"}" }; var state=PlayerKVSaveSchema.DecodeBuffs(raw,SchemaSnapshot(),new()); foreach(var item in new[] { state.Map[7],state.Target[0],state.Daily,state.Global[7] }) Check(item.SortID==1 && item.sourceID==2 && item.buffID==3 && !item.isEnd && item.buffEndTime.Day==24,"all saved buff fields"); var absent=PlayerKVSaveSchema.DecodeBuffs(new Dictionary(),SchemaSnapshot(),new()); Check(absent.Map.Count==0 && absent.Target.Count==0 && absent.Global.Count==0 && absent.Daily==null,"all missing defaults"); foreach(var key in raw.Keys) foreach(var bad in new[] { "null","{\"7\":null}","[null]","{}" }) { // Empty maps are valid only for map containers. if(bad=="{}" && (key=="BuffDataList" || key=="GlobalBuffDataList")) continue; Check(PlayerKVSaveSchema.DecodeBuffs(new Dictionary(raw) { [key]=bad },SchemaSnapshot(),new())==null,"bad present container fails"); } foreach(var property in JObject.Parse(value).Properties()) { var bad=JObject.Parse(value); bad[property.Name]=JValue.CreateNull(); Check(PlayerKVSaveSchema.DecodeBuffs(new Dictionary { ["FishDailyBuff"]=bad.ToString(Formatting.None) },SchemaSnapshot(),new())==null,"all required buff fields"); } Check(PlayerKVSaveSchema.DecodeBuffs(new Dictionary { ["BuffDataList"]="{\"7\":"+value+",\"07\":"+value+"}" },SchemaSnapshot(),new())==null,"normalized duplicate rejected"); var allBad=new Dictionary(raw); foreach(var key in raw.Keys) allBad[key]="null"; var errors=new PlayerKVSchemaDiagnostics(); Check(PlayerKVSaveSchema.DecodeBuffs(allBad,SchemaSnapshot(),errors)==null && errors.TotalErrors==4,"all bad keys counted"); return Task.CompletedTask; }); await Run("M13-trigger-gift-fixed-shapes", () => { var raw=new Dictionary { ["TriggerPackDataList"]="[{\"ID\":1,\"index\":2,\"lastID\":3,\"vipLevel\":4,\"mapID\":5,\"time\":\"2026-09-24\"}]", ["GiftVIPDData"]="{\"A\":{\"oldVipLevel\":1,\"curVipLevel\":2,\"timer\":3}}", ["FixedTimeShopDic"]="{\"7\":{\"id\":8,\"endTime\":\"2026-09-24\"}}" }; var state=PlayerKVSaveSchema.DecodeTriggers(raw,new()); var pack=state.Packs[0]; Check(pack.ID==1 && pack.index==2 && pack.lastID==3 && pack.vipLevel==4 && pack.mapID==5 && pack.time=="2026-09-24","pack fields"); Check(state.Gifts["A"].oldVipLevel==1 && state.Gifts["A"].curVipLevel==2 && state.Gifts["A"].timer==3 && state.Fixed[7].id==8 && state.Fixed[7].endTime=="2026-09-24","gift and fixed fields"); foreach(var key in raw.Keys) foreach(var bad in new[] { "null","[null]","{\"7\":null}","{\"A\":{}}" }) Check(PlayerKVSaveSchema.DecodeTriggers(new Dictionary(raw) { [key]=bad },new())==null,"invalid container or nested fails"); var missing=PlayerKVSaveSchema.DecodeTriggers(new Dictionary(),new()); Check(missing.Packs.Count==0 && missing.Gifts.Count==0 && missing.Fixed.Count==0,"missing all empty"); var allBad=new Dictionary(raw); foreach(var key in raw.Keys) allBad[key]="null"; var e=new PlayerKVSchemaDiagnostics(); Check(PlayerKVSaveSchema.DecodeTriggers(allBad,e)==null && e.TotalErrors==3,"all keys counted"); return Task.CompletedTask; }); } }