mirror of
https://github.com/block/buzz.git
synced 2026-10-31 22:41:12 +00:00
Co-authored-by: am <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@buzz.block.builderlab.xyz> Signed-off-by: am <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@buzz.block.builderlab.xyz>
272 lines
7.8 KiB
JavaScript
272 lines
7.8 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
selectionOnModelDropdownChange,
|
|
selectionOnProviderDropdownChange,
|
|
selectionOnRuntimeChange,
|
|
} from "./runtimeModelProviderSelection.ts";
|
|
|
|
const base = {
|
|
provider: "",
|
|
model: "",
|
|
isCustomProviderEditing: false,
|
|
isCustomModelEditing: false,
|
|
envVars: {},
|
|
};
|
|
|
|
// --- selectionOnRuntimeChange ---
|
|
|
|
test("runtime change to a provider-locked runtime, full reset (Persona/Edit): clears provider and custom flags but preserves env", () => {
|
|
const next = selectionOnRuntimeChange(
|
|
{
|
|
...base,
|
|
provider: "anthropic",
|
|
model: "claude-4",
|
|
isCustomProviderEditing: true,
|
|
isCustomModelEditing: true,
|
|
envVars: { ANTHROPIC_API_KEY: "sk-1", KEEP: "x" },
|
|
},
|
|
{
|
|
previousRuntime: "buzz-agent",
|
|
nextRuntime: "claude",
|
|
nextRuntimeCanChooseProvider: false,
|
|
lockedRuntimeReset: "full",
|
|
},
|
|
);
|
|
assert.equal(next.provider, "");
|
|
assert.equal(next.isCustomProviderEditing, false);
|
|
assert.equal(next.isCustomModelEditing, false);
|
|
assert.deepEqual(next.envVars, { ANTHROPIC_API_KEY: "sk-1", KEEP: "x" });
|
|
});
|
|
|
|
test("runtime change to a provider-locked runtime, provider-only reset (Create): keeps env vars and custom-model flag", () => {
|
|
const next = selectionOnRuntimeChange(
|
|
{
|
|
...base,
|
|
provider: "anthropic",
|
|
envVars: { ANTHROPIC_API_KEY: "sk-1" },
|
|
isCustomModelEditing: true,
|
|
model: "my-custom",
|
|
},
|
|
{
|
|
previousRuntime: "buzz-agent",
|
|
nextRuntime: "claude",
|
|
nextRuntimeCanChooseProvider: false,
|
|
lockedRuntimeReset: "provider-only",
|
|
},
|
|
);
|
|
assert.equal(next.provider, "");
|
|
assert.equal(next.isCustomProviderEditing, false);
|
|
assert.deepEqual(next.envVars, { ANTHROPIC_API_KEY: "sk-1" });
|
|
});
|
|
|
|
test("runtime change between provider-selection runtimes keeps provider state", () => {
|
|
const current = {
|
|
...base,
|
|
provider: "anthropic",
|
|
envVars: { ANTHROPIC_API_KEY: "sk-1" },
|
|
};
|
|
const next = selectionOnRuntimeChange(current, {
|
|
previousRuntime: "goose",
|
|
nextRuntime: "buzz-agent",
|
|
nextRuntimeCanChooseProvider: true,
|
|
lockedRuntimeReset: "full",
|
|
});
|
|
assert.equal(next.provider, "anthropic");
|
|
assert.deepEqual(next.envVars, { ANTHROPIC_API_KEY: "sk-1" });
|
|
});
|
|
|
|
// --- selectionOnProviderDropdownChange ---
|
|
|
|
test("provider switch preserves shared env and sets the provider", () => {
|
|
const next = selectionOnProviderDropdownChange(
|
|
{
|
|
...base,
|
|
provider: "anthropic",
|
|
envVars: { ANTHROPIC_API_KEY: "sk-1", KEEP: "x" },
|
|
},
|
|
{
|
|
runtime: "buzz-agent",
|
|
nextValue: "openai",
|
|
clearModelWhenApiKeyMissing: false,
|
|
},
|
|
);
|
|
assert.equal(next.provider, "openai");
|
|
assert.equal(next.isCustomProviderEditing, false);
|
|
assert.deepEqual(next.envVars, { ANTHROPIC_API_KEY: "sk-1", KEEP: "x" });
|
|
});
|
|
|
|
test("provider switch between OpenAI origins preserves both credential identities", () => {
|
|
const official = selectionOnProviderDropdownChange(
|
|
{
|
|
...base,
|
|
provider: "openai-compat",
|
|
envVars: {
|
|
OPENAI_COMPAT_API_KEY: "compat-key",
|
|
OPENAI_COMPAT_BASE_URL: "https://gateway.example/v1",
|
|
KEEP: "x",
|
|
},
|
|
},
|
|
{
|
|
runtime: "buzz-agent",
|
|
nextValue: "openai",
|
|
clearModelWhenApiKeyMissing: false,
|
|
},
|
|
);
|
|
assert.deepEqual(official.envVars, {
|
|
OPENAI_COMPAT_API_KEY: "compat-key",
|
|
OPENAI_COMPAT_BASE_URL: "https://gateway.example/v1",
|
|
KEEP: "x",
|
|
});
|
|
|
|
const compatible = selectionOnProviderDropdownChange(
|
|
{
|
|
...base,
|
|
provider: "openai",
|
|
envVars: { OPENAI_API_KEY: "official-key", KEEP: "x" },
|
|
},
|
|
{
|
|
runtime: "buzz-agent",
|
|
nextValue: "openai-compat",
|
|
clearModelWhenApiKeyMissing: false,
|
|
},
|
|
);
|
|
assert.deepEqual(compatible.envVars, {
|
|
OPENAI_API_KEY: "official-key",
|
|
KEEP: "x",
|
|
});
|
|
});
|
|
|
|
test("custom-provider entry preserves env and enters custom editing", () => {
|
|
const next = selectionOnProviderDropdownChange(
|
|
{
|
|
...base,
|
|
provider: "anthropic",
|
|
envVars: { ANTHROPIC_API_KEY: "sk-1" },
|
|
},
|
|
{
|
|
runtime: "buzz-agent",
|
|
nextValue: "__custom_provider__",
|
|
clearModelWhenApiKeyMissing: false,
|
|
},
|
|
);
|
|
assert.equal(next.isCustomProviderEditing, true);
|
|
assert.equal(next.provider, "");
|
|
assert.deepEqual(next.envVars, { ANTHROPIC_API_KEY: "sk-1" });
|
|
});
|
|
|
|
test("auto-provider selection maps to empty provider", () => {
|
|
const next = selectionOnProviderDropdownChange(
|
|
{ ...base, provider: "anthropic", envVars: { ANTHROPIC_API_KEY: "sk-1" } },
|
|
{
|
|
runtime: "buzz-agent",
|
|
nextValue: "__auto_provider__",
|
|
clearModelWhenApiKeyMissing: false,
|
|
},
|
|
);
|
|
assert.equal(next.provider, "");
|
|
assert.deepEqual(next.envVars, { ANTHROPIC_API_KEY: "sk-1" });
|
|
});
|
|
|
|
test("Persona mode clears the model when the new provider's API key is missing", () => {
|
|
const next = selectionOnProviderDropdownChange(
|
|
{ ...base, model: "claude-4", provider: "" },
|
|
{
|
|
runtime: "buzz-agent",
|
|
nextValue: "anthropic",
|
|
clearModelWhenApiKeyMissing: true,
|
|
},
|
|
);
|
|
assert.equal(next.model, "");
|
|
});
|
|
|
|
test("Create/Edit mode keeps the model when the new provider's API key is missing", () => {
|
|
// claude-4 is scope-agnostic here: shouldClearKnownModelForSelectionScope
|
|
// only clears known models for the selection scope, and a custom string
|
|
// stays put — mirroring the dialogs' behavior without the persona flag.
|
|
const next = selectionOnProviderDropdownChange(
|
|
{ ...base, model: "my-custom-model", provider: "" },
|
|
{
|
|
runtime: "buzz-agent",
|
|
nextValue: "anthropic",
|
|
clearModelWhenApiKeyMissing: false,
|
|
},
|
|
);
|
|
assert.equal(next.model, "my-custom-model");
|
|
});
|
|
|
|
test("custom-model editing suppresses the model-scope clear on provider switch", () => {
|
|
const next = selectionOnProviderDropdownChange(
|
|
{ ...base, model: "anything", isCustomModelEditing: true },
|
|
{
|
|
runtime: "buzz-agent",
|
|
nextValue: "openai",
|
|
clearModelWhenApiKeyMissing: false,
|
|
},
|
|
);
|
|
assert.equal(next.model, "anything");
|
|
assert.equal(next.isCustomModelEditing, true);
|
|
});
|
|
|
|
// --- selectionOnModelDropdownChange ---
|
|
|
|
test("custom-model entry with clear (Persona) drops a known model", () => {
|
|
const next = selectionOnModelDropdownChange(
|
|
{ ...base, model: "known-model" },
|
|
{
|
|
nextValue: "__custom_model__",
|
|
clearKnownModelOnCustomEntry: true,
|
|
isModelCustom: false,
|
|
},
|
|
);
|
|
assert.equal(next.isCustomModelEditing, true);
|
|
assert.equal(next.model, "");
|
|
});
|
|
|
|
test("custom-model entry keeps an already-custom model (Persona) and any model (Edit)", () => {
|
|
const personaCustom = selectionOnModelDropdownChange(
|
|
{ ...base, model: "already-custom" },
|
|
{
|
|
nextValue: "__custom_model__",
|
|
clearKnownModelOnCustomEntry: true,
|
|
isModelCustom: true,
|
|
},
|
|
);
|
|
assert.equal(personaCustom.model, "already-custom");
|
|
|
|
const edit = selectionOnModelDropdownChange(
|
|
{ ...base, model: "known-model" },
|
|
{
|
|
nextValue: "__custom_model__",
|
|
clearKnownModelOnCustomEntry: false,
|
|
isModelCustom: false,
|
|
},
|
|
);
|
|
assert.equal(edit.model, "known-model");
|
|
assert.equal(edit.isCustomModelEditing, true);
|
|
});
|
|
|
|
test("auto-model selection clears the model; concrete selection sets it", () => {
|
|
const auto = selectionOnModelDropdownChange(
|
|
{ ...base, model: "old", isCustomModelEditing: true },
|
|
{
|
|
nextValue: "__auto_model__",
|
|
clearKnownModelOnCustomEntry: false,
|
|
isModelCustom: false,
|
|
},
|
|
);
|
|
assert.equal(auto.model, "");
|
|
assert.equal(auto.isCustomModelEditing, false);
|
|
|
|
const concrete = selectionOnModelDropdownChange(
|
|
{ ...base, model: "" },
|
|
{
|
|
nextValue: "gpt-5",
|
|
clearKnownModelOnCustomEntry: false,
|
|
isModelCustom: false,
|
|
},
|
|
);
|
|
assert.equal(concrete.model, "gpt-5");
|
|
});
|