use assert_cmd::Command;
use predicates::prelude::*;
use tempfile::tempdir;
fn aim() -> Command {
#[allow(deprecated)]
Command::cargo_bin("aim").unwrap()
}
#[test]
fn test_cli_help() {
aim()
.arg("--help")
.assert()
.success()
.stdout(predicate::str::contains("secure vault"))
.stdout(predicate::str::contains("Usage").or(predicate::str::contains("USAGE")));
}
#[test]
fn test_cli_version() {
aim()
.arg("--version")
.assert()
.success()
.stdout(predicate::str::contains("1.2.1").or(predicate::str::contains("aim")));
}
#[test]
fn test_cli_no_args_shows_help() {
aim().assert().code(6);
}
#[test]
fn test_cli_init_help() {
aim()
.args(["init", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("init").or(predicate::str::contains("vault")));
}
#[test]
fn test_cli_store_help() {
aim()
.args(["store", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("store").or(predicate::str::contains("model")));
}
#[test]
fn test_cli_list_help() {
aim().args(["list", "--help"]).assert().success();
}
#[test]
fn test_cli_convert_help() {
aim()
.args(["convert", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("convert"));
}
#[test]
fn test_cli_compliance_help() {
aim()
.args(["compliance", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("compliance"));
}
#[test]
fn test_cli_init_vault() {
let dir = tempdir().unwrap();
aim()
.args(["init", "--name", "test-vault"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("Vault").or(predicate::str::contains("initialized").or(predicate::str::contains("created"))));
}
#[test]
fn test_cli_list_without_passphrase_source() {
let dir = tempdir().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["list"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.env_remove("aimodelvault_PASSPHRASE")
.timeout(std::time::Duration::from_secs(10))
.assert()
.failure();
}
#[test]
fn test_cli_empty_passphrase_env_is_rejected() {
let dir = tempdir().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["list"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.env("aimodelvault_PASSPHRASE", "")
.timeout(std::time::Duration::from_secs(10))
.assert()
.failure();
}
#[test]
fn test_cli_list_conversions() {
aim().args(["list-conversions"]).assert().success().stdout(
predicate::str::contains("safetensors")
.or(predicate::str::contains("Safetensors"))
.or(predicate::str::contains("GGUF"))
.or(predicate::str::contains("gguf")),
);
}
#[test]
fn test_cli_stats_on_vault() {
let dir = tempdir().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["stats"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
}
#[test]
fn test_cli_unknown_subcommand() {
aim().arg("nonexistent-command").assert().failure();
}
#[test]
fn test_cli_store_missing_args() {
aim()
.args(["store"])
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("error")));
}
#[test]
fn test_cli_get_missing_args() {
aim()
.args(["get"])
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("error")));
}
#[test]
fn test_cli_sqlite_versions_flag_accepted() {
let dir = tempdir().unwrap();
aim()
.args(["--sqlite-versions", "init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
}
#[test]
fn test_cli_compliance_runs() {
let dir = tempdir().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["compliance"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
}
#[test]
fn test_cli_versions_help() {
aim()
.args(["versions", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("version").or(predicate::str::contains("Version")));
}
#[test]
fn test_cli_lineage_help() {
aim()
.args(["lineage", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("lineage").or(predicate::str::contains("history")));
}
#[test]
fn test_cli_delete_help() {
aim()
.args(["delete", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("delete").or(predicate::str::contains("Delete")));
}
#[test]
fn test_cli_archive_help() {
aim()
.args(["archive", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("archive").or(predicate::str::contains("Archive")));
}
#[test]
fn test_cli_extract_help() {
aim()
.args(["extract", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("extract").or(predicate::str::contains("Extract")));
}
#[test]
fn test_cli_analyze_help() {
aim()
.args(["analyze", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("analyze").or(predicate::str::contains("Analyze")));
}
#[test]
fn test_cli_deduplicate_help() {
aim()
.args(["deduplicate", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("deduplicate").or(predicate::str::contains("duplicate")));
}
#[test]
fn test_cli_export_help() {
aim()
.args(["export", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("export").or(predicate::str::contains("Export")));
}
#[test]
fn test_cli_cache_help() {
aim()
.args(["cache", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("cache").or(predicate::str::contains("Cache")));
}
#[test]
fn test_cli_change_passphrase_help() {
aim()
.args(["change-passphrase", "--help"])
.assert()
.success()
.stdout(
predicate::str::contains("passphrase")
.or(predicate::str::contains("Passphrase"))
.or(predicate::str::contains("Change")),
);
}
#[test]
fn test_cli_cloud_help() {
aim()
.args(["cloud", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("cloud").or(predicate::str::contains("Cloud")));
}
#[test]
fn test_cli_database_help() {
aim()
.args(["database", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("database").or(predicate::str::contains("Database")));
}
#[test]
fn test_cli_card_help() {
aim()
.args(["card", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("card").or(predicate::str::contains("Card")));
}
#[test]
fn test_cli_telemetry_help() {
aim()
.args(["telemetry", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("telemetry").or(predicate::str::contains("Telemetry")));
}
#[test]
fn test_cli_telemetry_status() {
aim()
.args(["telemetry", "status"])
.assert()
.success()
.stdout(
predicate::str::contains("Telemetry")
.or(predicate::str::contains("telemetry"))
.or(predicate::str::contains("enabled"))
.or(predicate::str::contains("disabled")),
);
}
#[test]
fn test_cli_telemetry_disable_then_status() {
aim()
.args(["telemetry", "disable"])
.env("DO_NOT_TRACK", "1")
.assert()
.success();
aim()
.args(["telemetry", "status"])
.env("DO_NOT_TRACK", "1")
.assert()
.success();
}
#[test]
fn test_cli_database_init_and_stats() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
aim()
.args([
"database",
"init",
"--path",
db_path.to_str().unwrap(),
"--db-type",
"sqlite",
])
.assert()
.success();
aim()
.args(["database", "stats", "--path", db_path.to_str().unwrap()])
.assert()
.success();
}
#[test]
fn test_cli_database_list_empty() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
aim()
.args([
"database",
"init",
"--path",
db_path.to_str().unwrap(),
"--db-type",
"sqlite",
])
.assert()
.success();
aim()
.args(["database", "list", "--path", db_path.to_str().unwrap()])
.assert()
.success();
}
#[test]
fn test_cli_database_store_and_search() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let doc_path = dir.path().join("doc.txt");
std::fs::write(
&doc_path,
"The transformer architecture uses attention mechanisms.",
)
.unwrap();
aim()
.args([
"database",
"init",
"--path",
db_path.to_str().unwrap(),
"--db-type",
"sqlite",
])
.assert()
.success();
aim()
.args([
"database",
"store",
"--path",
db_path.to_str().unwrap(),
"--input",
doc_path.to_str().unwrap(),
])
.assert()
.success();
aim()
.args([
"database",
"search",
"--path",
db_path.to_str().unwrap(),
"transformer",
])
.assert()
.success();
}
#[test]
fn test_cli_card_template_basic() {
let dir = tempdir().unwrap();
let output = dir.path().join("card.json");
aim()
.args([
"card",
"template",
"--template-type",
"basic",
"--output",
output.to_str().unwrap(),
])
.assert()
.success();
assert!(output.exists());
}
#[test]
fn test_cli_card_create_and_validate() {
let dir = tempdir().unwrap();
let output = dir.path().join("card.json");
aim()
.args([
"card",
"create",
"test-model",
"--version",
"1.0",
"--description",
"A test model",
"--model-type",
"classifier",
"--architecture",
"ResNet-50",
"--output",
output.to_str().unwrap(),
])
.assert()
.success();
assert!(output.exists());
aim()
.args(["card", "validate", output.to_str().unwrap()])
.assert()
.success();
}
#[test]
fn test_cli_card_create_and_show() {
let dir = tempdir().unwrap();
let output = dir.path().join("card.yaml");
aim()
.args([
"card",
"create",
"my-llm",
"--version",
"2.0",
"--description",
"A language model",
"--model-type",
"LLM",
"--architecture",
"Transformer",
"--output",
output.to_str().unwrap(),
])
.assert()
.success();
aim()
.args([
"card",
"show",
output.to_str().unwrap(),
"--format",
"markdown",
])
.assert()
.success()
.stdout(predicate::str::contains("my-llm").or(predicate::str::contains("language model")));
}
#[test]
fn test_cli_card_convert_json_to_yaml() {
let dir = tempdir().unwrap();
let json_out = dir.path().join("card.json");
let yaml_out = dir.path().join("card.yaml");
aim()
.args([
"card",
"create",
"conv-model",
"--version",
"1.0",
"--description",
"For conversion test",
"--model-type",
"classifier",
"--architecture",
"CNN",
"--output",
json_out.to_str().unwrap(),
])
.assert()
.success();
aim()
.args([
"card",
"convert",
json_out.to_str().unwrap(),
yaml_out.to_str().unwrap(),
])
.assert()
.success();
assert!(yaml_out.exists());
}
#[test]
fn test_cli_delete_missing_args() {
aim()
.args(["delete"])
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("error")));
}
#[test]
fn test_cli_archive_missing_args() {
aim()
.args(["archive"])
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("error")));
}
#[test]
fn test_cli_versions_missing_args() {
aim()
.args(["versions"])
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("error")));
}
#[test]
fn test_cli_lineage_missing_args() {
aim()
.args(["lineage"])
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("error")));
}
#[test]
fn test_cli_export_missing_args() {
aim()
.args(["export"])
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("error")));
}
#[test]
fn test_cli_cache_on_vault() {
let dir = tempdir().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["cache"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
}
#[test]
fn test_cli_list_conversions_contains_formats() {
aim()
.args(["list-conversions"])
.assert()
.success()
.stdout(predicate::str::contains("ONNX").or(predicate::str::contains("onnx")))
.stdout(
predicate::str::contains("PyTorch")
.or(predicate::str::contains("pytorch"))
.or(predicate::str::contains("pt")),
);
}
#[test]
fn test_cli_init_custom_name() {
let dir = tempdir().unwrap();
aim()
.args(["init", "--name", "my-custom-vault"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success()
.stdout(
predicate::str::contains("my-custom-vault")
.or(predicate::str::contains("Vault"))
.or(predicate::str::contains("initialized")),
);
}
#[test]
fn test_cli_sqlite_versions_with_stats() {
let dir = tempdir().unwrap();
aim()
.args(["--sqlite-versions", "init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["--sqlite-versions", "stats"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
}
#[test]
fn test_cli_sqlite_versions_with_compliance() {
let dir = tempdir().unwrap();
aim()
.args(["--sqlite-versions", "init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["--sqlite-versions", "compliance"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
}
#[test]
fn test_cli_no_telemetry_flag_with_init() {
let dir = tempdir().unwrap();
aim()
.args(["--no-telemetry", "init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
}
#[test]
fn test_cli_do_not_track_env() {
aim()
.args(["compliance"])
.env("DO_NOT_TRACK", "1")
.assert()
.success();
}
#[test]
fn test_cli_convert_missing_args() {
aim()
.args(["convert"])
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("error")));
}
#[test]
fn test_cli_cloud_push_missing_args() {
aim()
.args(["cloud", "push"])
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("error")));
}
#[test]
fn test_cli_cloud_pull_missing_args() {
aim()
.args(["cloud", "pull"])
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("error")));
}
#[test]
fn test_cli_cloud_list_missing_args() {
aim()
.args(["cloud", "list"])
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("error")));
}
#[test]
fn test_cli_extract_nonexistent_archive() {
aim()
.args(["extract", "nonexistent.tar"])
.assert()
.failure();
}
#[test]
fn test_cli_database_search_no_results() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("empty.db");
aim()
.args([
"database",
"init",
"--path",
db_path.to_str().unwrap(),
"--db-type",
"sqlite",
])
.assert()
.success();
aim()
.args([
"database",
"search",
"--path",
db_path.to_str().unwrap(),
"nonexistent query",
])
.assert()
.success();
}
#[test]
fn test_cli_database_store_nonexistent_file() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
aim()
.args([
"database",
"init",
"--path",
db_path.to_str().unwrap(),
"--db-type",
"sqlite",
])
.assert()
.success();
aim()
.args([
"database",
"store",
"--path",
db_path.to_str().unwrap(),
"--input",
"nonexistent_file.txt",
])
.assert()
.failure();
}
#[test]
fn test_cli_card_validate_nonexistent_file() {
aim()
.args(["card", "validate", "no-such-card.json"])
.assert()
.failure();
}
#[test]
fn test_cli_card_show_nonexistent_file() {
aim()
.args(["card", "show", "no-such-card.json"])
.assert()
.failure();
}
#[test]
fn test_cli_sqlite_versions_with_cache() {
let dir = tempdir().unwrap();
aim()
.args(["--sqlite-versions", "init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["--sqlite-versions", "cache"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
}
#[test]
fn test_cli_init_twice_same_dir() {
let dir = tempdir().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
}
#[test]
fn test_cli_tag_help() {
aim()
.args(["tag", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("tag").or(predicate::str::contains("Tag")));
}
#[test]
fn test_cli_search_help() {
aim()
.args(["search", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("search").or(predicate::str::contains("Search")));
}
#[test]
fn test_cli_vault_export_help() {
aim()
.args(["vault-export", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("export").or(predicate::str::contains("Export")));
}
#[test]
fn test_cli_vault_import_help() {
aim()
.args(["vault-import", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("import").or(predicate::str::contains("Import")));
}
#[test]
fn test_cli_gc_help() {
aim().args(["gc", "--help"]).assert().success().stdout(
predicate::str::contains("gc")
.or(predicate::str::contains("garbage").or(predicate::str::contains("clean"))),
);
}
#[test]
fn test_cli_browse_help() {
aim()
.args(["browse", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("browse").or(predicate::str::contains("Browse")));
}
#[test]
fn test_cli_webhook_help() {
aim()
.args(["webhook", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("webhook").or(predicate::str::contains("Webhook")));
}
#[test]
fn test_cli_acl_help() {
aim()
.args(["acl", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("acl").or(predicate::str::contains("access")));
}
#[test]
fn test_cli_validate_help() {
aim()
.args(["validate", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("validate").or(predicate::str::contains("Validate")));
}
#[test]
fn test_cli_policy_help() {
aim()
.args(["policy", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("policy").or(predicate::str::contains("Policy")));
}
#[test]
fn test_cli_lineage_graph_help() {
aim()
.args(["lineage-graph", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("lineage").or(predicate::str::contains("Lineage")));
}
#[test]
fn test_cli_plugin_help() {
aim()
.args(["plugin", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("plugin").or(predicate::str::contains("Plugin")));
}
#[test]
fn test_cli_profile_help() {
aim()
.args(["profile", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("profile").or(predicate::str::contains("Profile")));
}
#[test]
fn test_cli_gc_dry_run_on_vault() {
let dir = tempdir().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["gc", "--dry-run"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
}
#[test]
fn test_cli_acl_grant_list_revoke() {
let dir = tempdir().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["acl", "grant", "alice", "writer"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["acl", "list"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("alice"));
aim()
.args(["acl", "revoke", "alice"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
}
#[test]
fn test_cli_webhook_add_list_remove() {
let dir = tempdir().unwrap();
let id = format!("hook-{}", std::process::id());
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["webhook", "add", &id, "https://example.com/hook"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["webhook", "list"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("example.com"));
}
#[test]
fn test_cli_policy_set_show() {
let dir = tempdir().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["policy", "set", "test-model", "--max-versions", "5"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["policy", "show", "test-model"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("test-model"));
}
#[test]
fn test_cli_profile_create_list_activate() {
let dir = tempdir().unwrap();
aim()
.args(["profile", "create", "dev"])
.env("aimodelvault_CONFIG", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["profile", "list"])
.env("aimodelvault_CONFIG", dir.path().to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("dev"));
aim()
.args(["profile", "activate", "dev"])
.env("aimodelvault_CONFIG", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["profile", "show", "dev"])
.env("aimodelvault_CONFIG", dir.path().to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("dev"));
}
#[test]
fn test_cli_lineage_graph_show_empty() {
let dir = tempdir().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["lineage-graph", "show", "any-model"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
}
#[test]
fn test_cli_plugin_list_empty() {
let dir = tempdir().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["plugin", "list"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
}
#[test]
fn test_cli_tag_add_list_on_vault() {
let dir = tempdir().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["tag", "add", "my-model", "llm", "production"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["tag", "list", "my-model"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("llm"));
}
#[test]
fn test_cli_validate_missing_args() {
aim()
.args(["validate"])
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("error")));
}
#[test]
fn test_cli_vault_export_missing_args() {
aim()
.args(["vault-export"])
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("error")));
}
#[test]
fn test_cli_vault_import_missing_args() {
aim()
.args(["vault-import"])
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("error")));
}
#[test]
fn test_cli_tag_add_missing_args() {
aim()
.args(["tag", "add"])
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("error")));
}
#[test]
fn test_cli_search_empty_query() {
let dir = tempdir().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["search"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
}
#[test]
fn test_cli_quantize_help() {
aim()
.args(["quantize", "--help"])
.assert()
.success()
.stdout(
predicate::str::contains("quantize")
.or(predicate::str::contains("Quantize")
.or(predicate::str::contains("quantization"))),
);
}
#[test]
fn test_cli_eval_help() {
aim()
.args(["eval", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("eval").or(predicate::str::contains("Eval")));
}
#[test]
fn test_cli_backup_help() {
aim()
.args(["backup", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("backup").or(predicate::str::contains("Backup")));
}
#[test]
fn test_cli_vaults_help() {
aim().args(["vaults", "--help"]).assert().success().stdout(
predicate::str::contains("vaults")
.or(predicate::str::contains("Vaults").or(predicate::str::contains("vault"))),
);
}
#[test]
fn test_cli_quantize_set_list_remove() {
let dir = tempdir().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args([
"quantize",
"set",
"fast-q4",
"--method",
"q4_k_m",
"--description",
"Fast 4-bit",
])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["quantize", "list"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("fast-q4"));
aim()
.args(["quantize", "remove", "fast-q4"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
}
#[test]
fn test_cli_quantize_estimate() {
let dir = tempdir().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args([
"quantize",
"estimate",
"--size",
"1000000000",
"--to",
"q4_k_m",
])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
}
#[test]
fn test_cli_eval_record_list_suites() {
let dir = tempdir().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args([
"eval",
"record",
"my-model",
"--version",
"1",
"--suite",
"mmlu",
"--metric",
"accuracy=0.85",
"--unit",
"score",
])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["eval", "list", "my-model"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("mmlu").or(predicate::str::contains("my-model")));
aim()
.args(["eval", "suites"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("mmlu"));
}
#[test]
fn test_cli_backup_set_list_remove() {
let dir = tempdir().unwrap();
let backup_dir = tempdir().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args([
"backup",
"set",
"nightly",
"--frequency",
"daily",
"--max-backups",
"5",
"--output-dir",
backup_dir.path().to_str().unwrap(),
])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["backup", "list"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("nightly"));
aim()
.args(["backup", "remove", "nightly"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
}
#[test]
fn test_cli_backup_history_empty() {
let dir = tempdir().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["backup", "history"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
}
#[test]
fn test_cli_vaults_register_list_activate_deactivate() {
let dir = tempdir().unwrap();
let vault_dir = tempdir().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args([
"vaults",
"register",
"prod",
vault_dir.path().to_str().unwrap(),
"--description",
"Production vault",
])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["vaults", "list"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success()
.stdout(predicate::str::contains("prod"));
aim()
.args(["vaults", "activate", "prod"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["vaults", "deactivate"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["vaults", "unregister", "prod"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
}
#[test]
fn test_cli_quantize_set_missing_method() {
aim()
.args(["quantize", "set", "test-profile"])
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("error")));
}
#[test]
fn test_cli_eval_record_missing_args() {
aim()
.args(["eval", "record"])
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("error")));
}
#[test]
fn test_cli_backup_set_missing_args() {
aim()
.args(["backup", "set"])
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("error")));
}
#[test]
fn test_cli_vaults_register_missing_path() {
aim()
.args(["vaults", "register", "test-vault"])
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("error")));
}
#[test]
fn test_cli_eval_compare_missing_suite() {
aim()
.args(["eval", "compare", "a@1", "b@1"])
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("error")));
}
const TEST_PASS: &str = "cli-integration-passphrase";
#[test]
fn test_cli_roundtrip_with_passphrase_env() {
let dir = tempdir().unwrap();
let vault_dir = dir.path().to_str().unwrap();
let model = dir.path().join("model.safetensors");
std::fs::write(&model, b"fake-safetensors-payload").unwrap();
aim()
.args(["init", "--name", "roundtrip"])
.env("aimodelvault_HOME", vault_dir)
.assert()
.success();
aim()
.args(["store", "demo", model.to_str().unwrap()])
.env("aimodelvault_HOME", vault_dir)
.env("aimodelvault_PASSPHRASE", TEST_PASS)
.assert()
.success()
.stdout(predicate::str::contains("stored successfully"));
aim()
.args(["list"])
.env("aimodelvault_HOME", vault_dir)
.env("aimodelvault_PASSPHRASE", TEST_PASS)
.assert()
.success()
.stdout(predicate::str::contains("demo"));
let out = dir.path().join("retrieved.safetensors");
aim()
.args(["get", "demo", out.to_str().unwrap()])
.env("aimodelvault_HOME", vault_dir)
.env("aimodelvault_PASSPHRASE", TEST_PASS)
.assert()
.success();
assert_eq!(
std::fs::read(&out).unwrap(),
b"fake-safetensors-payload",
"retrieved model must match what was stored"
);
}
#[test]
fn test_cli_wrong_passphrase_fails() {
let dir = tempdir().unwrap();
let vault_dir = dir.path().to_str().unwrap();
let model = dir.path().join("m.bin");
std::fs::write(&model, b"payload").unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", vault_dir)
.assert()
.success();
aim()
.args(["store", "m", model.to_str().unwrap()])
.env("aimodelvault_HOME", vault_dir)
.env("aimodelvault_PASSPHRASE", TEST_PASS)
.assert()
.success();
let out = dir.path().join("out.bin");
aim()
.args(["get", "m", out.to_str().unwrap()])
.env("aimodelvault_HOME", vault_dir)
.env("aimodelvault_PASSPHRASE", "not-the-right-passphrase")
.assert()
.failure();
}
#[test]
fn test_cli_passphrase_via_kms_env_uri() {
let dir = tempdir().unwrap();
let vault_dir = dir.path().to_str().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", vault_dir)
.assert()
.success();
aim()
.args(["list"])
.env("aimodelvault_HOME", vault_dir)
.env("aimodelvault_PASSPHRASE", "env://AIM_CLI_TEST_SECRET")
.env("AIM_CLI_TEST_SECRET", TEST_PASS)
.assert()
.success();
}
#[test]
fn test_cli_passphrase_via_kms_file_uri() {
let dir = tempdir().unwrap();
let vault_dir = dir.path().to_str().unwrap();
let secret = dir.path().join("passphrase.txt");
std::fs::write(&secret, format!("{TEST_PASS}\n")).unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
std::fs::set_permissions(&secret, std::fs::Permissions::from_mode(0o600)).unwrap();
}
aim()
.args(["init"])
.env("aimodelvault_HOME", vault_dir)
.assert()
.success();
aim()
.args(["list"])
.env("aimodelvault_HOME", vault_dir)
.env(
"aimodelvault_PASSPHRASE",
format!("file://{}", secret.display()),
)
.assert()
.success();
}
#[test]
fn test_cli_unresolvable_kms_uri_fails() {
let dir = tempdir().unwrap();
let vault_dir = dir.path().to_str().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", vault_dir)
.assert()
.success();
aim()
.args(["list"])
.env("aimodelvault_HOME", vault_dir)
.env("aimodelvault_PASSPHRASE", "env://AIM_NOT_SET_ANYWHERE_42")
.assert()
.failure();
}
#[test]
fn test_cli_passphrase_via_stdin() {
let dir = tempdir().unwrap();
let vault_dir = dir.path().to_str().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", vault_dir)
.assert()
.success();
aim()
.args(["list"])
.env("aimodelvault_HOME", vault_dir)
.env_remove("aimodelvault_PASSPHRASE")
.write_stdin(format!("{TEST_PASS}\n"))
.assert()
.success();
}
#[test]
fn test_cli_list_empty_vault_non_interactive() {
let dir = tempdir().unwrap();
let vault_dir = dir.path().to_str().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", vault_dir)
.assert()
.success();
aim()
.args(["list"])
.env("aimodelvault_HOME", vault_dir)
.env("aimodelvault_PASSPHRASE", TEST_PASS)
.assert()
.success();
}
#[test]
fn test_cli_sign_verify_with_kms_key_uri() {
let dir = tempdir().unwrap();
let vault_dir = dir.path().to_str().unwrap();
let model = dir.path().join("model.bin");
std::fs::write(&model, b"contents to be signed").unwrap();
let seed = "a".repeat(64);
aim()
.args([
"sign",
"ignored",
"--file",
model.to_str().unwrap(),
"--key",
"env://AIM_TEST_SIGN_KEY",
])
.env("aimodelvault_HOME", vault_dir)
.env("AIM_TEST_SIGN_KEY", &seed)
.assert()
.success()
.stdout(predicate::str::contains("KMS"));
let sig = model.with_extension("sig");
assert!(
sig.exists(),
"detached signature should be written next to the file"
);
aim()
.args([
"verify",
"ignored",
"--file",
model.to_str().unwrap(),
"--signature",
sig.to_str().unwrap(),
"--key",
"env://AIM_TEST_SIGN_KEY",
])
.env("aimodelvault_HOME", vault_dir)
.env("AIM_TEST_SIGN_KEY", &seed)
.assert()
.success()
.stdout(predicate::str::contains("Verification PASSED"));
}
#[test]
fn test_cli_sign_rejects_bad_kms_key() {
let dir = tempdir().unwrap();
let model = dir.path().join("m.bin");
std::fs::write(&model, b"data").unwrap();
aim()
.args([
"sign",
"ignored",
"--file",
model.to_str().unwrap(),
"--key",
"env://AIM_TEST_BAD_KEY",
])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.env("AIM_TEST_BAD_KEY", "not-a-valid-seed")
.assert()
.failure();
}
#[test]
fn test_cli_convert_resolves_stored_format() {
let dir = tempdir().unwrap();
let vault_dir = dir.path().to_str().unwrap();
let model = dir.path().join("m.pt");
let mut bytes = vec![0x50, 0x4b, 0x03, 0x04];
bytes.extend_from_slice(&[0u8; 60]);
std::fs::write(&model, &bytes).unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", vault_dir)
.assert()
.success();
aim()
.args(["store", "demo", model.to_str().unwrap()])
.env("aimodelvault_HOME", vault_dir)
.env("aimodelvault_PASSPHRASE", TEST_PASS)
.assert()
.success();
let work = dir.path().join("work");
std::fs::create_dir_all(&work).unwrap();
aim()
.args(["convert", "demo", "--to-format", "onnx"])
.current_dir(&work)
.env("aimodelvault_HOME", vault_dir)
.env("aimodelvault_PASSPHRASE", TEST_PASS)
.assert()
.success()
.stdout(predicate::str::contains("Source format: PyTorch"))
.stdout(predicate::str::contains("No conversion path").not());
}
#[test]
fn test_cli_convert_writes_plan_not_fake_target_file() {
let dir = tempdir().unwrap();
let vault_dir = dir.path().to_str().unwrap();
let model = dir.path().join("m.pt");
let mut bytes = vec![0x50, 0x4b, 0x03, 0x04];
bytes.extend_from_slice(&[0u8; 60]);
std::fs::write(&model, &bytes).unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", vault_dir)
.assert()
.success();
aim()
.args(["store", "demo", model.to_str().unwrap()])
.env("aimodelvault_HOME", vault_dir)
.env("aimodelvault_PASSPHRASE", TEST_PASS)
.assert()
.success();
let work = dir.path().join("work");
std::fs::create_dir_all(&work).unwrap();
aim()
.args(["convert", "demo", "--to-format", "onnx"])
.current_dir(&work)
.env("aimodelvault_HOME", vault_dir)
.env("aimodelvault_PASSPHRASE", TEST_PASS)
.assert()
.success()
.stdout(predicate::str::contains("No ONNX file was produced"));
assert!(
work.join("demo_converted.plan.json").exists(),
"the plan should be written alongside the requested output"
);
assert!(
!work.join("demo_converted.onnx").exists(),
"no .onnx file may be produced when nothing was converted"
);
}
fn write_gguf(path: &std::path::Path, tensors: &[(&str, &[u64], u32)]) {
fn push_str(out: &mut Vec<u8>, s: &str) {
out.extend_from_slice(&(s.len() as u64).to_le_bytes());
out.extend_from_slice(s.as_bytes());
}
let mut data = Vec::new();
data.extend_from_slice(b"GGUF");
data.extend_from_slice(&3u32.to_le_bytes());
data.extend_from_slice(&(tensors.len() as u64).to_le_bytes());
data.extend_from_slice(&2u64.to_le_bytes());
push_str(&mut data, "general.architecture");
data.extend_from_slice(&8u32.to_le_bytes()); push_str(&mut data, "llama");
push_str(&mut data, "tokenizer.ggml.tokens");
data.extend_from_slice(&9u32.to_le_bytes()); data.extend_from_slice(&8u32.to_le_bytes()); data.extend_from_slice(&2u64.to_le_bytes());
push_str(&mut data, "a");
push_str(&mut data, "bb");
for (name, dims, ggml_type) in tensors {
push_str(&mut data, name);
data.extend_from_slice(&(dims.len() as u32).to_le_bytes());
for d in *dims {
data.extend_from_slice(&d.to_le_bytes());
}
data.extend_from_slice(&ggml_type.to_le_bytes());
data.extend_from_slice(&0u64.to_le_bytes()); }
std::fs::write(path, data).unwrap();
}
#[test]
fn test_cli_diff_gguf_reports_real_tensor_changes() {
let dir = tempdir().unwrap();
let left = dir.path().join("left.gguf");
let right = dir.path().join("right.gguf");
write_gguf(
&left,
&[
("blk.0.attn_q.weight", &[4096, 4096], 0), ("blk.0.attn_k.weight", &[4096, 1024], 0),
("output_norm.weight", &[4096], 0),
],
);
write_gguf(
&right,
&[
("blk.0.attn_q.weight", &[4096, 4096], 12), ("blk.0.attn_k.weight", &[4096, 1024], 12),
("blk.0.ffn_gate.weight", &[4096, 11008], 12),
],
);
aim()
.args(["diff", left.to_str().unwrap(), right.to_str().unwrap()])
.assert()
.success()
.stdout(predicate::str::contains("added: 1"))
.stdout(predicate::str::contains("removed: 1"))
.stdout(predicate::str::contains("changed: 2"))
.stdout(predicate::str::contains("blk.0.ffn_gate.weight"))
.stdout(predicate::str::contains("F32 → Q4_K"))
.stdout(predicate::str::contains("output_norm.weight"));
}
#[test]
fn test_cli_diff_truncated_gguf_is_handled() {
let dir = tempdir().unwrap();
let full = dir.path().join("full.gguf");
write_gguf(&full, &[("a", &[2, 2], 0), ("b", &[2, 2], 0)]);
let bytes = std::fs::read(&full).unwrap();
let cut = dir.path().join("cut.gguf");
std::fs::write(&cut, &bytes[..bytes.len() - 12]).unwrap();
aim()
.args(["diff", full.to_str().unwrap(), cut.to_str().unwrap()])
.assert()
.success();
}
#[test]
fn test_cli_exit_code_invalid_input() {
aim()
.args(["introspect", "--format", "not-a-format"])
.assert()
.code(6)
.stderr(predicate::str::contains("Unknown format"));
}
#[test]
fn test_cli_exit_code_not_found() {
let dir = tempdir().unwrap();
aim()
.args(["init"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.assert()
.success();
aim()
.args(["versions", "no-such-model"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.env("aimodelvault_PASSPHRASE", "correct horse battery staple")
.timeout(std::time::Duration::from_secs(30))
.assert()
.code(3);
}
#[test]
fn test_cli_exit_code_usage_error_is_not_confused_with_auth_failure() {
aim()
.args(["no-such-subcommand"])
.assert()
.code(6)
.stderr(predicate::str::contains("unrecognized subcommand"));
aim().args(["versions", "--no-such-flag"]).assert().code(6);
aim().args(["versions"]).assert().code(6);
aim().assert().code(6);
}
#[test]
fn test_cli_exit_code_help_and_version_are_success() {
aim().args(["--help"]).assert().code(0);
aim().args(["--version"]).assert().code(0);
aim().args(["versions", "--help"]).assert().code(0);
}
#[test]
fn test_cli_exit_code_config_error() {
let dir = tempdir().unwrap();
let bad = dir.path().join("config.yaml");
std::fs::write(&bad, "dirs:\n\t data_dir: /tmp\n").unwrap();
aim()
.args(["--config", bad.to_str().unwrap(), "list"])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.timeout(std::time::Duration::from_secs(30))
.assert()
.code(7);
aim()
.args([
"--config",
dir.path().join("absent.yaml").to_str().unwrap(),
"list",
])
.env("aimodelvault_HOME", dir.path().to_str().unwrap())
.timeout(std::time::Duration::from_secs(30))
.assert()
.code(7);
}
#[test]
fn test_cli_exit_code_verify_failure() {
let dir = tempdir().unwrap();
let model = dir.path().join("model.bin");
std::fs::write(&model, b"the real payload").unwrap();
let key = dir.path().join("signing_key.json");
aim()
.args([
"sign",
"model",
"--file",
model.to_str().unwrap(),
"--key",
key.to_str().unwrap(),
])
.assert()
.success();
let sig = dir.path().join("model.sig");
assert!(sig.exists(), "expected a detached signature at {sig:?}");
std::fs::write(&model, b"the WRONG payload").unwrap();
aim()
.args([
"verify",
"model",
"--signature",
sig.to_str().unwrap(),
"--file",
model.to_str().unwrap(),
"--key",
key.to_str().unwrap(),
])
.assert()
.code(5)
.stdout(predicate::str::contains("FAILED"));
}
#[test]
fn test_cli_exit_code_verify_without_key_is_not_success() {
let dir = tempdir().unwrap();
let model = dir.path().join("model.bin");
std::fs::write(&model, b"payload").unwrap();
let key = dir.path().join("signing_key.json");
aim()
.args([
"sign",
"model",
"--file",
model.to_str().unwrap(),
"--key",
key.to_str().unwrap(),
])
.assert()
.success();
let sig = dir.path().join("model.sig");
aim()
.args([
"verify",
"model",
"--signature",
sig.to_str().unwrap(),
"--file",
model.to_str().unwrap(),
])
.assert()
.code(5)
.stdout(predicate::str::contains("NOT CHECKED"));
}
#[test]
fn test_cli_exit_code_success_is_zero() {
aim().args(["--version"]).assert().code(0);
aim()
.args(["introspect", "--format", "json"])
.assert()
.code(0);
}
#[test]
fn test_published_exit_code_tables_match_the_implementation() {
use ai_model_vault::{
EXIT_AUTH, EXIT_COMPLIANCE, EXIT_CONFIG, EXIT_GENERAL, EXIT_INTEGRITY, EXIT_INVALID_INPUT,
EXIT_NOT_FOUND, EXIT_PERMISSION, EXIT_SUCCESS,
};
let expected: Vec<(String, u8)> = vec![
("0".to_string(), EXIT_SUCCESS),
("1".to_string(), EXIT_GENERAL),
("2".to_string(), EXIT_AUTH),
("3".to_string(), EXIT_NOT_FOUND),
("4".to_string(), EXIT_PERMISSION),
("5".to_string(), EXIT_INTEGRITY),
("6".to_string(), EXIT_INVALID_INPUT),
("7".to_string(), EXIT_CONFIG),
("8".to_string(), EXIT_COMPLIANCE),
];
for (key, code) in &expected {
assert_eq!(key, &code.to_string(), "constant drifted from its own key");
}
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
let agents: serde_json::Value = serde_json::from_str(
&std::fs::read_to_string(root.join(".well-known/agents.json")).unwrap(),
)
.unwrap();
let cli_iface = agents["agent_interfaces"]
.as_array()
.unwrap()
.iter()
.find(|i| i["type"] == "cli")
.expect("agents.json must describe the cli interface");
let published = cli_iface["exit_codes"].as_object().unwrap();
assert_eq!(
published.len(),
expected.len(),
"agents.json publishes {} codes, the implementation defines {}",
published.len(),
expected.len()
);
for (key, _) in &expected {
assert!(
published.contains_key(key),
"agents.json is missing exit code {key}"
);
}
let ontology: serde_json::Value = serde_json::from_str(
&std::fs::read_to_string(root.join(".well-known/ontology.jsonld")).unwrap(),
)
.unwrap();
let contract = ontology["aimv:errors"]["@exitCodeContract"]
.as_object()
.expect("ontology.jsonld must publish aimv:errors.@exitCodeContract");
for (key, _) in &expected {
assert!(
contract.contains_key(key),
"ontology.jsonld is missing exit code {key}"
);
}
let taxonomy = ontology["aimv:errors"]["errorTypes"]
.as_array()
.expect("ontology.jsonld must list aimv:errors.errorTypes");
let valid: Vec<u8> = expected.iter().map(|(_, c)| *c).collect();
for entry in taxonomy {
let code = u8::try_from(entry["exit_code"].as_u64().unwrap()).unwrap();
assert!(
valid.contains(&code),
"{} claims exit code {code}, which is not in the contract",
entry["rdfs:label"]
);
assert_ne!(
code, EXIT_SUCCESS,
"{} is an error and must not claim exit 0",
entry["rdfs:label"]
);
}
}
#[test]
fn test_well_known_manifests_declare_the_crate_version() {
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
let crate_version = env!("CARGO_PKG_VERSION");
let agents: serde_json::Value = serde_json::from_str(
&std::fs::read_to_string(root.join(".well-known/agents.json")).unwrap(),
)
.unwrap();
assert_eq!(
agents["project"]["version"].as_str(),
Some(crate_version),
".well-known/agents.json project.version drifted from Cargo.toml"
);
let mcp: serde_json::Value = serde_json::from_str(
&std::fs::read_to_string(root.join(".well-known/mcp-manifest.json")).unwrap(),
)
.unwrap();
assert_eq!(
mcp["version"].as_str(),
Some(crate_version),
".well-known/mcp-manifest.json version drifted from Cargo.toml"
);
let openapi = std::fs::read_to_string(root.join(".well-known/openapi.yaml")).unwrap();
let declared = openapi
.lines()
.find(|l| l.starts_with(" version:"))
.map(|l| l.trim_start_matches(" version:").trim().to_string())
.expect("openapi.yaml has no top-level info.version");
assert_eq!(
declared, crate_version,
".well-known/openapi.yaml info.version drifted from Cargo.toml"
);
}
#[test]
fn test_manifests_do_not_advertise_removed_gcs_support() {
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
for name in [
".well-known/ai-plugin.json",
".well-known/agents.json",
".well-known/mcp-manifest.json",
] {
let body = std::fs::read_to_string(root.join(name)).unwrap();
assert!(
!body.contains("GCS") && !body.contains("Google Cloud Storage"),
"{name} advertises GCS, which `aim cloud` cannot do"
);
}
}
#[test]
fn test_manifest_python_install_uses_the_real_pypi_name() {
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
let agents: serde_json::Value = serde_json::from_str(
&std::fs::read_to_string(root.join(".well-known/agents.json")).unwrap(),
)
.unwrap();
let pypi_name = {
let pyproject = std::fs::read_to_string(root.join("pyproject.toml")).unwrap();
pyproject
.lines()
.find(|l| l.trim_start().starts_with("name"))
.and_then(|l| l.split('=').nth(1))
.map(|v| v.trim().trim_matches('"').to_string())
.expect("pyproject.toml has no name")
};
let install = agents["agent_interfaces"]
.as_array()
.unwrap()
.iter()
.find(|i| i["type"] == "python_bindings")
.and_then(|i| i["install"].as_str())
.expect("no python_bindings interface with an install command");
assert_eq!(
install,
format!("pip install {pypi_name}"),
"agents.json python install command does not match pyproject.toml name"
);
}
#[test]
fn test_manifest_does_not_advertise_unsupported_azure_shared_key() {
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
let agents = std::fs::read_to_string(root.join(".well-known/agents.json")).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&agents).unwrap();
for iface in parsed["agent_interfaces"].as_array().unwrap() {
if let Some(vars) = iface["environment_variables"].as_array() {
for v in vars {
let name = v["name"].as_str().unwrap_or_default();
assert_ne!(
name, "AZURE_STORAGE_KEY",
"agents.json lists AZURE_STORAGE_KEY, which the Azure v1 SDK cannot use"
);
}
}
}
}
#[test]
fn test_wired_capabilities_list_their_commands() {
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
let agents = std::fs::read_to_string(root.join(".well-known/agents.json")).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&agents).unwrap();
for name in ["federation", "blockchain_audit"] {
let cap = &parsed["capabilities"][name];
let commands = cap["commands"].as_array().unwrap_or_else(|| {
panic!("agents.json capability '{name}' is wired to the CLI but lists no commands")
});
assert!(
!commands.is_empty(),
"agents.json capability '{name}' has an empty commands list"
);
}
}