aion-server 0.30.0

Aion workflow server library: HTTP, gRPC, WebSocket, and worker endpoints. Run it with the `aion` binary from the aion-cli crate.
Documentation
//! The keep-set behind `prune_snapshots`: what a listing proves is still read.
//!
//! These cells are about the one answer the prune must never give by accident —
//! "nothing is live, remove everything" — when the store's listing could not
//! read a row (#211). The keep-set is a pure function of the listing, so the
//! arm is proven here without a store, a home directory, or a worker.

use std::collections::BTreeSet;
use std::path::PathBuf;

use aion_store::{
    DeployedBinaryIdentity, DesiredState, NewWorkerDeployment, UndecodableWorkerDeployment,
    WorkerArtifactRef, WorkerDeployment, WorkerDeploymentListing,
};
use chrono::{TimeZone, Utc};

use super::live_documents;

/// A record whose argv names a staged document, as the provisioner mints them.
fn agent_record(name: &str, document: &str) -> WorkerDeployment {
    let minted = WorkerDeployment::new(
        NewWorkerDeployment {
            name: name.to_owned(),
            artifact: WorkerArtifactRef::Builtin {
                verb: vec![
                    "worker".to_owned(),
                    "agent".to_owned(),
                    document.to_owned(),
                    "--task-queue".to_owned(),
                    "demo_agent".to_owned(),
                ],
            },
            binary: DeployedBinaryIdentity {
                version: "test".to_owned(),
                commit: "test".to_owned(),
                dirty: "false".to_owned(),
                content_hash: "test".to_owned(),
            },
            namespaces: BTreeSet::from(["default".to_owned()]),
            task_queue: "demo_agent".to_owned(),
            node: None,
            desired: DesiredState::Running,
        },
        Utc.with_ymd_and_hms(2026, 8, 31, 9, 0, 0)
            .single()
            .unwrap_or_default(),
    );
    match minted {
        Ok(record) => record,
        Err(error) => unreachable!("the fixture record is valid by construction: {error}"),
    }
}

#[test]
fn a_fully_decoded_listing_keeps_exactly_the_documents_its_records_name() {
    let listing = WorkerDeploymentListing {
        deployments: vec![
            agent_record("auto/one", "/home/workers/documents/one@aaaa/one.awl"),
            agent_record("auto/two", "/home/workers/documents/two@bbbb/two.awl"),
        ],
        undecodable: Vec::new(),
    };
    let live = live_documents(&listing);
    assert_eq!(
        live,
        Some(BTreeSet::from([
            PathBuf::from("/home/workers/documents/one@aaaa/one.awl"),
            PathBuf::from("/home/workers/documents/two@bbbb/two.awl"),
        ])),
        "every named document is live, and nothing else is"
    );
}

#[test]
fn an_empty_but_readable_listing_is_an_answer_and_keeps_nothing() {
    // The other arm of the same law: zero records that all decoded is a real
    // "nothing is live", and the prune may reclaim. The cell exists so the fix
    // for #211 cannot be widened into "never prune".
    let listing = WorkerDeploymentListing {
        deployments: Vec::new(),
        undecodable: Vec::new(),
    };
    assert_eq!(live_documents(&listing), Some(BTreeSet::new()));
}

#[test]
fn one_undecodable_record_keeps_every_document_not_just_its_own() {
    // The poisoned row's document path is unknown by definition — the row did
    // not decode — so there is no way to keep "just its" snapshot. The only
    // honest keep-set is everything, the same answer a listing that failed
    // outright already gives. Before this cell, the row was treated as absent
    // and the running worker minted from it lost its document on the next
    // deploy.
    let listing = WorkerDeploymentListing {
        deployments: vec![agent_record(
            "auto/one",
            "/home/workers/documents/one@aaaa/one.awl",
        )],
        undecodable: vec![UndecodableWorkerDeployment {
            name: "auto/assistant".to_owned(),
            error: "unknown variant `Closed` for DesiredState".to_owned(),
        }],
    };
    assert_eq!(
        live_documents(&listing),
        None,
        "an undecodable record makes the keep-set unknowable, and unknowable keeps everything"
    );
}