Hand-Icon als App-/Tray-Icon, App heißt ai-control, Tests für Keychain/Key-Status

- Icons komplett aus hand.png regeneriert (tauri icon)
- Tray-Icon explizit per include_bytes (cargo trackt die Datei) und als
  Template-Icon für helle/dunkle Menüleiste; tauri-Feature image-png
- Cargo-Paket app -> ai-control, Fenstertitel ai-control
- Tests: keychain_service-Suffix gegen verifizierten Referenzwert,
  hasCredentials-Status (Key vorhanden/leer/fehlt)
This commit is contained in:
marcus.hinz
2026-07-04 13:45:02 +02:00
parent 9b008cbf51
commit 62ce802465
55 changed files with 110 additions and 28 deletions
+39 -2
View File
@@ -837,7 +837,7 @@ fn main_builder() -> tauri::Builder<tauri::Wry> {
// Fenster im Code statt in tauri.conf.json, damit der Terminal-Prozess
// (gleiches Binary, gleiche Config) kein main-Fenster anlegt.
tauri::WebviewWindowBuilder::new(app, "main", tauri::WebviewUrl::default())
.title("AI Control")
.title("ai-control")
.inner_size(800.0, 600.0)
.visible(false)
.build()?;
@@ -846,7 +846,13 @@ fn main_builder() -> tauri::Builder<tauri::Wry> {
let quit = tauri::menu::MenuItem::with_id(app, "quit", "Beenden", true, None::<&str>)?;
let menu = tauri::menu::Menu::with_items(app, &[&open, &quit])?;
tauri::tray::TrayIconBuilder::with_id("main")
.icon(app.default_window_icon().unwrap().clone())
// include_bytes! statt default_window_icon: cargo trackt die Datei,
// neu generierte Icons landen damit sicher im nächsten Build.
.icon(tauri::image::Image::from_bytes(include_bytes!(
"../icons/128x128.png"
))?)
// Template-Icon: macOS färbt es passend zur Menüleiste ein.
.icon_as_template(true)
.menu(&menu)
.on_menu_event(|app, event| match event.id.as_ref() {
"open" => {
@@ -1078,6 +1084,37 @@ mod tests {
assert!(!p.pool_dir("kunde").exists());
}
/// Referenzwert vom echten System: privateDefault → 096c4ef9
/// (verifiziert 2026-07-03 gegen den von claude angelegten Eintrag).
#[test]
fn keychain_service_suffix() {
let p = Paths { home: PathBuf::from("/Users/marcus.hinz") };
assert_eq!(
keychain_service(&p, "privateDefault"),
"Claude Code-credentials-096c4ef9"
);
}
#[test]
fn pool_key_status() {
let p = tmp_paths();
create_apikey_pool_in(&p, "kunde", "sk-1").unwrap();
make_oauth_pool(&p, "privat");
let pools = list_pools_in(&p).unwrap();
assert!(pools.iter().find(|x| x.name == "kunde").unwrap().has_credentials);
// oauth: Keychain wird im Listing bewusst nicht geprüft → immer true.
assert!(pools.iter().find(|x| x.name == "privat").unwrap().has_credentials);
fs::write(p.pool_dir("kunde").join(APIKEY_FILE), "\n").unwrap();
let pools = list_pools_in(&p).unwrap();
assert!(!pools.iter().find(|x| x.name == "kunde").unwrap().has_credentials);
fs::remove_file(p.pool_dir("kunde").join(APIKEY_FILE)).unwrap();
let pools = list_pools_in(&p).unwrap();
assert!(!pools.iter().find(|x| x.name == "kunde").unwrap().has_credentials);
}
#[test]
fn pool_loeschen_loest_zuordnungen() {
let p = tmp_paths();