test(upserver): 归档存档迁移与玩法闭环回归工具
This commit is contained in:
98
CodexOutput/upserver/KVGameLoadTests/FishSchemaTests.cs
Normal file
98
CodexOutput/upserver/KVGameLoadTests/FishSchemaTests.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using System.Globalization;
|
||||
using game.PlayerDataStorage;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
static partial class Tests
|
||||
{
|
||||
static PlayerKVSchemaSnapshot FishSnapshot(Dictionary<int,PlayerKVFishConfig> fish=null,Dictionary<int,PlayerKVFishCardConfig> cards=null) =>
|
||||
new("fish-v1",new[]{101},new[]{1,2},new Dictionary<string,int>(),CultureInfo.InvariantCulture,SchemaSnapshot().NowUtc,17,
|
||||
fish:fish ?? new() { [101]=new(101,1,5,11,2,1,.5f),[102]=new(102,2,6,12,2,1,.5f) },
|
||||
cards:cards ?? new() { [11]=new(11,1,new[]{10},new[]{.25f,.5f}),[12]=new(12,1,new[]{20},Array.Empty<float>()) });
|
||||
static Dictionary<string,string> FishRaw(string record="2|99|1|7|True") => new() { ["Map_5_1"]=JsonConvert.SerializeObject(new Dictionary<int,string>{[101]=record}) };
|
||||
static PlayerKVFishState FishDecode(Dictionary<string,string> raw,PlayerKVSchemaSnapshot snapshot=null,Dictionary<string,string> modern=null) =>
|
||||
PlayerKVSaveSchema.DecodeFish(raw,modern ?? new(),snapshot ?? FishSnapshot(),new());
|
||||
static async Task RunFishSchemaTests()
|
||||
{
|
||||
await Run("M03-D5-raw-before-derived-and-config-snapshot",() =>
|
||||
{
|
||||
var a=FishDecode(FishRaw());
|
||||
Check(a.RawMaps[1][101].Level==2 && a.RawMaps[1][101].Weight==99 && a.RawMaps[1][101].Cards==1,"raw values retained");
|
||||
Check(a.Maps[1][101].Level==1 && a.Maps[1][101].Weight==4.5f && a.Maps[1][101].Cards==10 && a.Cards[11]==10,"weight before level cap, cards raised");
|
||||
Check(ReferenceEquals(a.Fish[101],a.Maps[1][101]) && a.Fish[101].Number==7 && a.Fish[101].New,"pure index coherence");
|
||||
var r=FishRaw(); r["Map_1"]="{\"101\":{\"Level\":2,\"MaxG\":98,\"TotalCard\":1,\"TotalNum\":7,\"NewFish\":true}}";
|
||||
var errors=new PlayerKVSchemaDiagnostics();
|
||||
Check(PlayerKVSaveSchema.DecodeFish(r,new Dictionary<string,string>(),FishSnapshot(),errors)==null && errors.Count(3,PlayerKVSchemaError.ConflictingRepresentations)==1,"different raw weights cannot normalize into equality");
|
||||
r["Map_1"]=r["Map_1"].Replace("98","99"); Check(FishDecode(r)!=null,"matching formats accepted");
|
||||
r["Map_5_1"]="{}"; Check(FishDecode(r).Maps[1].Count==0,"new empty replaces old map");
|
||||
var required=new[]{10}; var weights=new[]{.5f}; var configs=new Dictionary<int,PlayerKVFishConfig>{[101]=new(101,1,5,11,2,1,0)};
|
||||
var cards=new Dictionary<int,PlayerKVFishCardConfig>{[11]=new(11,1,required,weights)}; var snapshot=FishSnapshot(configs,cards);
|
||||
required[0]=99; weights[0]=99; configs.Clear(); cards.Clear();
|
||||
a=FishDecode(FishRaw("1|99|1|7|false"),snapshot);
|
||||
Check(a.Fish[101].Weight==3 && a.Fish[101].Cards==10,"input configuration collections copied");
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
await Run("M03-D5-weight-directions-quality-and-invalid-config",() =>
|
||||
{
|
||||
Check(FishDecode(FishRaw("1|.125|1|7|false")).Fish[101].Weight==.125f,"P3-3 low weight not raised");
|
||||
Check(FishDecode(FishRaw("0|-2|-3|-4|false")).Fish[101].Weight==-2,"finite negatives not banned");
|
||||
Check(FishDecode(FishRaw("-1|99|-3|-4|false")).Fish[101].Level==-1,"nonpositive level preserved");
|
||||
var high=new Dictionary<string,string>{["Map_5_2"]="{\"102\":\"5|99|1|7|False\"}"};
|
||||
var a=FishDecode(high); Check(a.Fish[102].Level==1 && a.Fish[102].Weight==99 && a.Cards[12]==20,"quality6 skips weight but caps level");
|
||||
foreach(var record in new[]{"3|99|1|7|true","2147483647|99|1|7|true"}) Check(FishDecode(FishRaw(record))==null,"weight index checked before cap");
|
||||
foreach(var fish in new[]{new PlayerKVFishConfig(101,2,5,11,2,1,0),new(101,1,5,999,2,1,0),new(101,1,5,11,float.MaxValue,2,0),new(101,1,5,11,float.NaN,1,0)})
|
||||
{
|
||||
var e=new PlayerKVSchemaDiagnostics();
|
||||
Check(PlayerKVSaveSchema.DecodeFish(FishRaw(),new Dictionary<string,string>(),FishSnapshot(new(){[101]=fish}),e)==null && e.Count(3,PlayerKVSchemaError.UnsupportedSchema)==1,"invalid config pre-apply rejection");
|
||||
}
|
||||
foreach(var card in new[]{new PlayerKVFishCardConfig(11,0,new[]{10},new[]{0f,0f}),new(11,1,Array.Empty<int>(),new[]{0f,0f}),new(11,1,new[]{10},new[]{0f,float.PositiveInfinity})})
|
||||
Check(FishDecode(FishRaw(),FishSnapshot(cards:new(){[11]=card}))==null,"card bounds and finite weights");
|
||||
Check(FishDecode(FishRaw(),FishSnapshot(fish:new Dictionary<int,PlayerKVFishConfig>()))==null,"unknown fish rejected");
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
await Run("M03-D5-all-fields-accessories-and-luck",() =>
|
||||
{
|
||||
var raw=FishRaw();
|
||||
var intKeys=new[]{"RodData","RodLevelData","FishSwimToMap","FishSwimToMapCount","MapProgress","HonorLevelData"};
|
||||
foreach(var k in intKeys) raw[k]="{\"-1\":7}";
|
||||
raw["InitRodAccessoriesData"]="{\"GloveID\":1,\"RodToSkin\":{\"2\":3},\"GloveIds\":[4],\"SkinIds\":[5],\"GloveNewIds\":[6],\"SkinNewIds\":[7]}";
|
||||
raw["LuckDataDic"]="{\"InitFishLuck\":1.25,\"InitBotLuck\":2.5,\"BotCount\":3,\"unknown\":4}";
|
||||
var modern=new Dictionary<string,string>{["LDD"]=raw["LuckDataDic"],["ld"]="1.25|2.5|3"};
|
||||
var a=FishDecode(raw,modern:modern);
|
||||
Check(new[]{a.Rods,a.RodLevels,a.Swim,a.SwimCount,a.Progress,a.Honor}.All(m=>m[-1]==7),"six maps");
|
||||
var acc=a.Accessories; Check(acc.Glove==1 && acc.RodSkin[2]==3 && acc.Gloves[0]==4 && acc.Skins[0]==5 && acc.NewGloves[0]==6 && acc.NewSkins[0]==7,"all six accessories fields");
|
||||
Check(a.Luck.Count==3 && !a.Luck.ContainsKey("unknown"),"highest priority whole luck replaces");
|
||||
foreach(var key in raw.Keys)
|
||||
{
|
||||
var bad=new Dictionary<string,string>(raw){[key]="null"}; Check(FishDecode(bad,modern:modern)==null,"present bad rejected");
|
||||
bad[key]=null; Check(FishDecode(bad,modern:modern)==null,"present null rejected");
|
||||
}
|
||||
foreach(var invalid in new[]{"1|2|3.5","1|2|2147483648","1|2|-2147483904","NaN|2|3","1|2|3|4",null})
|
||||
Check(FishDecode(new(),modern:new(){["ld"]=invalid})==null,"bad compact or lossy BotCount");
|
||||
Check(FishDecode(new(),modern:new(){["ld"]="0|0|-2147483648"}).Luck["BotCount"]==int.MinValue,"exact lower bound allowed");
|
||||
modern["ld"]="1.25|2.5|4"; var e=new PlayerKVSchemaDiagnostics();
|
||||
Check(PlayerKVSaveSchema.DecodeFish(raw,modern,FishSnapshot(),e)==null && e.Count(3,PlayerKVSchemaError.ConflictingRepresentations)>0,"luck conflicts detected");
|
||||
a=FishDecode(new()); Check(a.Maps.Count==0 && a.Fish.Count==0 && a.Cards.Count==0 && a.Accessories==null && a.Luck.Count==0 && a.Rods.Count==0,"missing explicit empty");
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
await Run("M03-D5-map-layout-compatibility-and-order",() =>
|
||||
{
|
||||
var a=FishDecode(FishRaw("2|99|ignored||compat|1|7|True|tail"));
|
||||
Check(a.RawMaps[1][101].Compatibility.SequenceEqual(new[]{"ignored","","compat","tail"}),"all four ignored slots retained");
|
||||
foreach(var record in new[]{"2|99|1|7","2|99|1|7|true|extra","2|NaN|1|7|true","2|99|1.5|7|true","2|99|1|7|1"})
|
||||
Check(FishDecode(FishRaw(record))==null,"malformed consumed slots");
|
||||
foreach(var key in new[]{"Map_5_01","Map_0","Map_5_0","Map_3","Map_2147483648"})
|
||||
Check(FishDecode(new(){[key]="{}"})==null,"invalid map suffix");
|
||||
Check(FishDecode(new(){["Map_other"]="not-json"})!=null,"nonmatching keys unconsumed");
|
||||
var fish=new Dictionary<int,PlayerKVFishConfig>{[101]=new(101,1,6,11,1,1,0),[102]=new(102,2,6,11,1,1,0)};
|
||||
var raw=new Dictionary<string,string>{["Map_5_1"]="{\"101\":\"0|1|1|1|true\"}",["Map_5_2"]="{\"102\":\"0|1|2|1|true\"}"};
|
||||
foreach(var r in new[]{raw,raw.Reverse().ToDictionary(p=>p.Key,p=>p.Value)})
|
||||
{
|
||||
var e=new PlayerKVSchemaDiagnostics(); Check(PlayerKVSaveSchema.DecodeFish(r,new Dictionary<string,string>(),FishSnapshot(fish),e)==null && e.Count(3,PlayerKVSchemaError.UnsupportedSchema)==1,"card conflict order independent");
|
||||
}
|
||||
raw["Map_5_2"]="{\"102\":\"0|1|1|1|true\"}";
|
||||
Check(FishDecode(raw,FishSnapshot(fish)).Cards[11]==1,"matching shared card quantity allowed");
|
||||
Check(FishDecode(new(){["Map_5_1"]="{\"101\":\"0|1|1|1|true\",\"0101\":\"0|1|1|1|true\"}"})==null,"normalized duplicate fish ids");
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user