import json, urllib.request base='http://127.0.0.1:8765' def post(path, data): req=urllib.request.Request(base+path, data=json.dumps(data).encode(), headers={'Content-Type':'application/json'}) try: return json.loads(urllib.request.urlopen(req, timeout=5).read()) except Exception as e: body=e.read().decode() if hasattr(e,'read') else str(e) return json.loads(body) if body.startswith('{') else {'ok':False,'error':body} def get_room(code): return json.loads(urllib.request.urlopen(base+'/api/room?code='+code).read())['room'] # 复刻前端 renderActions 的近似:history 里不同叫骰人数 def recent_caller_count(room): s=set() for h in room.get('history',[]): if h.get('type')=='call' and h.get('player'): s.add(h['player']) return len(s) # 4 人局:房主 + 3 机器人 r=post('/api/create', {'game':'dice','name':'小鱼','playerId':'p-host'}) code=r['room']['code'] for _ in range(3): post('/api/add-bot', {'code':code,'playerId':'p-host'}) post('/api/start', {'code':code}) # 房主先叫,再让 3 个机器人各叫一手,回到房主回合 post('/api/call', {'code':code,'playerId':'p-host','count':4,'point':1}) for _ in range(3): post('/api/bot-turn', {'code':code,'playerId':'p-host'}) room=get_room(code) callers=recent_caller_count(room) my_turn = room['turnPlayerId']=='p-host' frontend_shows_open3 = my_turn and len(room['players'])>=4 and callers>=3 print('turn=', room['turnPlayerName'], 'distinct callers=', callers, 'frontend_shows_open3=', frontend_shows_open3) assert frontend_shows_open3, '前端门控未点亮开三家,测试前提不成立' # 前端点亮了 → 服务端必须接受 res=post('/api/multi-open3', {'code':code,'playerId':'p-host'}) print('server multi-open3 ok=', res['ok'], '| status=', res.get('room',{}).get('status') if res['ok'] else res.get('error')) assert res['ok'], '前端亮了开三家但服务端拒绝——门控与服务端不一致' assert res['room']['status']=='result', '开三家应直接结算' print('result:', res['room']['lastResult']['text']) print('PASS: 动作门控与服务端接受判定一致(开三家)')