98 lines
4.4 KiB
Python
98 lines
4.4 KiB
Python
"""职业生成与更新保持稳定引用,并保留手写计算规则。"""
|
|
import ast
|
|
import json
|
|
from pathlib import Path
|
|
import runpy
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parents[3]
|
|
MERGER = runpy.run_path(str(ROOT / 'scripts/migrate_generated_characters.py'))
|
|
IMPORT = 'from core.character.refs.extra.creator import CreatorSkillRef as Ref\n'
|
|
|
|
|
|
def merge(tmp_path, runtime, generated):
|
|
old, new = tmp_path / 'runtime.py', tmp_path / 'generated.py'
|
|
old.write_text(runtime)
|
|
new.write_text(generated)
|
|
return MERGER['merge'](new, old)
|
|
|
|
|
|
def block(name='名称', ref='Ref.CREATESPACE', uuid='uuid1', number=0, extra=''):
|
|
return (f'class Skill{number}(PassiveSkill):\n name = {name!r}\n'
|
|
+ (f' ref = {ref}\n' if ref else '')
|
|
+ (f' uuid = {uuid!r}\n' if uuid else '')
|
|
+ ' position = 1\n cellPos = (1, 1)\n' + extra + '\n')
|
|
|
|
|
|
def test_generate_class_uses_shared_member_normalization(monkeypatch):
|
|
scripts = ROOT / 'backend/openapi/scripts'
|
|
monkeypatch.syspath_prepend(str(scripts))
|
|
gen = runpy.run_path(str(scripts / 'generate.py'))
|
|
job = {'name': 'gunner_female', 'jobId': 'job'}
|
|
profession = {'name': 'launcher_female'}
|
|
base = ROOT / 'backend/openapi/static/data/cn/gunner_female/launcher_female'
|
|
row = next(x for x in json.loads((base / 'skill_tree.json').read_text()) if x['code'] == 'apg-63')
|
|
detail = json.loads((base / 'skillDetail' / f"{row['skillId']}.json").read_text())
|
|
skill = gen['convert_data'](detail, row['skillId'], job)
|
|
output = gen['generate_class'](skill, 0, row, job, profession)
|
|
assert 'ref = Ref.APG_63' in output
|
|
ast.parse(output)
|
|
with pytest.raises(ValueError, match='code'):
|
|
gen['generate_class'](skill, 0, {**row, 'code': None}, job, profession)
|
|
|
|
|
|
def test_renamed_skill_matches_ref_and_keeps_rules_and_alias(tmp_path):
|
|
runtime = IMPORT.replace('as Ref', 'as OldRef') + block('旧名称', 'OldRef.CREATESPACE', number=27,
|
|
extra=' associate = ["手写规则"]\n level_source = OldRef.THETREEOFSEFIROT\n def custom(self):\n return 42\n')
|
|
generated = IMPORT + block('新名称', extra=' data0 = [0, 10]\n')
|
|
output, stats = merge(tmp_path, runtime, generated)
|
|
assert stats['matched'] == 1
|
|
assert 'class Skill27(' in output
|
|
assert 'ref = OldRef.CREATESPACE' in output
|
|
assert 'level_source = OldRef.THETREEOFSEFIROT' in output
|
|
assert 'return 42' in output and '手写规则' in output
|
|
assert "name = '旧名称'" in output
|
|
assert 'data0 = [0, 10]' in output
|
|
ast.parse(output)
|
|
|
|
|
|
def test_old_uuid_matches_after_rename_and_gets_ref_import(tmp_path):
|
|
output, stats = merge(tmp_path, block('旧名称', ref=None), IMPORT + block('新名称'))
|
|
assert stats['matched'] == 1
|
|
assert IMPORT.strip() in output
|
|
assert 'ref = Ref.CREATESPACE' in output
|
|
output2, _ = merge(tmp_path, output, IMPORT + block('新名称'))
|
|
assert output == output2
|
|
|
|
|
|
def test_runtime_only_skills_survive_and_new_skills_stay_commented(tmp_path):
|
|
old = IMPORT + block() + block('手写技能', ref='Ref.BACKSTEP', uuid='manual', number=9,
|
|
extra=' def effect(self, old, new):\n return 42\n')
|
|
new = IMPORT + block() + block('新增技能', ref='Ref.THETREEOFSEFIROT', uuid='new', number=1)
|
|
output, stats = merge(tmp_path, old, new)
|
|
assert stats['runtime_only'] == stats['commented'] == 1
|
|
assert 'class Skill9(' in output and 'return 42' in output
|
|
assert '# class Skill1(' in output
|
|
assert len([x for x in ast.parse(output).body if isinstance(x, ast.ClassDef)]) == 2
|
|
|
|
|
|
@pytest.mark.parametrize(('old_ref', 'old_uuid', 'new_ref', 'new_uuid'), [
|
|
('Ref.CREATESPACE', 'one', 'Ref.THETREEOFSEFIROT', 'one'),
|
|
(None, 'one', None, 'two'),
|
|
])
|
|
def test_identity_conflicts_do_not_fall_back_to_name(tmp_path, old_ref, old_uuid, new_ref, new_uuid):
|
|
with pytest.raises(ValueError, match='冲突'):
|
|
merge(tmp_path, IMPORT + block(ref=old_ref, uuid=old_uuid), IMPORT + block(ref=new_ref, uuid=new_uuid))
|
|
|
|
|
|
def test_duplicate_refs_fail_before_merge(tmp_path):
|
|
with pytest.raises(ValueError, match='duplicate ref_key'):
|
|
merge(tmp_path, IMPORT + block() + block('另一个技能', uuid='uuid2', number=1), IMPORT + block())
|
|
|
|
|
|
def test_legacy_name_fallback_without_identity(tmp_path):
|
|
output, stats = merge(tmp_path, block(ref=None, uuid=None), block(ref=None, uuid=None))
|
|
assert stats['matched'] == 1
|
|
ast.parse(output)
|