"""Tests for `raycast_api.discovery.ast_parse`.""" from __future__ import annotations from raycast_api.discovery.ast_parse import ( collect_numeric_literals, find_calls, find_function_by_shape, has_string_literal, iter_function_declarations, ) def test_finds_declarations_and_skips_decoys(mock_bundle_source: str) -> None: fns = list(iter_function_declarations(mock_bundle_source)) names = [f.name for f in fns] assert { "RotXform", "RotXform2", "SignReq", "decoyOneParam", "decoyHmac", "noiseFn", } <= set(names) def test_param_count_filter(mock_bundle_source: str) -> None: fns = list(iter_function_declarations(mock_bundle_source)) one_param = {f.name for f in find_function_by_shape(fns, param_count=1)} assert "RotXform" in one_param assert "decoyOneParam" in one_param four_param = { f.name for f in find_function_by_shape(fns, is_async=True, param_count=4) } assert "SignReq" in four_param assert "decoyHmac" in four_param def test_body_contains_excludes_decoys(mock_bundle_source: str) -> None: fns = list(iter_function_declarations(mock_bundle_source)) signing_like = find_function_by_shape( fns, is_async=True, param_count=4, body_contains_all=["HMAC", "SHA-256", "importKey"], ) assert {f.name for f in signing_like} == {"SignReq", "decoyHmac"} def test_find_calls_locates_map_calls(mock_bundle_source: str) -> None: fns = list(iter_function_declarations(mock_bundle_source)) sign = next(f for f in fns if f.name == "SignReq") map_calls = find_calls(sign, "map") assert len(map_calls) == 3 def test_has_string_literal(mock_bundle_source: str) -> None: fns = list(iter_function_declarations(mock_bundle_source)) sign = next(f for f in fns if f.name == "SignReq") assert has_string_literal(sign, "HMAC") assert has_string_literal(sign, "SHA-256") assert not has_string_literal(sign, "MD5") def test_collect_numeric_literals(mock_bundle_source: str) -> None: fns = list(iter_function_declarations(mock_bundle_source)) rot = next(f for f in fns if f.name == "RotXform") nums = collect_numeric_literals(rot) assert {65, 90, 13, 26, 97, 122, 48, 57, 5, 10} <= nums def test_regex_literal_with_brace_does_not_truncate_function() -> None: """The brace matcher must skip braces inside regex literals.""" src = """ function tricky(s) { let re = /\\}/; if (s.match(re)) { return true } return false; } """ fns = list(iter_function_declarations(src)) assert len(fns) == 1 assert fns[0].name == "tricky" assert "return true" in fns[0].body_source assert fns[0].body_source.endswith("}") def test_template_literal_with_interpolation() -> None: src = r""" function templating(x) { let nested = `prefix-${x.toString()}-${`inner-${x}`}-end`; if (x) { return nested } return ""; } """ fns = list(iter_function_declarations(src)) assert len(fns) == 1 assert fns[0].name == "templating" assert "return nested" in fns[0].body_source def test_strings_with_braces_dont_confuse_matcher() -> None: src = """ function strs() { let a = "{ not real }"; let b = '} also not };'; return a + b; } """ fns = list(iter_function_declarations(src)) assert len(fns) == 1 assert fns[0].body_source.endswith("}")