@@ -74,4 +74,34 @@ describe("prepared provider auth state", () => {
|
74 | 74 | expect(hasAuthForModelProvider({ provider: "anthropic", cfg })).toBe(true); |
75 | 75 | expect(modelAuthMocks.hasRuntimeAvailableProviderAuth).toHaveBeenCalledTimes(3); |
76 | 76 | }); |
| 77 | + |
| 78 | +it("hasAuthForModelProvider falls through to compute when the caller narrows the auth-discovery scope", async () => { |
| 79 | +const cfg = {} as OpenClawConfig; |
| 80 | +modelCatalogMocks.loadModelCatalog.mockResolvedValue([ |
| 81 | +{ id: "gpt", name: "gpt", provider: "openai" }, |
| 82 | +]); |
| 83 | +// Warm with the broad answer: provider has CLI/synthetic auth. |
| 84 | +modelAuthMocks.hasRuntimeAvailableProviderAuth.mockReturnValue(true); |
| 85 | +await warmCurrentProviderAuthState(cfg); |
| 86 | +expect(modelAuthMocks.hasRuntimeAvailableProviderAuth).toHaveBeenCalledTimes(1); |
| 87 | + |
| 88 | +// Flip the underlying compute to false. A narrow-scope caller must NOT |
| 89 | +// pick up the warmed broad answer — gateway models.list with |
| 90 | +// runtimeAuthDiscovery: false maps to both flags false, and the answer |
| 91 | +// must reflect that narrower scope, not the prepared broad answer. |
| 92 | +modelAuthMocks.hasRuntimeAvailableProviderAuth.mockReturnValue(false); |
| 93 | +expect( |
| 94 | +hasAuthForModelProvider({ |
| 95 | +provider: "openai", |
| 96 | + cfg, |
| 97 | +discoverExternalCliAuth: false, |
| 98 | +allowPluginSyntheticAuth: false, |
| 99 | +}), |
| 100 | +).toBe(false); |
| 101 | +expect(modelAuthMocks.hasRuntimeAvailableProviderAuth).toHaveBeenCalledTimes(2); |
| 102 | + |
| 103 | +// Broad-scope caller (default flags) still hits the prepared map. |
| 104 | +expect(hasAuthForModelProvider({ provider: "openai", cfg })).toBe(true); |
| 105 | +expect(modelAuthMocks.hasRuntimeAvailableProviderAuth).toHaveBeenCalledTimes(2); |
| 106 | +}); |
77 | 107 | }); |