"""Tests for scripts/build_common.py version stamping helpers.""" import json import os import shutil import sys import tempfile import unittest sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'scripts')) import build_common class BuildCommonTest(unittest.TestCase): def setUp(self): self.dir = tempfile.mkdtemp() self.payload = os.path.join(self.dir, 'user', 'remote_access', 'pager-webui') os.makedirs(self.payload) with open(os.path.join(self.dir, '_hak5_manifest.json'), 'w') as f: f.write('{"payload": "pager-webui", "version": "1.3.2"}') with open(os.path.join(self.payload, 'payload.sh'), 'w') as f: f.write('#!/bin/bash\n' '# Title: Mark VIII\n' '# Description: test payload\n' '# Version: 1.3.2\n' '# Category: Remote-Access\n' '\n' 'echo hi\n') with open(os.path.join(self.payload, 'server.py'), 'w') as f: f.write('"""Mark VIII server."""\n' 'import os\n' '\n' 'PORT = 8080\n') def tearDown(self): shutil.rmtree(self.dir, ignore_errors=True) def _server_text(self): with open(os.path.join(self.payload, 'server.py')) as f: return f.read() def test_stamp_version_updates_all_three_files(self): stamped = sorted(build_common.stamp_version(self.dir, '1.4.0')) expected = sorted([ os.path.join(self.dir, '_hak5_manifest.json'), os.path.join(self.payload, 'payload.sh'), os.path.join(self.payload, 'server.py'), ]) self.assertEqual(stamped, expected) with open(os.path.join(self.dir, '_hak5_manifest.json')) as f: self.assertEqual(json.load(f)['version'], '1.4.0') with open(os.path.join(self.payload, 'payload.sh')) as f: sh_text = f.read() self.assertIn('# Version: 1.4.0', sh_text) self.assertNotIn('# Version: 1.3.2', sh_text) server_text = self._server_text() self.assertIn("SERVER_VERSION = '1.4.0'", server_text) self.assertEqual(server_text.count('SERVER_VERSION'), 1) self.assertLess(server_text.index("SERVER_VERSION = '1.4.0'"), server_text.index('\nimport os')) def test_stamp_version_is_idempotent_and_upgrades(self): build_common.stamp_version(self.dir, '1.4.0') stamped = build_common.stamp_version(self.dir, '1.5.0') self.assertEqual(len(stamped), 3) server_text = self._server_text() self.assertEqual(server_text.count('SERVER_VERSION'), 1) self.assertIn("SERVER_VERSION = '1.5.0'", server_text) with open(os.path.join(self.payload, 'payload.sh')) as f: self.assertIn('# Version: 1.5.0', f.read()) def test_server_without_docstring_gets_top_injection(self): path = os.path.join(self.payload, 'server.py') with open(path, 'w') as f: f.write('# comment header\n' '\n' 'import os\n' 'PORT = 8080\n') build_common.stamp_version(self.dir, '9.9.9') text = self._server_text() lines = text.splitlines(True) idx = [i for i, l in enumerate(lines) if l.startswith('SERVER_VERSION')] self.assertEqual(len(idx), 1) self.assertLess(idx[0], [i for i, l in enumerate(lines) if l.startswith('import os')][0]) def test_missing_files_are_tolerated(self): empty = tempfile.mkdtemp() try: self.assertEqual(build_common.stamp_version(empty, '1.4.0'), []) finally: shutil.rmtree(empty, ignore_errors=True) def test_invalid_version_is_rejected(self): for bad in ("1.4'; import os", '', 'a' * 64, 'ver x'): with self.assertRaises(ValueError): build_common.stamp_version(self.dir, bad) if __name__ == '__main__': unittest.main()