gunnar-server 3.0.0

The gunnar git server: SSH and smart-HTTP git transports and the gRPC control plane. The appliance half, extracted from gunnar-cli so the server code lives in its own crate.
Documentation
//! ★ **`Admin.StorageInfo` reports the engine in parts** (owner, 2026-10-02):
//! the console's Storage page read "Engine Backend znippy-plugin-git
//! (Arrow-IPC archive; objects + refs)" — one prose line it could only print
//! whole. `StorageInfoResponse.engine` carries the name, the description and
//! the source repository apart, off the same `Engine` the prose line is made
//! from, so the console shows the name, the description as its tooltip, and
//! links the name.

use std::sync::Arc;

use gunnar_grpc::ControlPlane as _;
use gunnar_server::control_plane::CoreControlPlane;
use gunnar_server::stores::ServerStores;

#[test]
fn storage_info_names_the_engine_its_description_and_its_source_repository_apart() {
    let dir = tempfile::tempdir().expect("tempdir");
    let stores = Arc::new(ServerStores::open(dir.path()).expect("stores"));
    let plane = CoreControlPlane::new(Arc::clone(&stores));
    let info = plane.storage_info(None).expect("storage info");
    let engine = info
        .engine
        .clone()
        .expect("StorageInfoResponse.engine is set");
    let running = stores.engine();
    assert_eq!(
        engine,
        running.report(),
        "the report is the running engine's"
    );
    assert!(
        !engine.name.is_empty()
            && !engine.description.is_empty()
            && engine.repo_url.starts_with("https://"),
        "{engine:?}"
    );
    assert_eq!(
        info.object_backend,
        format!("{} ({})", engine.name, engine.description),
        "the prose line is the same parts"
    );
}