38 lines
2.7 KiB
Python
38 lines
2.7 KiB
Python
from pathlib import Path
|
|
import hashlib,json,os,sys,tempfile,types
|
|
from unittest import mock
|
|
from PyInstaller.archive.readers import CArchiveReader
|
|
|
|
root=Path('C:/wechat_rpa')
|
|
folder=Path('C:/kefu/deploy/protocol-file-identity-20260917')
|
|
app=root/'dist/ZhenAI-WeCom-Assistant-v1.4.17'
|
|
exe=app/'ZhenAI-WeCom-Assistant-v1.4.17.exe'
|
|
manifest=json.loads((folder/'build-source-manifest.json').read_text(encoding='utf-8'))
|
|
assert sys.version_info[:2]==(3,12)
|
|
assert all(hashlib.sha256((root/name).read_bytes()).hexdigest()==digest for name,digest in manifest.items())
|
|
archive=CArchiveReader(str(exe));pyz=archive.open_embedded_archive('PYZ.pyz')
|
|
sender=pyz.extract('wecom_native_sender')
|
|
functions={item.co_name:item for item in sender.co_consts if isinstance(item,types.CodeType)}
|
|
needed=['_file_identity','_path_file_identity','_hash_client_file']
|
|
assert all(name in functions for name in needed)
|
|
assert 'stat' not in functions['_path_file_identity'].co_names
|
|
assert 'fstat' in functions['_path_file_identity'].co_names
|
|
assert '_path_file_identity' in functions['_hash_client_file'].co_names
|
|
env={'Path':Path,'os':os,'hashlib':hashlib,'NativeUnavailable':type('NativeUnavailable',(RuntimeError,),{})}
|
|
for name in needed:env[name]=types.FunctionType(functions[name],env)
|
|
with tempfile.TemporaryDirectory(prefix='frozen-identity-',dir=folder) as temp:
|
|
p=Path(temp)/'fixture.bin';content=b'packaged file identity regression\x00'*4096;p.write_bytes(content)
|
|
os.utime(p,ns=(1_600_000_000_000_000_000,1_600_000_000_000_000_000))
|
|
with mock.patch.object(Path,'stat',side_effect=AssertionError('Path.stat must not be used for handle identity')):
|
|
actual,identity=env['_hash_client_file'](p)
|
|
assert actual==hashlib.sha256(content).hexdigest()
|
|
assert identity[2]==len(content)
|
|
private={'wxwork_keys.json','protocol_sends.sqlite3','pending_replies.protocol.json','pending_replies.json','conversations.json','backend_connection.json','runtime_settings.json','desktop_account.json'}
|
|
assert not any(p.name in private for p in app.rglob('*') if p.is_file())
|
|
for relative in ['assets/ui/console-app.js','assets/ui/zhenyang-ai-console.html','assets/ui/console-review.css','assets/ui/console-responsive.css']:
|
|
assert (app/'_internal'/relative).read_bytes()==(root/relative).read_bytes(),relative
|
|
assert (app/'_internal/python312.dll').is_file()
|
|
report={'version':'1.4.17','sourceManifestMatches':True,'frozenFileIdentityFixPresent':True,'frozenHashSyntheticRegression':'passed','python312Bundled':True,'privateFilesExcluded':True,'uiAssetsMatchSource':True,'runtime':sys.version,'realMessagesSent':0,'installed':False}
|
|
(folder/'frozen-release-qa.json').write_text(json.dumps(report,ensure_ascii=False,indent=2),encoding='utf-8')
|
|
print(json.dumps(report,ensure_ascii=False))
|