File size: 3,633 Bytes
e249c6d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import { describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import type { Model } from "../../llm/types.js";
import type { AuthProfileStore } from "../auth-profiles/types.js";
import { GCP_VERTEX_CREDENTIALS_MARKER } from "../model-auth-markers.js";
import { prepareAgentRuntimeAuth } from "./prepare-auth.js";
import {
  resolvePreparedRuntimeAuthAttempts,
  resolvePreparedRuntimeModelAuth,
} from "./resolve-auth.js";

const authLookupMocks = vi.hoisted(() => ({
  resolveProviderEnvAuthLookupMaps: vi.fn(() => ({
    aliasMap: {},
    envCandidateMap: {},
    authEvidenceMap: {},
    setupProviderFallbackRefs: ["anthropic-vertex"],
  })),
}));

const setupRegistryMocks = vi.hoisted(() => ({
  resolvePluginSetupProviderCore: vi.fn(() => ({
    resolveConfigApiKey: () => "gcp-vertex-credentials",
  })),
}));

vi.mock("../model-auth-env-vars.js", async (importOriginal) => ({
  ...(await importOriginal<typeof import("../model-auth-env-vars.js")>()),
  resolveProviderEnvAuthLookupMaps: authLookupMocks.resolveProviderEnvAuthLookupMaps,
}));

vi.mock("../../plugins/setup-registry.js", async (importOriginal) => ({
  ...(await importOriginal<typeof import("../../plugins/setup-registry.js")>()),
  resolvePluginSetupProviderCore: setupRegistryMocks.resolvePluginSetupProviderCore,
}));

describe("prepared setup-provider auth fallback", () => {
  it("defers setup resolution until a prepared profile attempt fails", async () => {
    const profileId = "anthropic-vertex:missing";
    const config = {
      auth: { order: { "anthropic-vertex": [profileId] } },
    } as OpenClawConfig;
    const store = {
      version: 1,
      profiles: {
        [profileId]: {
          type: "oauth",
          provider: "anthropic-vertex",
          access: "test-token",
          refresh: "test-refresh-token",
          expires: 0,
        },
      },
      order: { "anthropic-vertex": [profileId] },
    } satisfies AuthProfileStore;
    const prepared = prepareAgentRuntimeAuth({
      provider: "anthropic-vertex",
      modelId: "claude-sonnet-4-6",
      config,
      env: {},
      authProfileStore: store,
    });

    expect(setupRegistryMocks.resolvePluginSetupProviderCore).not.toHaveBeenCalled();
    expect(prepared.attempts).toMatchObject([
      { kind: "profile", profileId },
      {
        kind: "direct",
        allowAuthProfileFallback: false,
        requiresPriorProfileAttempt: true,
      },
    ]);

    const model = {
      id: "claude-sonnet-4-6",
      name: "Claude Sonnet 4.6",
      provider: "anthropic-vertex",
      api: "anthropic-messages",
      baseUrl: "https://example.invalid",
      reasoning: true,
      input: ["text", "image"],
      cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
      contextWindow: 200_000,
      maxTokens: 64_000,
    } as Model;
    const resolved = await resolvePreparedRuntimeAuthAttempts({
      attempts: prepared.attempts,
      store,
      modelId: model.id,
      model,
      materializeModel: async ({ model: preparedModel }) => preparedModel,
      resolveAuth: async ({ attempt, model: preparedModel }) =>
        resolvePreparedRuntimeModelAuth({
          plan: attempt.plan,
          model: preparedModel,
          cfg: config,
          store,
        }),
      errorMessage: "prepared Anthropic Vertex auth failed",
    });

    expect(resolved.auth).toMatchObject({
      apiKey: GCP_VERTEX_CREDENTIALS_MARKER,
      source: "gcloud adc",
      mode: "api-key",
    });
    expect(setupRegistryMocks.resolvePluginSetupProviderCore).toHaveBeenCalledOnce();
  });
});