test: cover read-only models list

This commit is contained in:
Shakker
2026-04-24 00:30:53 +01:00
committed by Shakker
parent c7af4dcb31
commit dcc180f14f
2 changed files with 52 additions and 13 deletions

View File

@@ -467,26 +467,62 @@ describe("models list/status", () => {
expect(Array.from(loaded.availableKeys ?? [])).toEqual(["openai/gpt-4.1-mini"]);
});
it("modelsListCommand persists using the source snapshot config when provided", async () => {
modelRegistryState.models = [OPENAI_MODEL];
modelRegistryState.available = [OPENAI_MODEL];
it("modelsListCommand lists source snapshot provider models without persisting models.json", async () => {
modelRegistryState.models = [];
modelRegistryState.available = [];
const sourceConfig = {
models: { providers: { openai: { apiKey: "$OPENAI_API_KEY" } } }, // pragma: allowlist secret
models: {
providers: {
"custom-proxy": {
baseUrl: "https://custom.example/v1",
models: [
{
id: "custom-model",
name: "Custom Model",
input: ["text"],
contextWindow: 128000,
},
],
},
},
},
};
const resolvedConfig = {
models: { providers: { openai: { apiKey: "sk-resolved-runtime-value" } } }, // pragma: allowlist secret
models: {
providers: {
"custom-proxy": {
baseUrl: "https://custom.example/v1",
apiKey: "sk-resolved-runtime-value", // pragma: allowlist secret
models: [
{
id: "custom-model",
name: "Custom Model",
input: ["text"],
contextWindow: 128000,
},
],
},
},
},
};
readConfigFileSnapshotForWrite.mockResolvedValue({
snapshot: { valid: true, resolved: resolvedConfig, sourceConfig },
writeOptions: {},
});
setDefaultModel("openai/gpt-4.1-mini");
getRuntimeConfig.mockReturnValue(resolvedConfig);
const runtime = makeRuntime();
await modelsListCommand({ all: true, json: true }, runtime);
await modelsListCommand({ all: true, provider: "custom-proxy", json: true }, runtime);
expect(ensureOpenClawModelsJson).toHaveBeenCalled();
expect(ensureOpenClawModelsJson.mock.calls[0]?.[0]).toEqual(sourceConfig);
expect(ensureOpenClawModelsJson).not.toHaveBeenCalled();
const payload = parseJsonLog(runtime);
expect(payload.models).toEqual([
expect.objectContaining({
key: "custom-proxy/custom-model",
name: "Custom Model",
missing: false,
}),
]);
});
it("toModelRow does not crash without cfg/authStore when availability is undefined", async () => {

View File

@@ -295,14 +295,17 @@ describe("modelsListCommand forward-compat", () => {
expect(codexPro?.tags).not.toContain("missing");
});
it("passes source config to model registry loading for persistence safety", async () => {
it("loads model registry without source config persistence input", async () => {
const runtime = createRuntime();
await modelsListCommand({ json: true }, runtime as never);
expect(mocks.loadModelRegistry).toHaveBeenCalledWith(mocks.resolvedConfig, {
sourceConfig: mocks.sourceConfig,
});
expect(mocks.loadModelRegistry).toHaveBeenCalledWith(
mocks.resolvedConfig,
expect.not.objectContaining({
sourceConfig: expect.anything(),
}),
);
});
it("keeps configured local openai gpt-5.4 entries visible in --local output", async () => {