227 lines
7.5 KiB
TypeScript
227 lines
7.5 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { ENHANCED_TOOL_CONTRACTS } from "./enhancedToolContracts.js";
|
|
import { OFFICIAL_TOOL_CONTRACTS } from "./officialToolContracts.js";
|
|
import { SESSION_TOOL_CONTRACTS } from "./sessionToolContracts.js";
|
|
import { TOOL_CONTRACTS } from "./toolContracts.js";
|
|
import { ARTIFACT_TOOL_CONTRACTS } from "./artifactToolContracts.js";
|
|
import { MANAGED_TOOL_CONTRACTS } from "./managedToolContracts.js";
|
|
import { FIRMWARE_TOOL_CONTRACTS } from "./firmware/firmwareToolContracts.js";
|
|
import { ANDROID_TOOL_CONTRACTS } from "./android/androidToolContracts.js";
|
|
import { MANAGED_WORKFLOW_TOOL_CONTRACTS } from "./managedWorkflowToolContracts.js";
|
|
import { NATIVE_TOOL_CONTRACTS } from "./nativeToolContracts.js";
|
|
import { BROWSER_PROVIDER_TOOL_CONTRACTS } from "./browserProviderToolContracts.js";
|
|
import { ELECTRON_TOOL_CONTRACTS } from "./electronToolContracts.js";
|
|
import { JAVASCRIPT_RUNTIME_OBSERVATION_TOOL_CONTRACTS } from "./javascriptRuntimeObservationToolContracts.js";
|
|
import { APPLICATION_TOOL_CONTRACTS } from "./applicationToolContracts.js";
|
|
import { JAVASCRIPT_RECOVERY_TOOL_CONTRACTS } from "./javascriptRecoveryToolContracts.js";
|
|
import { WEB_SCRIPT_TOOL_CONTRACTS } from "./webScriptToolContracts.js";
|
|
|
|
const GROUPS = {
|
|
official: OFFICIAL_TOOL_CONTRACTS,
|
|
enhanced: ENHANCED_TOOL_CONTRACTS,
|
|
native: NATIVE_TOOL_CONTRACTS,
|
|
artifact: ARTIFACT_TOOL_CONTRACTS,
|
|
managed: MANAGED_TOOL_CONTRACTS,
|
|
android: ANDROID_TOOL_CONTRACTS,
|
|
firmware: FIRMWARE_TOOL_CONTRACTS,
|
|
managed_workflow: MANAGED_WORKFLOW_TOOL_CONTRACTS,
|
|
browser_provider: BROWSER_PROVIDER_TOOL_CONTRACTS,
|
|
electron: ELECTRON_TOOL_CONTRACTS,
|
|
javascript_runtime_observation: JAVASCRIPT_RUNTIME_OBSERVATION_TOOL_CONTRACTS,
|
|
application: APPLICATION_TOOL_CONTRACTS,
|
|
javascript_recovery: JAVASCRIPT_RECOVERY_TOOL_CONTRACTS,
|
|
web_script_export: WEB_SCRIPT_TOOL_CONTRACTS,
|
|
session: SESSION_TOOL_CONTRACTS,
|
|
} as const;
|
|
|
|
/**
|
|
* Published tool names that must remain in the inventory.
|
|
*
|
|
* This is a floor, not an equality. Structural invariants already prove every
|
|
* group member is published, so on their own a contract removed from both its
|
|
* group and the aggregate would regenerate the catalog with no failing test.
|
|
* These names catch that removal. Adding a tool needs no edit here; removing
|
|
* one is a deliberate API change that must delete its name deliberately.
|
|
*/
|
|
const PUBLISHED_TOOL_NAME_FLOOR = [
|
|
"address_name",
|
|
"address_to_file_offset",
|
|
"analyze_function",
|
|
"analyze_javascript_application",
|
|
"analyze_swift_types",
|
|
"analyze_web_bundle",
|
|
"batch_decompile",
|
|
"binary_overview",
|
|
"binary_session",
|
|
"build_call_path",
|
|
"build_reconstruction_obligation_ledger",
|
|
"capture_browser_scenario",
|
|
"capture_electron_scenario",
|
|
"capture_process_scenario",
|
|
"capture_web_screenshot",
|
|
"close_binary",
|
|
"comment",
|
|
"compare_application_versions",
|
|
"compare_artifacts",
|
|
"compare_bundles",
|
|
"compare_functions",
|
|
"compare_javascript_export_shapes",
|
|
"compare_managed_members",
|
|
"compare_process_captures",
|
|
"compare_source_to_bundle",
|
|
"compare_web_captures",
|
|
"compare_web_screenshots",
|
|
"correlate_static_and_runtime",
|
|
"current_address",
|
|
"current_document",
|
|
"current_procedure",
|
|
"decode_interface_builder",
|
|
"demangle_swift",
|
|
"discover_webmcp_tools",
|
|
"evaluate_reconstruction_coverage",
|
|
"export_evidence_bundle",
|
|
"extract_artifact",
|
|
"export_web_scripts",
|
|
"trace_web_module_imports",
|
|
"find_changed_behavior",
|
|
"find_xrefs_to_name",
|
|
"get_call_graph",
|
|
"get_evidence_bundle",
|
|
"get_navigation_context",
|
|
"get_objc_classes",
|
|
"get_objc_protocols",
|
|
"goto_address",
|
|
"import_evidence_bundle",
|
|
"import_managed_reconstruction",
|
|
"inline_comment",
|
|
"inspect_address_context",
|
|
"inspect_artifact",
|
|
"inspect_asset_catalog",
|
|
"inspect_electron_page",
|
|
"inspect_macho",
|
|
"inspect_managed_artifact",
|
|
"inspect_firmware_regions",
|
|
"extract_firmware",
|
|
"inspect_android_package",
|
|
"search_android_classes",
|
|
"inspect_android_class",
|
|
"inspect_android_method",
|
|
"trace_android_references",
|
|
"inspect_managed_members",
|
|
"inspect_managed_native_boundaries",
|
|
"inspect_native_api",
|
|
"inspect_native_dispatch_metadata",
|
|
"inspect_plist",
|
|
"inspect_signature",
|
|
"inspect_web_page",
|
|
"list_architectures",
|
|
"list_bookmarks",
|
|
"list_browser_targets",
|
|
"list_documents",
|
|
"list_electron_targets",
|
|
"list_javascript_runtime_targets",
|
|
"list_names",
|
|
"list_procedures",
|
|
"list_segments",
|
|
"list_strings",
|
|
"list_unknowns",
|
|
"next_address",
|
|
"observe_javascript_runtime",
|
|
"observe_web_session",
|
|
"open_binary",
|
|
"prev_address",
|
|
"procedure_address",
|
|
"procedure_assembly",
|
|
"procedure_callees",
|
|
"procedure_callers",
|
|
"procedure_info",
|
|
"procedure_pseudo_code",
|
|
"procedure_references",
|
|
"project_android_application_graph",
|
|
"project_apple_application_graph",
|
|
"project_managed_application_graph",
|
|
"read_bytes",
|
|
"read_function_instructions",
|
|
"reconcile_javascript_runtime",
|
|
"record_unknown",
|
|
"resolve_containing_procedure",
|
|
"search_procedures",
|
|
"search_strings",
|
|
"set_address_name",
|
|
"set_addresses_names",
|
|
"set_bookmark",
|
|
"set_comment",
|
|
"set_current_document",
|
|
"set_inline_comment",
|
|
"trace_application_feature",
|
|
"trace_call_path",
|
|
"trace_feature",
|
|
"trace_javascript_semantics",
|
|
"trace_native_ui_action",
|
|
"unset_bookmark",
|
|
"update_unknown",
|
|
"verify_managed_native_boundaries",
|
|
"verify_reconstruction",
|
|
"verify_unknown_resolution",
|
|
"xrefs",
|
|
] as const;
|
|
|
|
// These assertions are deliberately structural rather than numeric. Pinning
|
|
// group sizes made every additive catalog change require a test edit, which
|
|
// blocked additive change without catching defects. Removal is still covered:
|
|
// the floor above catches a contract dropped from both its group and the
|
|
// aggregate.
|
|
describe("tool contract inventory", () => {
|
|
it("assigns every published tool a unique name", () => {
|
|
const names = TOOL_CONTRACTS.map(({ name }) => name);
|
|
expect(new Set(names).size).toBe(names.length);
|
|
});
|
|
|
|
it("contains every group member in the published inventory", () => {
|
|
const published = new Set(TOOL_CONTRACTS.map(({ name }) => name));
|
|
for (const [group, contracts] of Object.entries(GROUPS)) {
|
|
for (const { name } of contracts) {
|
|
expect(
|
|
published.has(name),
|
|
`${group} tool ${name} is not in TOOL_CONTRACTS`,
|
|
).toBe(true);
|
|
}
|
|
}
|
|
});
|
|
|
|
it("keeps every published tool name in the declared floor", () => {
|
|
const published = new Set(TOOL_CONTRACTS.map(({ name }) => name));
|
|
const removed = PUBLISHED_TOOL_NAME_FLOOR.filter(
|
|
(name) => !published.has(name),
|
|
);
|
|
expect(
|
|
removed,
|
|
"published tool names were removed; deleting one is a deliberate API change",
|
|
).toEqual([]);
|
|
});
|
|
|
|
it("keeps groups mutually disjoint", () => {
|
|
const entries = Object.entries(GROUPS);
|
|
for (const [leftName, left] of entries) {
|
|
for (const [rightName, right] of entries) {
|
|
if (leftName >= rightName) continue;
|
|
const rightNames = new Set(right.map(({ name }) => name));
|
|
const overlap = left
|
|
.map(({ name }) => name)
|
|
.filter((name) => rightNames.has(name));
|
|
expect(
|
|
overlap,
|
|
`${leftName} and ${rightName} both publish ${overlap.join(", ")}`,
|
|
).toEqual([]);
|
|
}
|
|
}
|
|
});
|
|
|
|
it("publishes a non-empty inventory from every group", () => {
|
|
for (const [group, contracts] of Object.entries(GROUPS)) {
|
|
expect(contracts.length, `${group} is empty`).toBeGreaterThan(0);
|
|
}
|
|
});
|
|
});
|