"""Tests for `raycast_api.discovery.extractors`.""" from __future__ import annotations from pathlib import Path import pytest from raycast_api.discovery.extractors import ( extract_signing_spec, extract_user_agent_template, read_app_version, ) from raycast_api.errors import DiscoveryError def test_extract_signing_spec_from_synthetic(mock_bundle_source: str) -> None: spec = extract_signing_spec(mock_bundle_source) assert spec.rot_fn_name in {"RotXform", "RotXform2"} assert spec.signing_fn_name == "SignReq" assert spec.join_char == "." assert spec.body_hash_algorithm == "SHA-256" assert spec.hmac_algorithm == "SHA-256" assert spec.key_encoding == "utf-8" def test_rot_ranges_extracted(mock_bundle_source: str) -> None: spec = extract_signing_spec(mock_bundle_source) ranges = {(r.start, r.end, r.shift) for r in spec.rot_ranges} assert ranges == {(65, 90, 13), (97, 122, 13), (48, 57, 5)} def test_missing_rot_raises() -> None: src = """ async function loneSigner(a, b, c, d) { let k = await crypto.subtle.importKey("raw", new TextEncoder().encode(a), {name:"HMAC", hash:"SHA-256"}, false, ["sign"]); return crypto.subtle.sign("HMAC", k, b); } """ with pytest.raises(DiscoveryError, match="rot13"): extract_signing_spec(src) def test_signer_must_reference_rot() -> None: """If the only signing candidate doesn't .map() through the rot fn, fail loudly.""" src = """ function rotFn(s) { let out = ""; for (let ch of s) { let r = ch.charCodeAt(0); if (r >= 65 && r <= 90) out += String.fromCharCode((r - 65 + 13) % 26 + 65); else if (r >= 97 && r <= 122) out += String.fromCharCode((r - 97 + 13) % 26 + 97); else if (r >= 48 && r <= 57) out += String.fromCharCode((r - 48 + 5) % 10 + 48); else out += ch; } return out; } async function lone(a, b, c, d) { // 4 params, HMAC/SHA-256/importKey — but doesn't call .map(rotFn). let k = await crypto.subtle.importKey("raw", new TextEncoder().encode(a), {name:"HMAC", hash:"SHA-256"}, false, ["sign"]); return crypto.subtle.sign("HMAC", k, b); } """ with pytest.raises(DiscoveryError, match=r"none of the signers calls\s+\.map"): extract_signing_spec(src) def test_read_app_version(mock_app: Path) -> None: assert read_app_version(mock_app) == "9.9.9.0" def test_user_agent_template(mock_app: Path) -> None: ua = extract_user_agent_template(mock_app, platform_version="13.5") assert ua == "Raycast/9.9.9.0 (x-macOS Version 13.5)"