using game.PlayerDataStorage; using Newtonsoft.Json.Linq; using System.Globalization; internal static class AdvertCompatibilityTests { internal static void Run() { var snapshot = new PlayerKVSchemaSnapshot("nullable-advert-test", new[]{20001,30003}, new[]{1}, new Dictionary{{"None",0}}, CultureInfo.InvariantCulture, DateTime.UtcNow, 17); var raw = JObject.Parse(File.ReadAllText("CodexOutput/upserver/KVGameplayLoopTests/pf-source-backups/20260924T041802Z/102.json")); var advert = JObject.Parse((string)raw["data"]["VIPAdPackData"]); int assertions=0; void Check(bool value,string name) { if(!value) throw new InvalidOperationException(name); assertions++; } PlayerKVShopState Decode(JObject value, out PlayerKVSchemaDiagnostics diagnostics) { diagnostics=new PlayerKVSchemaDiagnostics(); return PlayerKVSaveSchema.DecodeShop(new Dictionary{{"VIPAdPackData",value.ToString()}},snapshot,diagnostics); } var state=Decode(advert,out var errors); Check(state!=null && errors.TotalErrors==0 && state.Vip.Time==null,"real saved explicit null must remain null"); foreach(var text in new[]{"", "2026-09-24T01:02:03Z"}) { var value=(JObject)advert.DeepClone(); value["cdTime"]=text; state=Decode(value,out errors); Check(state!=null && state.Vip.Time==text && errors.TotalErrors==0,"string unchanged"); } foreach(var bad in new JToken[]{new JValue(1),new JValue(true),new JObject(),new JArray()}) { var value=(JObject)advert.DeepClone(); value["cdTime"]=bad; Check(Decode(value,out errors)==null && errors.TotalErrors==1,"invalid time type rejected"); } var missing=(JObject)advert.DeepClone(); missing.Remove("cdTime"); Check(Decode(missing,out errors)==null && errors.TotalErrors==1,"missing time rejected"); foreach(var field in new[]{"ID","index","day","isOpen"}) { var value=(JObject)advert.DeepClone(); value[field]=JValue.CreateNull(); Check(Decode(value,out errors)==null && errors.TotalErrors==1,"other null rejected: "+field); value=(JObject)advert.DeepClone(); value.Remove(field); Check(Decode(value,out errors)==null && errors.TotalErrors==1,"other missing rejected: "+field); } Console.WriteLine($"PASS M04-advert-null-compatibility assertions={assertions} requests=0"); } }