use serde_json::Value;
use std::path::PathBuf;
use std::process::Command;
fn fixture_index_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("..")
.join("dotrepo-core")
.join("tests")
.join("fixtures")
.join("public-export")
.join("fixture-index")
}
fn dotrepo_bin() -> &'static str {
env!("CARGO_BIN_EXE_dotrepo")
}
fn run_public(args: &[&str]) -> std::process::Output {
Command::new(dotrepo_bin())
.args(args)
.output()
.expect("dotrepo command runs")
}
fn parse_stdout_json(output: &std::process::Output) -> Value {
serde_json::from_slice(&output.stdout).expect("stdout contains json")
}
#[test]
fn public_query_success_prints_json_to_stdout() {
let index_root = fixture_index_root();
let output = run_public(&[
"public",
"query",
"github.com",
"example",
"nova",
"repo.description",
"--index-root",
index_root.to_str().expect("fixture path is utf-8"),
]);
assert!(output.status.success(), "command should succeed");
assert!(output.stderr.is_empty(), "success should not write stderr");
let json = parse_stdout_json(&output);
assert_eq!(json["apiVersion"], Value::String("v0".into()));
assert_eq!(
json["selection"]["record"]["claim"]["handoff"],
Value::String("pending_canonical".into())
);
}
#[test]
fn public_summary_honors_base_path_in_links() {
let index_root = fixture_index_root();
let output = run_public(&[
"public",
"summary",
"github.com",
"example",
"orbit",
"--index-root",
index_root.to_str().expect("fixture path is utf-8"),
"--base-path",
"/dotrepo",
]);
assert!(output.status.success(), "command should succeed");
assert!(output.stderr.is_empty(), "success should not write stderr");
let json = parse_stdout_json(&output);
assert_eq!(
json["links"]["self"],
Value::String("/dotrepo/v0/repos/github.com/example/orbit/index.json".into())
);
assert_eq!(
json["links"]["trust"],
Value::String("/dotrepo/v0/repos/github.com/example/orbit/trust.json".into())
);
}
#[test]
fn public_profile_success_prints_compact_research_shape() {
let index_root = fixture_index_root();
let output = run_public(&[
"public",
"profile",
"github.com",
"example",
"orbit",
"--index-root",
index_root.to_str().expect("fixture path is utf-8"),
"--base-path",
"/dotrepo",
]);
assert!(output.status.success(), "command should succeed");
assert!(output.stderr.is_empty(), "success should not write stderr");
let json = parse_stdout_json(&output);
assert_eq!(json["apiVersion"], Value::String("v0".into()));
assert_eq!(
json["purpose"],
Value::String("Reviewed orbital tooling metadata.".into())
);
assert_eq!(json["completeness"]["hasDocs"], Value::Bool(true));
assert_eq!(
json["trust"]["selectedStatus"],
Value::String("reviewed".into())
);
assert_eq!(
json["synthesis"]["synthesisPath"],
Value::String("repos/github.com/example/orbit/synthesis.toml".into())
);
assert_eq!(
json["synthesis"]["architecture"]["summary"],
Value::String(
"Orbit exposes reviewed metadata and documentation entry points for agents.".into()
)
);
assert_eq!(
json["synthesis"]["forAgents"]["howToBuild"],
Value::String("unknown".into())
);
assert!(
json.get("repo").is_none(),
"synthesis must not overwrite factual profile fields"
);
assert_eq!(
json["links"]["repository"],
Value::String("/dotrepo/v0/repos/github.com/example/orbit/index.json".into())
);
}
#[test]
fn public_batch_profiles_reports_successes_and_item_errors() {
let index_root = fixture_index_root();
let output = run_public(&[
"public",
"batch-profiles",
"--repo",
"github.com/example/orbit",
"--repo",
"github.com/missing/repo",
"--index-root",
index_root.to_str().expect("fixture path is utf-8"),
]);
assert!(output.status.success(), "batch envelope should succeed");
assert!(output.stderr.is_empty(), "success should not write stderr");
let json = parse_stdout_json(&output);
assert_eq!(json["apiVersion"], Value::String("v0".into()));
assert_eq!(json["resultCount"], Value::Number(2.into()));
assert_eq!(
json["results"][0]["profile"]["purpose"],
Value::String("Reviewed orbital tooling metadata.".into())
);
assert_eq!(
json["results"][1]["error"]["code"],
Value::String("repository_not_found".into())
);
}
#[test]
fn public_batch_query_reports_multiple_paths_and_item_errors() {
let index_root = fixture_index_root();
let output = run_public(&[
"public",
"batch-query",
"--repo",
"https://github.com/example/orbit",
"--path",
"repo.description",
"--path",
"repo.missing_field",
"--index-root",
index_root.to_str().expect("fixture path is utf-8"),
]);
assert!(output.status.success(), "batch envelope should succeed");
assert!(output.stderr.is_empty(), "success should not write stderr");
let json = parse_stdout_json(&output);
assert_eq!(json["repositoryCount"], Value::Number(1.into()));
assert_eq!(json["pathCount"], Value::Number(2.into()));
assert_eq!(json["resultCount"], Value::Number(2.into()));
assert_eq!(
json["results"][0]["query"]["value"],
Value::String("Reviewed orbital tooling metadata.".into())
);
assert_eq!(
json["results"][1]["error"]["code"],
Value::String("query_path_not_found".into())
);
}
#[test]
fn public_search_filters_profiles() {
let index_root = fixture_index_root();
let output = run_public(&[
"public",
"search",
"--q",
"orbit",
"--status",
"reviewed",
"--require-docs",
"--require-security-contact",
"--limit",
"5",
"--index-root",
index_root.to_str().expect("fixture path is utf-8"),
]);
assert!(output.status.success(), "search envelope should succeed");
assert!(output.stderr.is_empty(), "success should not write stderr");
let json = parse_stdout_json(&output);
assert_eq!(json["apiVersion"], Value::String("v0".into()));
assert_eq!(json["query"], Value::String("orbit".into()));
assert_eq!(json["matchedCount"], Value::Number(1.into()));
assert_eq!(json["returnedCount"], Value::Number(1.into()));
assert_eq!(
json["results"][0]["identity"]["repo"],
Value::String("orbit".into())
);
assert_eq!(
json["results"][0]["matched"][0],
Value::String("identity".into())
);
assert_eq!(
json["results"][0]["ranking"]["matchedFieldCount"],
Value::Number(4.into())
);
assert_eq!(
json["results"][0]["ranking"]["basis"][0],
Value::String("matchedFields".into())
);
assert!(
json["results"][0]["ranking"]["trust"].is_null(),
"ranking metadata must remain separate from trust"
);
assert_eq!(json["filters"]["requireDocs"], Value::Bool(true));
}
#[test]
fn public_compare_reports_side_by_side_signals() {
let index_root = fixture_index_root();
let output = run_public(&[
"public",
"compare",
"--repo",
"github.com/example/orbit",
"--repo",
"github.com/example/nova",
"--base-path",
"/dotrepo",
"--index-root",
index_root.to_str().expect("fixture path is utf-8"),
]);
assert!(output.status.success(), "compare envelope should succeed");
assert!(output.stderr.is_empty(), "success should not write stderr");
let json = parse_stdout_json(&output);
assert_eq!(json["apiVersion"], Value::String("v0".into()));
assert_eq!(json["repositoryCount"], Value::Number(2.into()));
assert_eq!(
json["results"][0]["identity"]["repo"],
Value::String("orbit".into())
);
assert_eq!(
json["results"][0]["links"]["self"],
Value::String("/dotrepo/v0/repos/github.com/example/orbit/profile.json".into())
);
assert_eq!(
json["signals"]["selectedStatuses"][0]["value"],
Value::String("reviewed".into())
);
assert_eq!(json["signals"]["hasDocs"][0]["value"], Value::Bool(true));
assert_eq!(json["signals"]["hasDocs"][1]["value"], Value::Bool(false));
}
#[test]
fn public_relations_resolves_referenced_profiles() {
let index_root = fixture_index_root();
let output = run_public(&[
"public",
"relations",
"github.com",
"example",
"orbit",
"--base-path",
"/dotrepo",
"--index-root",
index_root.to_str().expect("fixture path is utf-8"),
]);
assert!(output.status.success(), "relations response should succeed");
assert!(output.stderr.is_empty(), "success should not write stderr");
let json = parse_stdout_json(&output);
assert_eq!(json["apiVersion"], Value::String("v0".into()));
assert_eq!(json["relationCount"], Value::Number(1.into()));
assert_eq!(
json["references"][0]["target"],
Value::String("github.com/example/nova".into())
);
assert_eq!(
json["references"][0]["relationship"],
Value::String("reference".into())
);
assert_eq!(
json["references"][0]["direction"],
Value::String("outgoing".into())
);
assert_eq!(
json["references"][0]["identity"]["repo"],
Value::String("nova".into())
);
assert_eq!(
json["references"][0]["profile"]["links"]["self"],
Value::String("/dotrepo/v0/repos/github.com/example/nova/profile.json".into())
);
}
#[test]
fn public_relations_reports_reverse_references() {
let index_root = fixture_index_root();
let output = run_public(&[
"public",
"relations",
"github.com",
"example",
"nova",
"--base-path",
"/dotrepo",
"--index-root",
index_root.to_str().expect("fixture path is utf-8"),
]);
assert!(output.status.success(), "relations response should succeed");
assert!(output.stderr.is_empty(), "success should not write stderr");
let json = parse_stdout_json(&output);
assert_eq!(json["apiVersion"], Value::String("v0".into()));
assert_eq!(json["relationCount"], Value::Number(1.into()));
assert_eq!(
json["references"][0]["relationship"],
Value::String("referenced_by".into())
);
assert_eq!(
json["references"][0]["direction"],
Value::String("incoming".into())
);
assert_eq!(
json["references"][0]["target"],
Value::String("github.com/example/orbit".into())
);
assert_eq!(
json["references"][0]["profile"]["links"]["self"],
Value::String("/dotrepo/v0/repos/github.com/example/orbit/profile.json".into())
);
}
#[test]
fn public_query_missing_path_prints_json_error_and_exit_code_1() {
let index_root = fixture_index_root();
let output = run_public(&[
"public",
"query",
"github.com",
"example",
"orbit",
"repo.missing_field",
"--index-root",
index_root.to_str().expect("fixture path is utf-8"),
]);
assert_eq!(output.status.code(), Some(1));
assert!(
output.stderr.is_empty(),
"public error json should not be duplicated on stderr"
);
let json = parse_stdout_json(&output);
assert_eq!(
json["error"]["code"],
Value::String("query_path_not_found".into())
);
assert_eq!(json["path"], Value::String("repo.missing_field".into()));
}
#[test]
fn public_summary_missing_repo_prints_json_error_and_exit_code_1() {
let index_root = fixture_index_root();
let output = run_public(&[
"public",
"summary",
"github.com",
"missing",
"repo",
"--index-root",
index_root.to_str().expect("fixture path is utf-8"),
]);
assert_eq!(output.status.code(), Some(1));
assert!(
output.stderr.is_empty(),
"public error json should not be duplicated on stderr"
);
let json = parse_stdout_json(&output);
assert_eq!(
json["error"]["code"],
Value::String("repository_not_found".into())
);
assert!(
json.get("path").is_none(),
"summary failures should not include a query path"
);
}
#[test]
fn public_trust_invalid_identity_prints_json_error_and_exit_code_1() {
let index_root = fixture_index_root();
let output = run_public(&[
"public",
"trust",
"github.com",
"example/nested",
"orbit",
"--index-root",
index_root.to_str().expect("fixture path is utf-8"),
]);
assert_eq!(output.status.code(), Some(1));
assert!(
output.stderr.is_empty(),
"public error json should not be duplicated on stderr"
);
let json = parse_stdout_json(&output);
assert_eq!(
json["error"]["code"],
Value::String("invalid_repository_identity".into())
);
assert!(
json.get("path").is_none(),
"trust failures should not include a query path"
);
}