import json import unittest from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] CRM_PACK = REPO_ROOT / "salesforce-crm-standard" SCOPE_PATH = CRM_PACK / "oauth_scopes" / "external_records_read.json" def load_json(path: Path): with path.open(encoding="utf-8") as handle: return json.load(handle) class OAuthCustomScopeContractsTest(unittest.TestCase): def test_external_records_read_manifest_component_is_locked(self): manifest = load_json(CRM_PACK / "pack.json") components = { (component["kind"], component["businessKey"]): component for component in manifest["components"] } component = components[("oauth_custom_scope", "external.records.read")] self.assertEqual(component["manageability"], "locked") self.assertEqual(component["path"], "oauth_scopes/external_records_read.json") self.assertNotIn("payload", component) def test_external_records_read_policy_is_public_read_only(self): scope = load_json(SCOPE_PATH) self.assertEqual(scope["id"], "oauthscope_external_records_read") self.assertEqual(scope["name"], "external.records.read") self.assertEqual(scope["allowed_client_types"], ["public"]) # The supported model is administrator-provisioned public clients with # exact registered callbacks. Anonymous DCR and its dynamic-public # scope opt-in are intentionally absent; PKCE does not change that. self.assertNotIn("allow_dynamic_public_clients", scope) self.assertEqual(scope["allowed_methods"], ["GET", "HEAD"]) # The route-family prefix is the control plane's fail-closed input: it # validates these route_patterns via oauthroutes.NewFamily(prefix) and # rejects the seed if patterns exist without it. Guard against accidental # removal locally. self.assertEqual(scope["route_family_prefix"], "/services/data/") self.assertEqual( scope["route_patterns"], [ "/services/data", "/services/data/:version", "/services/data/:version/", "/services/data/:version/sobjects", "/services/data/:version/sobjects/:sobject/describe", "/services/data/:version/sobjects/:sobject/:id", "/services/data/:version/query", "/services/data/:version/queryAll", "/services/data/:version/query/:locator", "/services/data/:version/id/:orgId/:userId", "/id/:orgId/:userId", ], ) self.assertEqual(scope["resource_links"], ["sobjects", "query", "queryAll", "identity"]) self.assertTrue(scope["active"]) def test_external_records_read_policy_stays_narrow(self): scope = load_json(SCOPE_PATH) self.assertNotIn("*", scope["route_patterns"]) self.assertNotIn("/services/data/*", scope["route_patterns"]) self.assertNotIn("POST", scope["allowed_methods"]) self.assertNotIn("PATCH", scope["allowed_methods"]) self.assertNotIn("DELETE", scope["allowed_methods"]) for forbidden_link in ("connect", "tooling", "analytics", "composite", "metadata", "ui-api"): self.assertNotIn(forbidden_link, scope["resource_links"]) if __name__ == "__main__": unittest.main()