import { expect, test } from "vite-plus/test"; import { parseConfigFile, type ConfigFile, type Settings } from "../src/config/schema.ts"; import { buildSettings, type ResolutionSources } from "../src/config/loader.ts"; import { resolveApiKey, resolveConsole, resolveModelBaseUrl, resolveModelBaseUrlState, resolveOpenApi, } from "../src/auth/resolver.ts"; import { getModelProfilePreset } from "../src/config/profile-presets.ts"; // 行为锁定:所有配置字段统一保持 flag>env>selected file>默认。`--config` 只选择 // file block,不提升该 block 的字段优先级。Profile 预设只在登录写入阶段使用。 // buildSettings 与 resolver 都是纯函数,sources 直接构造,无需环境隔离。 function src(s: { flags?: ResolutionSources["flags"]; env?: Record; file?: ConfigFile; configName?: string; }): ResolutionSources { return { flags: s.flags ?? {}, file: s.file ?? {}, env: s.env ?? {}, configName: s.configName, }; } const resolve = (s: Parameters[0]): Settings => buildSettings(src(s)); test("token-plan Profile 预设保持固定", () => { expect(getModelProfilePreset("token-plan")).toEqual({ baseUrl: "https://token-plan.cn-beijing.maas.aliyuncs.com", defaultTextModel: "qwen3.8-max", defaultVideoModel: "happyhorse-1.1-t2v", defaultImageToVideoModel: "happyhorse-1.1-i2v", defaultReferenceToVideoModel: "happyhorse-1.1-r2v", defaultImageModel: "wan2.7-image", defaultSpeechModel: "qwen-audio-3.0-tts-plus", defaultSpeechRecognitionModel: "qwen-audio-3.0-asr-flash", apiKeyCapabilities: [ "text.chat", "vision.describe", "image.generate", "image.edit", "speech.recognize", "speech.synthesize", "video.generate", "video.ref", "video.task.get", "video.download", ], }); }); test("api_key_capabilities 解析并去重叶子能力,保留显式空白名单", () => { expect( parseConfigFile({ api_key_capabilities: [" text.chat ", "image.generate", "text.chat", "video.task.get"], }).api_key_capabilities, ).toEqual(["text.chat", "image.generate", "video.task.get"]); expect(parseConfigFile({ api_key_capabilities: [] }).api_key_capabilities).toEqual([]); expect(parseConfigFile({}).api_key_capabilities).toBeUndefined(); }); test("api_key_capabilities 手工配置格式错误时 fail closed", () => { expect(parseConfigFile({ api_key_capabilities: "text.chat" }).api_key_capabilities).toEqual([]); expect( parseConfigFile({ api_key_capabilities: ["text.chat", "Video Generate"] }).api_key_capabilities, ).toEqual([]); }); test("default_speech_recognition_model 从配置文件进入运行时 Settings", () => { const file = parseConfigFile({ default_speech_recognition_model: "qwen-audio-3.0-asr-flash", }); expect(file.default_speech_recognition_model).toBe("qwen-audio-3.0-asr-flash"); expect(resolve({ file }).defaultSpeechRecognitionModel).toBe("qwen-audio-3.0-asr-flash"); }); test("watermark 从配置文件进入 Settings,缺省时保持合规默认值 true", () => { expect(parseConfigFile({ watermark: false }).watermark).toBe(false); expect(parseConfigFile({ watermark: true }).watermark).toBe(true); expect(parseConfigFile({ watermark: "false" }).watermark).toBeUndefined(); expect(resolve({ file: { watermark: false } }).watermark).toBe(false); expect(resolve({}).watermark).toBe(true); }); test("baseUrl:flag > env > file > 默认,所有来源统一归一化", () => { const flags = { baseUrl: "https://flag.example.com/compatible-mode/v1?source=flag" }; const env = { DASHSCOPE_BASE_URL: "https://env.example.com/apps/anthropic#env" }; const file: ConfigFile = { base_url: "https://file.example.com/gateway/" }; expect(resolveModelBaseUrl(src({ flags, env, file }))).toBe("https://flag.example.com"); expect(resolveModelBaseUrl(src({ env, file }))).toBe("https://env.example.com"); expect(resolveModelBaseUrl(src({ file }))).toBe("https://file.example.com"); expect(resolveModelBaseUrl(src({}))).toBe("https://dashscope.aliyuncs.com"); }); test("baseUrl:非法 flag/env 在 resolver 边界报 usage error", () => { expect(() => resolveModelBaseUrl(src({ flags: { baseUrl: "not-a-url" } }))).toThrow( /Invalid model base URL/, ); expect(() => resolveModelBaseUrl(src({ env: { DASHSCOPE_BASE_URL: "file:///tmp/model" } })), ).toThrow(/Invalid model base URL/); }); test("baseUrl state distinguishes an implicit default from an explicitly configured default origin", () => { const defaultOrigin = "https://dashscope.aliyuncs.com"; expect(resolveModelBaseUrlState(src({}))).toEqual({ baseUrl: defaultOrigin, baseUrlIsDefault: true, }); for (const sources of [ src({ flags: { baseUrl: defaultOrigin } }), src({ env: { DASHSCOPE_BASE_URL: defaultOrigin } }), src({ file: { base_url: defaultOrigin } }), ]) { expect(resolveModelBaseUrlState(sources)).toEqual({ baseUrl: defaultOrigin, baseUrlIsDefault: false, }); } }); test("命名 config 仍保持 flag > env > selected file", () => { const env = { DASHSCOPE_BASE_URL: "https://env.example.com", DASHSCOPE_API_KEY: "sk-env", }; const sources = src({ configName: "token-plan", env, file: { api_key: "sk-token-plan", base_url: "https://profile.example.com", default_text_model: "custom-text", default_image_model: "custom-image", }, }); expect(resolveModelBaseUrl(sources)).toBe("https://env.example.com"); expect(resolveApiKey(sources)).toMatchObject({ token: "sk-env", baseUrl: "https://env.example.com", source: "env", }); expect(buildSettings(sources)).toMatchObject({ defaultTextModel: "custom-text", defaultImageModel: "custom-image", }); expect( resolveApiKey( src({ configName: "token-plan", flags: { apiKey: "sk-flag", baseUrl: "https://flag.example.com" }, env, file: sources.file, }), ), ).toMatchObject({ token: "sk-flag", baseUrl: "https://flag.example.com", source: "flag", }); }); test("output:flag > env > file > text", () => { const env = { DASHSCOPE_OUTPUT: "json" }; const file: ConfigFile = { output: "json" }; expect(resolve({ flags: { output: "text" }, env, file }).output).toBe("text"); expect(resolve({ env, file: { output: "text" } }).output).toBe("json"); expect(resolve({ file }).output).toBe("json"); expect(resolve({}).output).toBe("text"); }); test("timeout:flag > 合法 env > file > 300;非法 env 被跳过;非法 flag 抛错", () => { const file: ConfigFile = { timeout: 30 }; expect(resolve({ flags: { timeout: 10 }, env: { DASHSCOPE_TIMEOUT: "20" }, file }).timeout).toBe( 10, ); expect(resolve({ env: { DASHSCOPE_TIMEOUT: "20" }, file }).timeout).toBe(20); expect(resolve({ env: { DASHSCOPE_TIMEOUT: "abc" }, file }).timeout).toBe(30); expect(resolve({ env: { DASHSCOPE_TIMEOUT: "-5" }, file }).timeout).toBe(30); expect(resolve({}).timeout).toBe(300); expect(() => resolve({ flags: { timeout: -1 } })).toThrow(/Timeout/); }); test("workspaceId:flag > env > file(console 域 flag)", () => { const file: ConfigFile = { workspace_id: "ws-file" }; const env = { BAILIAN_WORKSPACE_ID: "ws-env" }; expect(resolve({ flags: { workspaceId: "ws-flag" }, env, file }).workspaceId).toBe("ws-flag"); expect(resolve({ env, file }).workspaceId).toBe("ws-env"); expect(resolve({ file }).workspaceId).toBe("ws-file"); expect(resolve({}).workspaceId).toBeUndefined(); }); test("console 三元组:flag > file,无兜底(默认值由 gateway 层兜)", () => { const file: ConfigFile = { console_region: "cn-shanghai", console_site: "international", console_switch_agent: 111, }; const fromFlags = resolve({ flags: { consoleRegion: "ap-southeast-1", consoleSite: "domestic", consoleSwitchAgent: 222 }, file, }); expect(fromFlags.consoleRegion).toBe("ap-southeast-1"); expect(fromFlags.consoleSite).toBe("domestic"); expect(fromFlags.consoleSwitchAgent).toBe(222); const fromFile = resolve({ file }); expect(fromFile.consoleRegion).toBe("cn-shanghai"); expect(fromFile.consoleSite).toBe("international"); expect(fromFile.consoleSwitchAgent).toBe(111); expect(resolve({}).consoleRegion).toBeUndefined(); expect(resolve({}).consoleSite).toBeUndefined(); }); test("verbose:flag 或 DASHSCOPE_VERBOSE=1(无 file 源;env 非 1 不生效)", () => { expect(resolve({ flags: { verbose: true } }).verbose).toBe(true); expect(resolve({ env: { DASHSCOPE_VERBOSE: "1" } }).verbose).toBe(true); expect(resolve({ env: { DASHSCOPE_VERBOSE: "0" } }).verbose).toBe(false); expect(resolve({}).verbose).toBe(false); }); test("telemetry:DO_NOT_TRACK=1 一票否决 > file > 默认 true", () => { expect(resolve({ env: { DO_NOT_TRACK: "1" }, file: { telemetry: true } }).telemetry).toBe(false); expect(resolve({ file: { telemetry: false } }).telemetry).toBe(false); expect(resolve({}).telemetry).toBe(true); }); test("apiKey 凭证:flag > env > file,source 字段随之;无 key 抛 AUTH", () => { const all = src({ flags: { apiKey: "sk-flag" }, env: { DASHSCOPE_API_KEY: "sk-env" }, file: { api_key: "sk-file" }, }); expect(resolveApiKey(all)).toMatchObject({ token: "sk-flag", source: "flag" }); const envFile = src({ env: { DASHSCOPE_API_KEY: "sk-env" }, file: { api_key: "sk-file" } }); expect(resolveApiKey(envFile)).toMatchObject({ token: "sk-env", source: "env" }); const fileOnly = src({ file: { api_key: "sk-file" } }); expect(resolveApiKey(fileOnly)).toMatchObject({ token: "sk-file", source: "config" }); expect(() => resolveApiKey(src({}))).toThrow(/No API key/); }); test("console 凭证:token 仅 file 源;目标 flag > file > 默认;无 token 抛 AUTH", () => { const cred = resolveConsole( src({ flags: { consoleRegion: "ap-southeast-1" }, file: { access_token: "tok", console_site: "international", console_switch_agent: 7 }, }), ); expect(cred).toMatchObject({ token: "tok", region: "ap-southeast-1", site: "international", switchAgent: 7, }); expect(resolveConsole(src({ file: { access_token: "tok" } }))).toMatchObject({ region: "cn-beijing", site: "domestic", }); expect(() => resolveConsole(src({}))).toThrow(/console access token/); }); test("openapi 凭证:按来源成对解析,flag > env > file;缺 id 或 secret 抛 AUTH", () => { const all = src({ flags: { accessKeyId: "ak-flag", accessKeySecret: "secret-flag" }, env: { ALIBABA_CLOUD_ACCESS_KEY_ID: "ak-env", ALIBABA_CLOUD_ACCESS_KEY_SECRET: "secret-env", }, }); expect(resolveOpenApi(all)).toMatchObject({ accessKeyId: "ak-flag", accessKeySecret: "secret-flag", source: "flag", }); const envOnly = src({ env: { ALIBABA_CLOUD_ACCESS_KEY_ID: "ak-env", ALIBABA_CLOUD_ACCESS_KEY_SECRET: "secret-env", }, }); expect(resolveOpenApi(envOnly)).toMatchObject({ accessKeyId: "ak-env", accessKeySecret: "secret-env", source: "env", }); const fileOnly = src({ file: { access_key_id: "ak-file", access_key_secret: "secret-file", }, }); expect(resolveOpenApi(fileOnly)).toMatchObject({ accessKeyId: "ak-file", accessKeySecret: "secret-file", source: "config", }); const legacyFileOnly = src({ file: parseConfigFile({ openapi_access_key_id: "ak-legacy-file", openapi_access_key_secret: "secret-legacy-file", }), }); expect(resolveOpenApi(legacyFileOnly)).toMatchObject({ accessKeyId: "ak-legacy-file", accessKeySecret: "secret-legacy-file", source: "config", }); expect(() => resolveOpenApi( src({ flags: { accessKeyId: "ak-flag" }, env: { ALIBABA_CLOUD_ACCESS_KEY_ID: "ak-env", ALIBABA_CLOUD_ACCESS_KEY_SECRET: "secret-env", }, file: { access_key_id: "ak-file", access_key_secret: "secret-file", }, }), ), ).toThrow(/Incomplete OpenAPI AK\/SK/); expect(() => resolveOpenApi( src({ env: { ALIBABA_CLOUD_ACCESS_KEY_SECRET: "secret-env" }, file: { access_key_id: "ak-file", access_key_secret: "secret-file", }, }), ), ).toThrow(/Incomplete OpenAPI AK\/SK/); expect(() => resolveOpenApi( src({ file: { access_key_id: "ak-file", }, }), ), ).toThrow(/Incomplete OpenAPI AK\/SK/); expect(() => resolveOpenApi(src({}))).toThrow(/No OpenAPI AK\/SK/); expect(() => resolveOpenApi(src({ env: { ALIBABA_CLOUD_ACCESS_KEY_ID: "ak-env" } }))).toThrow( /Incomplete OpenAPI AK\/SK/, ); }); test("openapi 凭证:低优先级来源缺字段时不影响更高优先级成对凭证", () => { const completeFlagCred = src({ flags: { accessKeyId: "ak-flag", accessKeySecret: "secret-flag" }, env: { ALIBABA_CLOUD_ACCESS_KEY_SECRET: "secret-env" }, }); expect(resolveOpenApi(completeFlagCred)).toMatchObject({ accessKeyId: "ak-flag", accessKeySecret: "secret-flag", source: "flag", }); const completeEnvCred = src({ env: { ALIBABA_CLOUD_ACCESS_KEY_ID: "ak-env", ALIBABA_CLOUD_ACCESS_KEY_SECRET: "secret-env", }, file: { access_key_id: "ak-file" }, }); expect(resolveOpenApi(completeEnvCred)).toMatchObject({ accessKeyId: "ak-env", accessKeySecret: "secret-env", source: "env", }); }); test("default*Model / outputDir:仅 file 源", () => { const c = resolve({ file: parseConfigFile({ default_text_model: "qwen-max", default_video_model: "wan-t2v", default_image_to_video_model: "wan-i2v", default_reference_to_video_model: "wan-r2v", output_dir: "/tmp/out", }), }); expect(c.defaultTextModel).toBe("qwen-max"); expect(c.defaultVideoModel).toBe("wan-t2v"); expect(c.defaultImageToVideoModel).toBe("wan-i2v"); expect(c.defaultReferenceToVideoModel).toBe("wan-r2v"); expect(c.outputDir).toBe("/tmp/out"); expect(resolve({}).defaultTextModel).toBeUndefined(); }); test("quiet/dryRun:仅 flag 源,直通", () => { const on = resolve({ flags: { quiet: true, dryRun: true }, }); expect(on).toMatchObject({ quiet: true, dryRun: true, }); const off = resolve({}); expect(off).toMatchObject({ quiet: false, dryRun: false, }); });