Files
raycast-api/tests/test_extractors.py

75 lines
2.5 KiB
Python

"""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)"