feat:完善加点代码导入
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
import base64
|
||||
import struct
|
||||
import zlib
|
||||
from contextlib import nullcontext
|
||||
|
||||
import pytest
|
||||
|
||||
from core.open import skillTree
|
||||
|
||||
from api.utils.skill_tree import load_skill_tree_map
|
||||
from core.open.skillTree import decode_skill_hash, get_openapi_skill_tree_info, get_skill_tree_info
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def no_live_redis(monkeypatch):
|
||||
monkeypatch.setattr(skillTree, 'get_redis_client', lambda: nullcontext(None))
|
||||
|
||||
|
||||
def encode(skills=(), job=(0, 3), remaining=b''):
|
||||
header = bytearray(14)
|
||||
header[0] = 0xF3
|
||||
header[2:4] = bytes(job)
|
||||
header[5] = 115
|
||||
header[8] = ord('K')
|
||||
data = struct.pack('<H', len(skills) << 8)
|
||||
for index, level in skills:
|
||||
data += struct.pack('<HH', (index & 255) << 8, (level << 8) | (index >> 8))
|
||||
# 无进化/强化,两个快捷栏:鬼斩、空槽。
|
||||
data += bytes([0, 0, 0, 2, 5, 0, 0, 0]) + remaining
|
||||
return base64.b64encode(zlib.compress(header + data)).decode()
|
||||
|
||||
|
||||
def test_decode_adds_missing_auto_skills_without_overwriting_encoded_levels():
|
||||
result = decode_skill_hash(encode([(5, 7)]))
|
||||
assert result['skills'][0]['level'] == 7
|
||||
automatic = {item['skill_index']: item['level'] for item in result['auto_skills']}
|
||||
tree = load_skill_tree_map('swordman_male', 'berserker')
|
||||
assert automatic == {
|
||||
int(key): item['autoLevel'] for key, item in tree.items()
|
||||
if 'autoLevel' in item and key != '5'
|
||||
}
|
||||
assert automatic[174] == 115
|
||||
assert 'arrangements' not in result
|
||||
|
||||
|
||||
def test_openapi_result_does_not_create_character(monkeypatch):
|
||||
import core.basic.character as character
|
||||
|
||||
def fail(*args, **kwargs):
|
||||
raise AssertionError('must not create a character')
|
||||
|
||||
monkeypatch.setattr(character, 'createCharacter', fail)
|
||||
result = get_openapi_skill_tree_info(encode())
|
||||
assert result['skills']['5']['lv'] == 1
|
||||
assert result['shortcuts'] == [
|
||||
{'slot': 0, 'skill_id': 5, 'skill_name': '鬼斩'},
|
||||
{'slot': 1, 'skill_id': None, 'skill_name': None},
|
||||
]
|
||||
|
||||
|
||||
def test_calculator_receives_automatic_levels_and_shortcuts():
|
||||
result = get_skill_tree_info(encode())
|
||||
skills = {item['name']: item for item in result['skills'].values()}
|
||||
assert skills['鬼斩']['lv'] == 1
|
||||
assert skills['基础精通']['lv'] == 115
|
||||
assert result['shortcuts'][0]['skill_name'] == '鬼斩'
|
||||
|
||||
|
||||
def test_dark_knight_arrangements_and_remaining_are_preserved():
|
||||
remaining = bytes([1, 118, 0, 1, 60, 0])
|
||||
result = decode_skill_hash(encode(job=(9, 0), remaining=remaining))
|
||||
assert result['arrangements'] == [{'slot_index': 118, 'skills': [60]}]
|
||||
assert result['section2_remaining'] == remaining
|
||||
assert result['auto_skills']
|
||||
|
||||
|
||||
def test_context_uses_separate_redis_map_cache(monkeypatch):
|
||||
from config.main import config
|
||||
|
||||
monkeypatch.setattr(config, 'DEBUG_MODE', False)
|
||||
|
||||
class FakeRedis:
|
||||
value = None
|
||||
keys = []
|
||||
|
||||
def get(self, key):
|
||||
self.keys.append(key)
|
||||
return self.value
|
||||
|
||||
def set(self, key, value):
|
||||
self.value = value
|
||||
|
||||
redis = FakeRedis()
|
||||
monkeypatch.setattr(skillTree, 'get_redis_client', lambda: nullcontext(redis))
|
||||
header = {'job_bytes': (0, 3)}
|
||||
_, original = skillTree._get_skill_tree_context(header)
|
||||
|
||||
def fail(*args):
|
||||
raise AssertionError('cache hit must not load local data')
|
||||
|
||||
monkeypatch.setattr(skillTree, 'load_skill_tree_map', fail)
|
||||
_, cached = skillTree._get_skill_tree_context(header)
|
||||
assert cached == original
|
||||
assert cached['174']['autoLevel'] == 115
|
||||
assert redis.keys == ['openapi:skillTreeMap:v1:swordman_male:berserker:map'] * 2
|
||||
|
||||
|
||||
@pytest.mark.parametrize(('code', 'interval_ms'), [
|
||||
('eJytjb9OwnAABu9CmpQG0MUYjBYHaCRi8ytQW/7sDiQ46OxLuMArsTCwM/lGvICBZ+Cbbrjcd4o6UeOXzQqk1Qo/26/P+Jt1O3ykLI1c2HZuk5mxtXdW3vvuLaUPTtWJN47tUpgZTM199c3c0YUSX0wYGnxSMnWg9JVHE/bIDumpqXpEDsREuRZaayWlNEZSSS2dc3ro1MyZE/8srA0uXJ7VIzugLzw7cM/1Znl5qoTTP4KaGC0=', 0),
|
||||
('eJytjT9OwnAARt8LaVIb/iXGEI1WB2gkQvOrUFuE3cFEBp25hIvewLOwMLgzcSMuQOAMfNMbXt63j9pR45ufd5BmM6x+P5fxFx+t8JayMHJuy1cvmBlbe2Vlzxe7lN44VSd2fPaawsxgau6TY3NHJ0p8NGFo8E7J1IHSV25N2CBr5F5N1S3yT0yUa6G1VlJKYySV1NI+podOzZw5cWdhbXDu4qhuWQN94cGBG843y9NT5d/l/gCEWBjI', 5000),
|
||||
])
|
||||
def test_confirmed_skill_combo_samples(code, interval_ms):
|
||||
result = decode_skill_hash(code)
|
||||
assert result['skill_combo'] == {
|
||||
'skills': [309, 305, 312, 311], 'interval_ms': interval_ms,
|
||||
}
|
||||
raw = zlib.decompress(base64.b64decode(code))
|
||||
assert result['section2_remaining'] == raw[-103:] + b'\x00'
|
||||
|
||||
|
||||
def test_skill_combo_preserves_empty_slots_and_rejects_truncated_layout():
|
||||
remaining = bytearray(103)
|
||||
remaining[92:100] = struct.pack('<4H', 98, 114, 93, 0)
|
||||
remaining[102] = 0xF3
|
||||
original = bytes(remaining)
|
||||
assert skillTree._parse_skill_combo(original) == {
|
||||
'skills': [98, 114, 93, None], 'interval_ms': 0,
|
||||
}
|
||||
assert bytes(remaining) == original
|
||||
assert skillTree._parse_skill_combo(original[:102]) is None
|
||||
assert skillTree._parse_skill_combo(bytes(103)) is None
|
||||
assert 'skill_combo' not in decode_skill_hash(encode(job=(9, 0)))
|
||||
Reference in New Issue
Block a user