import { AuthOrchestrationOperateScope, AuthOrchestrationReadScope, AuthRelayReadScope, AuthRelayWriteScope, WS_METHODS, WsRpcGroup, } from "@t3tools/contracts"; import { describe, expect, it } from "@effect/vitest"; import { RPC_REQUIRED_SCOPES, requiredScopeForRpcMethod } from "./RpcAuthorization.ts"; describe("RPC authorization scopes", () => { it("declares exactly one scope for every RPC in the server group", () => { expect(new Set(Object.keys(RPC_REQUIRED_SCOPES))).toEqual(new Set(WsRpcGroup.requests.keys())); }); it("authorizes background policy reporting and observation deliberately", () => { expect(requiredScopeForRpcMethod(WS_METHODS.serverReportClientActivity)).toBe( AuthOrchestrationReadScope, ); expect(requiredScopeForRpcMethod(WS_METHODS.serverReportHostPowerState)).toBe( AuthOrchestrationOperateScope, ); expect(requiredScopeForRpcMethod(WS_METHODS.serverGetBackgroundPolicy)).toBe( AuthOrchestrationReadScope, ); expect(requiredScopeForRpcMethod(WS_METHODS.subscribeBackgroundPolicy)).toBe( AuthOrchestrationReadScope, ); }); it("allows relay status reads without granting relay installation access", () => { expect(requiredScopeForRpcMethod(WS_METHODS.cloudGetRelayClientStatus)).toBe( AuthRelayReadScope, ); expect(requiredScopeForRpcMethod(WS_METHODS.cloudInstallRelayClient)).toBe(AuthRelayWriteScope); }); it("requires permission to operate on a thread before uploading feedback", () => { expect(requiredScopeForRpcMethod(WS_METHODS.providerUploadFeedback)).toBe( AuthOrchestrationOperateScope, ); }); it("requires write access to import agent session history", () => { expect(requiredScopeForRpcMethod(WS_METHODS.agentSessionsScan)).toBe( AuthOrchestrationReadScope, ); expect(requiredScopeForRpcMethod(WS_METHODS.agentSessionsImport)).toBe( AuthOrchestrationOperateScope, ); }); it("reads the reviewer menu under the same scope as the pull request it belongs to", () => { // The candidate list is a read like the detail beside it, and asking somebody for a review is // a write like every other pull request operation. expect(requiredScopeForRpcMethod(WS_METHODS.pullRequestsReviewerCandidates)).toBe( requiredScopeForRpcMethod(WS_METHODS.pullRequestsDetail), ); expect(requiredScopeForRpcMethod(WS_METHODS.pullRequestsRequestReviewers)).toBe( requiredScopeForRpcMethod(WS_METHODS.pullRequestsComment), ); }); it("rejects unknown RPC method names", () => { for (const method of ["server.notRegistered", "toString", "constructor"]) { expect(() => requiredScopeForRpcMethod(method)).toThrow( `RPC method ${method} has no declared authorization scope.`, ); } }); });