Текстовый файл из учебного комплекта, не свидетельство установленного runtime. Вернуться к учебнику · Исходный файл
1 """Проверки аудиоупаковки и структуры учебника. Реальной сети/браузера нет.""" 2 import base64 3 import contextlib 4 import email.policy 5 from email.parser import BytesParser 6 from html.parser import HTMLParser 7 import html 8 import importlib.util 9 import io 10 import json 11 import hashlib 12 from pathlib import Path 13 import re 14 import subprocess 15 import tempfile 16 import unittest 17 from unittest.mock import patch 18 from urllib.parse import unquote, urlparse 19 import urllib.request 20 import xml.etree.ElementTree as ET 21 22 REPO = Path(__file__).resolve().parents[2] 23 BOOK = REPO / "docs/codex-cli-voice-textbook.html" 24 25 26 class Structure(HTMLParser): 27 def __init__(self): 28 super().__init__(convert_charrefs=True) 29 self.ids, self.links, self.stack, self.rects, self.chapters = {}, [], [], [], [] 30 self.h1 = 0 31 def handle_starttag(self, tag, attrs): 32 attrs = dict(attrs) 33 if "id" in attrs: 34 if attrs["id"] in self.ids: 35 raise AssertionError("duplicate ID " + attrs["id"]) 36 self.ids[attrs["id"]] = attrs 37 if tag == "a": 38 self.links.append(attrs.get("href", "")) 39 if tag == "h1": self.h1 += 1 40 if "chapter" in attrs.get("class", "").split(): self.chapters.append(attrs["id"]) 41 if tag == "rect" and any(t == "figure" for t, _ in self.stack): 42 self.rects.append(any(t == "a" and a.get("href", "").startswith("#") for t, a in self.stack)) 43 if tag == "img": 44 src = attrs.get("src", "") 45 if not src.startswith("data:image/png;base64,") or not attrs.get("alt"): 46 raise AssertionError("image must be embedded PNG with alt") 47 data = base64.b64decode(src.split(",", 1)[1], validate=True) 48 if not data.startswith(b"\x89PNG\r\n\x1a\n") or len(data) > 2_000_000: 49 raise AssertionError("invalid or oversized screenshot") 50 if tag in ("script", "iframe", "link") and ("src" in attrs or "href" in attrs): 51 raise AssertionError("external executable/asset") 52 if tag not in ("meta", "br", "input", "hr", "link", "img"): 53 self.stack.append((tag, attrs)) 54 def handle_startendtag(self, tag, attrs): 55 self.handle_starttag(tag, attrs) 56 if tag not in ("meta", "br", "input", "hr", "link", "img"): 57 self.handle_endtag(tag) 58 def handle_endtag(self, tag): 59 if not self.stack or self.stack.pop()[0] != tag: 60 raise AssertionError("unbalanced tag " + tag) 61 62 63 class TextbookTests(unittest.TestCase): 64 def setUp(self): 65 self.source = BOOK.read_text() 66 self.parsed = Structure() 67 self.parsed.feed(self.source) 68 69 def test_unique_ids_balanced_autonomous_and_original_anchors(self): 70 self.assertFalse(self.parsed.stack) 71 self.assertEqual(self.parsed.h1, 1) 72 self.assertTrue({"overview", "cli", "protocols", "demo", "launch", "evidence"}.issubset(self.parsed.chapters)) 73 self.assertEqual(len(self.parsed.chapters), 29) 74 self.assertIn("ui-entrypoints", self.parsed.chapters) 75 76 def test_every_svg_block_is_real_anchor_and_paths_exist(self): 77 self.assertTrue(self.parsed.rects) 78 self.assertTrue(all(self.parsed.rects)) 79 for url in self.parsed.links: 80 with self.subTest(url=url): 81 if url.startswith("#"): 82 self.assertIn(unquote(url[1:]), self.parsed.ids) 83 elif url.startswith("file:"): 84 self.assertTrue(Path(unquote(urlparse(url).path)).exists(), url) 85 else: 86 if url.startswith("https://"): 87 continue 88 self.assertFalse(urlparse(url).scheme, url) 89 target = (BOOK.parent / unquote(urlparse(url).path)).resolve() 90 self.assertTrue(target.is_file(), url) 91 if target.suffix == ".html" and urlparse(url).fragment: 92 parsed = Structure() 93 parsed.feed(target.read_text()) 94 self.assertIn(unquote(urlparse(url).fragment), parsed.ids) 95 96 def test_js_syntax_and_shell_command_blocks(self): 97 scripts = re.findall(r"<script>(.*?)</script>", self.source, re.S) 98 self.assertEqual(len(scripts), 1) 99 result = subprocess.run(["env", "-u", "NODE_OPTIONS", "node", "--check"], input=scripts[0], text=True, capture_output=True) 100 self.assertEqual(result.returncode, 0, result.stderr) 101 commands = re.findall(r'<pre><code class="language-bash"[^>]*>(.*?)</code></pre>', self.source, re.S) 102 self.assertGreaterEqual(len(commands), 14) 103 for command in commands: 104 result = subprocess.run(["bash", "-n"], input=html.unescape(re.sub('<[^>]+>', '', command)), text=True, capture_output=True) 105 self.assertEqual(result.returncode, 0, result.stderr) 106 107 def test_svg_xml_and_two_schemes_per_audio_method(self): 108 diagrams = [svg for svg in re.findall(r"<svg\b.*?</svg>", self.source, re.S) if 'role="img"' in svg] 109 self.assertGreaterEqual(len(diagrams), 20) 110 for svg in diagrams: 111 ET.fromstring(svg) 112 for chapter in ["demo", "stt-http", "tts-http", "tts-read-aloud", "tts-realtime", "tts-appserver", "tts-local", "voice-console"]: 113 match = re.search(r'<section id="' + chapter + r'".*?</section>', self.source, re.S) 114 self.assertGreaterEqual(match[0].count('<figure'), 2, chapter) 115 116 def test_entrypoint_page_navigation_screenshots_and_code_map(self): 117 page = BOOK 118 source = self.source 119 parsed = Structure() 120 parsed.feed(source) 121 self.assertFalse(parsed.stack) 122 self.assertEqual(parsed.h1, 1) 123 self.assertTrue(all(parsed.rects)) 124 parsed_files={page.resolve():parsed} 125 for url in parsed.links: 126 parts = urlparse(url) 127 if parts.scheme == "https": 128 continue 129 self.assertFalse(parts.scheme, url) 130 target = page if not parts.path else (page.parent / unquote(parts.path)).resolve() 131 self.assertTrue(target.is_file(), url) 132 if target.suffix == ".html" and parts.fragment: 133 if target not in parsed_files: 134 target_parsed = Structure() 135 target_parsed.feed(target.read_text()) 136 parsed_files[target]=target_parsed 137 target_parsed=parsed_files[target] 138 self.assertIn(unquote(parts.fragment), target_parsed.ids, url) 139 self.assertIn('href="audio-ui-entrypoints.html#mic"', self.source) 140 self.assertIn('href="#ui-entrypoints"', source) 141 for svg in re.findall(r"<svg\b.*?</svg>", source, re.S): 142 ET.fromstring(svg) 143 for _, block in re.findall(r'<div class="example" data-demo="([^"]+)">.*?<pre><code[^>]*>(.*?)</code></pre>', source, re.S): 144 check = subprocess.run(["bash", "-n"], input=html.unescape(re.sub('<[^>]+>', '', block)), text=True, capture_output=True) 145 self.assertEqual(check.returncode, 0, check.stderr) 146 147 def test_screenshot_provenance_and_embedded_pixels(self): 148 folder = REPO / "docs/audio-ui-assets" 149 manifest = json.loads((folder / "screenshots.json").read_text()) 150 page = self.source 151 embedded = {base64.b64decode(value, validate=True) for value in re.findall(r'src="data:image/png;base64,([^"]+)"', page)} 152 self.assertEqual(len(embedded), 3) 153 for item in manifest["images"]: 154 data = (folder / item["file"]).read_bytes() 155 self.assertEqual(hashlib.sha256(data).hexdigest(), item["sha256"]) 156 self.assertIn(data, embedded) 157 self.assertEqual(int.from_bytes(data[16:20], "big"), item["width"]) 158 self.assertEqual(int.from_bytes(data[20:24], "big"), item["height"]) 159 self.assertIn("not proof of Community fork", manifest["scope"]) 160 evidence = json.loads((folder / "bundle-code-map.json").read_text()) 161 self.assertEqual(len(evidence["entries"]), 11) 162 self.assertTrue(all(e["offset"] >= 0 for e in evidence["entries"])) 163 164 165 class ContractTests(unittest.TestCase): 166 def run_bash_python_payload(self, filename, data): 167 # Выполняем именно вложенный Python из Bash. Сетевой opener заменён 168 # перехватчиком: пакет до сервера не отправляется. 169 script = (REPO / "scripts/voice-probes/chatgpt-transcribe.sh").read_text().split("<<'PY'\n", 1)[1].rsplit("\nPY", 1)[0] 170 seen = [] 171 class Response(io.BytesIO): 172 def __enter__(self): return self 173 def __exit__(self, *args): self.close() 174 class Opener: 175 def open(self, request, timeout): 176 seen.append(request) 177 return Response(b'{"text":"synthetic transcript"}') 178 with tempfile.TemporaryDirectory() as folder: 179 root = Path(folder) 180 path = root / filename 181 path.write_bytes(data) 182 auth = root / "auth.json" 183 auth.write_text(json.dumps({"auth_mode": "chatgpt", "tokens": {"access_token": "synthetic-token", "account_id": "own"}})) 184 output = io.StringIO() 185 with patch("sys.argv", ["-", str(path), str(auth)]), patch.object(urllib.request, "build_opener", return_value=Opener()), contextlib.redirect_stdout(output): 186 exec(compile(script, "chatgpt-transcribe.sh", "exec"), {"__name__": "__main__"}) 187 self.assertEqual(output.getvalue().strip(), "synthetic transcript") 188 request = seen[0] 189 mime = request.get_header("Content-type") 190 envelope = BytesParser(policy=email.policy.default).parsebytes(("Content-Type: " + mime + "\r\n\r\n").encode() + request.data) 191 return request, list(envelope.iter_parts()) 192 193 def test_bash_multipart_contains_exact_flac_no_metadata_or_base64(self): 194 data = b"fLaC" + bytes(range(256)) 195 request, parts = self.run_bash_python_payload("probe.flac", data) 196 self.assertEqual(request.get_method(), "POST") 197 self.assertEqual(request.full_url, "https://chatgpt.com/backend-api/transcribe") 198 self.assertEqual(len(parts), 1) 199 self.assertEqual(parts[0].get_param("name", header="content-disposition"), "file") 200 self.assertEqual(parts[0].get_content_type(), "audio/flac") 201 self.assertEqual(parts[0].get_payload(decode=True), data) 202 self.assertNotIn("synthetic-token", request.full_url) 203 204 def test_mime_mismatch_rejected_before_send(self): 205 # Локальная сигнатура известного контейнера; это не проверка сервера 206 # и не полное декодирование повреждённых фреймов внутри контейнера. 207 for bytes_ in [b"ID3-not-flac", b"RIFF-not-flac"]: 208 with contextlib.redirect_stderr(io.StringIO()), self.assertRaises(SystemExit): 209 self.run_bash_python_payload("wrong.flac", bytes_) 210 211 def test_raw_pcm_rate_is_external_metadata_not_self_describing(self): 212 pcm = b"\x01\x00" * 24000 213 self.assertEqual(len(pcm) / (24000 * 1 * 2), 1) 214 self.assertEqual(len(pcm) / (48000 * 1 * 2), .5) 215 self.assertEqual(base64.b64decode(base64.b64encode(pcm), validate=True), pcm) 216 with self.assertRaises(ValueError): 217 base64.b64decode("!invalid", validate=True) 218 219 220 if __name__ == "__main__": 221 unittest.main()