test(upserver): 归档存档迁移与玩法闭环回归工具

This commit is contained in:
Liubing\LB
2026-09-28 18:16:14 +08:00
parent c4ae02def9
commit a6f5e107f8
232 changed files with 12903 additions and 0 deletions

View File

@@ -0,0 +1 @@
<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net9.0</TargetFramework><ImplicitUsings>enable</ImplicitUsings><RestoreConfigFile>$(MSBuildThisFileDirectory)../MinimalLoginTests/NuGet.Config</RestoreConfigFile></PropertyGroup><ItemGroup><Reference Include="Newtonsoft.Json"><HintPath>../../../Client/Library/PackageCache/com.unity.nuget.newtonsoft-json@3.2.1/Runtime/Newtonsoft.Json.dll</HintPath></Reference></ItemGroup></Project>

View File

@@ -0,0 +1,49 @@
void Assert(bool ok){if(!ok)throw new Exception("Club regression failed");}
int passed=0;
foreach(var body in new string[]{null,"","[]","{}","bad","{\"code\":\"0\"}","{\"code\":2}","{\"code\":1,\"clubInfo\":{\"id\":8}}","{\"code\":0}","{\"code\":0,\"clubInfo\":{\"id\":0}}"}){
var s=new Subject();s.customServerMgr.Response=body;var old=s.myClubInfo;
Assert(await s.CreateClub(new ClubSettingData())==null && ReferenceEquals(old,s.myClubInfo) && s.Effects==0 && s.chatService.Effects==0);passed++;
}
foreach(var body in new string[]{null,"","[]","{}","bad","{\"code\":\"0\"}","{\"code\":2}"}){
var s=new Subject();s.customServerMgr.Response=body;var old=s.myClubInfo;var toasts=ToastPanel.Count;
s.QuitClub();Assert(ReferenceEquals(old,s.myClubInfo) && s.Effects==0 && s.chatService.Effects==0 && ToastPanel.Count==toasts);passed++;
}
{
var s=new Subject();s.customServerMgr.Response="{\"code\":0,\"clubInfo\":{\"id\":8}}";
Assert((await s.CreateClub(new ClubSettingData())).id==8 && ((CreateClubRequest)s.customServerMgr.Body).playerId=="101" && s.internalIdToPlayerId[42]=="101" && s.memberRole==EMemberRole.Owner);passed++;
}
foreach(var code in new[]{0,1}){var s=new Subject();s.customServerMgr.Response=$"{{\"code\":{code}}}";s.QuitClub("102");Assert(s.myClubInfo==null && s.chatService.Effects==1 && ((QuitClubRequest)s.customServerMgr.Body).appointId=="102" && ((QuitClubRequest)s.customServerMgr.Body).playerId=="101");passed++;}
{
var s=new Subject();var old=s.myClubInfo;s.customServerMgr.Gate=new();var pending=s.CreateClub(new ClubSettingData());s.customServerMgr.SessionVersion++;s.userService.ApiPlayerId="102";s.customServerMgr.Gate.SetResult("{\"code\":0,\"clubInfo\":{\"id\":8}}");Assert(await pending==null && ReferenceEquals(old,s.myClubInfo) && s.Effects==0);passed++;
}
{
var s=new Subject();var old=s.myClubInfo;s.customServerMgr.Gate=new();s.QuitClub();s.customServerMgr.SessionVersion++;s.customServerMgr.Gate.SetResult("{\"code\":0}");Assert(ReferenceEquals(old,s.myClubInfo) && s.Effects==0);passed++;
}
{
var s=new Subject();s.userService.IsAuthorized=false;Assert(await s.CreateClub(new ClubSettingData())==null);s.QuitClub();s.Demote("102");s.Promote("102",EMemberRole.Elder);s.Kickout("102");Assert(s.customServerMgr.Calls==0);passed++;
}
{
var s=new Subject();s.QuitClub("101");s.QuitClub("HEX");s.Demote("101");s.Promote("HEX",EMemberRole.Elder);s.Kickout("101");Assert(s.customServerMgr.Calls==0);s.Demote("102");Assert(((DemoteClubRequest)s.customServerMgr.Body).demoteId=="102" && ((DemoteClubRequest)s.customServerMgr.Body).playerId=="101");passed++;
}
{
var s=new Subject();s.customServerMgr.Response="{\"code\":2}";s.Kickout("102");Assert(s.myClubInfo.curMembers==3);s.customServerMgr.Response="{\"code\":0}";s.Kickout("102");Assert(s.myClubInfo.curMembers==2);passed++;
}
{
var s=new Subject();s.customServerMgr.Response="{\"code\":4}";s.HandleApply(false,"");Assert(s.clubPlayerInfos.Count==0 && s.myClubInfo.curMembers==3);passed++;
s=new Subject();s.customServerMgr.Response="{\"code\":0}";s.HandleApply(false,"");Assert(s.clubPlayerInfos.Count==1);passed++;
s=new Subject();s.HandleApply(true,"");Assert(s.customServerMgr.Calls==0);passed++;
}
{
var s=new Subject();s.customServerMgr.Gate=new();var rewards=PlayerItemData.Rewards;var writes=PlayFabMgr.Instance.Writes;
var first=s.ClubSingin();await s.ClubSingin();Assert(s.customServerMgr.Calls==1 && s.signTime==null);
s.customServerMgr.Gate.SetResult("{\"code\":0}");await first;await s.ClubSingin();Assert(s.customServerMgr.Calls==1 && PlayerItemData.Rewards==rewards+1 && PlayFabMgr.Instance.Writes==writes+1);passed++;
}
{
var s=new Subject();var rewards=PlayerItemData.Rewards;s.customServerMgr.Response=null;await s.ClubSingin();Assert(s.signTime==null && PlayerItemData.Rewards==rewards);
s.customServerMgr.Response="{\"code\":0}";await s.ClubSingin();Assert(s.signTime!=null && s.customServerMgr.Calls==2 && PlayerItemData.Rewards==rewards+1);passed++;
}
{
var s=new Subject();var rewards=PlayerItemData.Rewards;var writes=PlayFabMgr.Instance.Writes;s.customServerMgr.Gate=new();var old=s.ClubSingin();s.customServerMgr.SessionVersion++;s.userService.ApiPlayerId="102";s.customServerMgr.Gate.SetResult("{\"code\":0}");await old;Assert(s.signTime==null && PlayerItemData.Rewards==rewards && PlayFabMgr.Instance.Writes==writes);passed++;
}
Console.WriteLine($"PASS {passed} club cases: failure preserves state, explicit business success, decimal self/target identity, stale responses, no duplicate creation charge path");

View File

@@ -0,0 +1,42 @@
from pathlib import Path
s=Path('Client/Assets/Scripts/Services/ClubService.cs').read_text(encoding='utf-8-sig')
models=Path('Client/Assets/Scripts/Services/ClubModel.cs').read_text(encoding='utf-8-sig')
def block(text,marker):
start=text.index(marker);a=text.index('{',start);depth=1;b=a+1
while depth:
depth+=(text[b]=='{')-(text[b]=='}');b+=1
return text[start:b]
helper=s[s.index(' private static bool IsDecimalPlayerId'):s.index(' public async Task<List<SocialInfoItemData>> GetClubDetails')]
methods='\n'.join(block(s,m) for m in ['public async Task<ClubInfo> CreateClub(', 'public async void QuitClub(', 'public async void Demote(', 'public async void Promote(', 'public async void Kickout(', 'public async void HandleApply(', 'public async Task ClubSingin('])
dtos='\n'.join(block(models,'public class '+name+'\n') for name in ['ClubInfo','ClubSettingData','CreateClubRequest','CreateClubResp','QuitClubRequest','QuitClubResp','DemoteClubRequest','PromoteClubRequest','KickoutClubRequest','KickoutClubResp','HandleApplyRequest','HandleApplyResp','ClubSinginRequest','ClubSinginResp'])
code='''// Generated from production methods; do not edit. Engine and transport are controlled test doubles.
using System;using System.Collections.Generic;using System.ComponentModel;using System.Linq;using System.Threading.Tasks;using Newtonsoft.Json.Linq;
public enum EMemberRole:byte {Owner,Vice,Member,Elder}
'''+dtos+'''
public class Subject {
public User userService=new();public Server customServerMgr=new();public Chat chatService=new();public ClubInfo myClubInfo=new(){id=9,curMembers=3};public EMemberRole memberRole=EMemberRole.Member;public Dictionary<long,string> internalIdToPlayerId=new();public int Effects;
private int? signingSession; public DateTime? signTime; public List<Person> clubPlayerInfos=new(){new Person{playFabId="102"}}; public bool CanAccepted=true; void SetClubRed(){} void ShowPromote(){Effects++;}void MyClubInfoChange(bool join=false){Effects++;}void ClearLikeCursorIds(){Effects++;}
'''+helper+methods+'''
}
public class User{public string UserId="8BDE34F732DD4210";public string ApiPlayerId="101";public bool IsAuthorized=true;public uint InternalId=42;}
public class Server{public int SessionVersion;public int Calls;public object Body;public string Response;public TaskCompletionSource<string> Gate;public Task<string> ClubRequest(string path,object data){Calls++;Body=data;return Gate?.Task??Task.FromResult(Response);}}
public class Chat{public int Effects;public void SendSystemMessage(string text){Effects++;}public void OnQuit(){Effects++;}public void GetConversationList(long id){Effects++;}}
public class ClubSttingUpdateEvent{} public static class GContext {public static Container container=new();public static int Events;public static void Publish(object value){Events++;}}
public static class ToastPanel{public static int Count;public static void Show(string text){Count++;}}
public static class LocalizationMgr{public static string GetText(string key)=>key;}
'''
Path('CodexOutput/upserver/ClubRegression/Generated.cs').write_text(code)
p=Path('CodexOutput/upserver/ClubRegression/Generated.cs')
with p.open('a') as f:f.write("""
public class Person{public string playFabId;}
public enum RedPointName {ApplyList}
public class RedPointManager {public static RedPointManager Instance=new();public void SetRedPointState(RedPointName n,bool b){}}
public static class ZZTimeHelper {public static DateTime UtcNow()=>new DateTime(2026,9,21);public static DateTime UtcNowOffset(this DateTime time)=>time;}
public class PlayFabMgr {public static PlayFabMgr Instance=new();public int Writes;public void UpdateUserDataValue(string k,string v){Writes++;}}
public enum MessageChatType {SI}
public class Container {public T Resolve<T>() where T:new()=>new T();}
public class PlayerItemData {public static int Rewards;public void AddItemByDrop(int id,object ctx){Rewards++;}}
public class ShowData{}
namespace cfg {public class Tables {public Config TbServerConfig=new();}public class Config{public int ClubSignReward=1;}}
""")