mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-10-25 17:22:27 +08:00
Switching, adding, editing, sync, universal providers and entering or leaving local routing now write Codex through the write engine. Only routing, credential, model, reasoning, context-window and compatibility fields are replaced; MCP servers, [projects], comments and the user's own provider tables stay byte-for-byte. auth.json, config.toml, the model catalog, the managed-account marker and a device-local login stash commit as one operation, so a crash at any point rolls forward on startup. - Third-party routes are always written as [model_providers.custom] with the key in experimental_bearer_token. Rows that use another id, a top-level openai_base_url or a legacy reserved table are normalized on the way out. Switching to an official card leaves a dormant custom table (local proxy address plus placeholder credential) so older third-party sessions can still resume. - requires_openai_auth follows the route's auth and whether a login is on disk; keyring and ephemeral credential stores are handled separately. - A switch is refused before anything is written when the active profile overrides the route, the selected table is missing or built in, a keyless row would fall back to the official login, or the row has no auth. - Provider tables an older version wrote are removed only when they can be proven to be CC Switch's. Tables a profile references are kept, and reserved ids that cannot be proven are renamed. - Provider-exclusive fields, including web_search and a row's own model_catalog_json, are removed on the way out only when live still holds the previous provider's value. - No more backfill. A native ChatGPT login that CC Switch deletes or overwrites is kept in ~/.cc-switch/codex-login-stash.json (0600, not synced) and restored when switching back to an official card; logging out on an official card discards it. Login snapshots already in official rows seed the stash once. A leftover API key in auth.json is deleted only when it matches a third-party row's key. - Managed accounts fetch their token before taking the lock. If the CLI refreshes the login during a switch, the switch stops before writing anything. Once files are published, a later failure no longer rolls the row back; startup finishes the switch. - Local routing: third-party routes get a fixed local-proxy table and the official route uses cc-switch-official plus the client's own login. The contract covers every field written, so a route switch with the same contract touches no files. - The Codex editor shows what config.toml will look like after switching. On save, key fields go to the row and other edits go to live as global settings with a three-way check. Row fields that never reach live are listed read-only and can be copied. - The Codex common-config snippet is frozen like Claude's; its UI and the TOML snippet command are removed. New Codex rows keep commonConfigEnabled: true for older versions that sync them. - The write engine can delete a file as part of an operation. - Integration-test mutexes tolerate poisoning so one failure no longer cascades into unrelated tests.
43 lines
1.5 KiB
Rust
43 lines
1.5 KiB
Rust
use cc_switch_lib::{
|
||
get_pricing_model_source_test_hook, set_pricing_model_source_test_hook, AppError,
|
||
};
|
||
|
||
#[path = "support.rs"]
|
||
mod support;
|
||
use support::{create_test_state, ensure_test_home, reset_test_fs, test_mutex};
|
||
|
||
// 测试使用 Mutex 进行串行化,跨 await 持锁是预期行为
|
||
#[allow(clippy::await_holding_lock)]
|
||
#[tokio::test]
|
||
async fn pricing_model_source_commands_round_trip() {
|
||
let _guard = test_mutex().lock().unwrap_or_else(|e| e.into_inner());
|
||
reset_test_fs();
|
||
let _home = ensure_test_home();
|
||
|
||
let state = create_test_state().expect("create test state");
|
||
|
||
let default = get_pricing_model_source_test_hook(&state, "claude")
|
||
.await
|
||
.expect("read default pricing model source");
|
||
assert_eq!(default, "response");
|
||
|
||
set_pricing_model_source_test_hook(&state, "claude", "request")
|
||
.await
|
||
.expect("set pricing model source");
|
||
let updated = get_pricing_model_source_test_hook(&state, "claude")
|
||
.await
|
||
.expect("read updated pricing model source");
|
||
assert_eq!(updated, "request");
|
||
|
||
let err = set_pricing_model_source_test_hook(&state, "claude", "invalid")
|
||
.await
|
||
.expect_err("invalid pricing model source should error");
|
||
// 错误已改为 Localized 类型(支持 i18n)
|
||
match err {
|
||
AppError::Localized { key, .. } => {
|
||
assert_eq!(key, "error.invalidPricingMode");
|
||
}
|
||
other => panic!("expected localized error, got {other:?}"),
|
||
}
|
||
}
|