mod infra;
use infra::{
assert_output_contains, assert_output_excludes, run, run_locked, run_update_selected,
run_update_selected_error, FetchResult,
};
#[test]
fn single_file_dep() {
let result = run("single_file_dep");
assert_output_contains(&result, &["proto/hello.proto"]);
}
#[test]
fn two_repos_transitive_dep() {
let result = run("two_repos_transitive_dep");
assert_output_contains(&result, &["proto/b.proto", "proto/v1.proto"]);
assert_output_excludes(&result, &["proto/v2.proto"]);
}
#[test]
fn transitive_dep_only() {
let result = run("transitive_dep_only");
assert_output_contains(&result, &["proto/a.proto", "proto/b.proto"]);
}
#[test]
fn circular_dependencies() {
let result = run("circular_dependencies");
assert_output_contains(&result, &["a.proto", "b.proto"]);
}
#[test]
fn circular_dependencies_with_content_roots() {
let result = run("circular_dependencies_with_content_roots");
assert_output_contains(&result, &["bar.proto", "b.proto", "foo.proto"]);
}
#[test]
fn circular_dependencies_with_prune() {
let result = run("circular_dependencies_with_prune");
assert_output_contains(&result, &["bar/bar.proto"]);
}
#[test]
fn locked_mode_uses_pinned_commit() {
let result = run_locked("locked_mode_uses_pinned_commit");
assert_output_contains(&result, &["proto/v1.proto"]);
assert_output_excludes(&result, &["proto/v2.proto"]);
}
#[test]
fn allow_policies_apply_only_to_own_dependency() {
let result = run("allow_policies_apply_only_to_own_dependency");
assert_output_contains(
&result,
&[
"public/service.proto",
"public/child.proto",
"internal/child.proto",
],
);
assert_output_excludes(&result, &["internal/admin.proto"]);
}
#[test]
fn allow_policies_with_prune_include_import_tree() {
let result = run("allow_policies_with_prune_include_import_tree");
assert_output_contains(
&result,
&[
"public/service.proto",
"internal/common.proto",
"shared.proto",
],
);
assert_output_excludes(&result, &["internal/admin.proto", "unused.proto"]);
}
#[test]
fn content_roots() {
let result = run("content_roots");
assert_output_contains(&result, &["model.proto", "service.proto"]);
assert_output_excludes(
&result,
&["api/proto/model.proto", "api/proto/service.proto"],
);
}
#[test]
fn deny_policies_apply_to_dependency_subtree() {
let result = run("deny_policies_apply_to_dependency_subtree");
assert_output_contains(&result, &["public/service.proto", "public/child.proto"]);
assert_output_excludes(&result, &["internal/admin.proto", "internal/child.proto"]);
}
#[test]
fn deny_policies_with_prune_exclude_matching_files_and_deps() {
let result = run("deny_policies_with_prune_exclude_matching_files_and_deps");
assert_output_contains(&result, &["public/service.proto", "shared/public.proto"]);
assert_output_excludes(&result, &["internal/admin.proto", "shared/secret.proto"]);
}
#[test]
fn revision_pin() {
let result = run("revision_pin");
assert_output_contains(&result, &["proto/v1.proto"]);
assert_output_excludes(&result, &["proto/v2.proto"]);
}
#[test]
fn transitive_flag() {
let result = run("transitive_flag");
assert_output_contains(&result, &["a.proto", "shared.proto"]);
assert_output_excludes(&result, &["unused.proto"]);
}
#[test]
fn transitive_cross_dependencies() {
let result = run("transitive_cross_dependencies");
assert_output_contains(
&result,
&["b_1.proto", "b_2.proto", "c_1.proto", "c_2.proto"],
);
}
#[test]
fn regex_policy() {
let result = run("regex_policy");
assert_output_contains(&result, &["service.proto"]);
assert_output_excludes(&result, &["internal.proto"]);
}
#[test]
fn partial_lock_update() {
let result = run("partial_lock_update");
assert_output_contains(&result, &["proto/b.proto", "proto/v1.proto"]);
assert_output_excludes(&result, &["proto/v2.proto"]);
}
#[test]
fn selected_lock_update() {
let result = run_update_selected("selected_lock_update", "repo1", None);
assert_lockfile_dependency_commit(&result, "repo1", "<commit:main:2>");
assert_lockfile_dependency_commit(&result, "repo2", "<commit:main:3>");
}
#[test]
fn selected_precise_lock_update() {
let result = run_update_selected(
"selected_precise_lock_update",
"repo1",
Some("<commit:main:2>"),
);
assert_lockfile_dependency_commit(&result, "repo1", "<commit:main:2>");
assert_lockfile_dependency_commit(&result, "repo2", "<commit:main:4>");
}
#[test]
fn selected_precise_lock_update_rejects_invalid_commit_hash() {
let error = run_update_selected_error(
"selected_precise_revision_mismatch",
"repo1",
"not-a-commit",
);
assert!(
error.contains("Invalid commit hash not-a-commit"),
"unexpected error: {error}"
);
}
#[test]
fn selected_precise_lock_update_rejects_missing_commit() {
let commit = "1111111111111111111111111111111111111111";
let error = run_update_selected_error("selected_precise_revision_mismatch", "repo1", commit);
assert!(
error.contains(&format!("Commit {commit} was not found")),
"unexpected error: {error}"
);
}
#[test]
fn selected_precise_lock_update_rejects_commit_from_another_branch() {
let error = run_update_selected_error(
"selected_precise_revision_mismatch",
"repo1",
"<commit:side:1>",
);
assert!(
error.contains("does not belong to the branch main"),
"unexpected error: {error}"
);
}
#[test]
fn selected_precise_lock_update_rejects_revision_mismatch() {
let error = run_update_selected_error(
"selected_precise_revision_mismatch",
"repo1",
"<commit:main:2>",
);
assert!(
error.contains("does not match revision"),
"unexpected error: {error}"
);
}
fn assert_lockfile_dependency_commit(result: &FetchResult, name: &str, commit: &str) {
let snapshot = result.snapshot_lockfile();
let dependency = snapshot
.split("\n\n")
.find(|dependency| {
dependency.contains("[[dependencies]]")
&& dependency.contains(&format!("name = \"{name}\""))
})
.unwrap_or_else(|| panic!("expected lockfile dependency {name}\n\n{snapshot}"));
assert!(
dependency.contains(&format!("commit_hash = \"{commit}\"")),
"expected {name} to be locked to {commit}\n\n{dependency}"
);
}
#[test]
fn prune_mixed_true_and_false_rules() {
let result = run("prune_mixed_true_and_false_rules");
assert_output_contains(
&result,
&[
"extra.proto",
"imported.proto",
"other.proto",
"service.proto",
],
);
}
#[test]
fn transitive_flag_mixed_with_normal_transitive_dep() {
let result = run("transitive_flag_mixed_with_normal_transitive_dep");
assert_output_contains(&result, &["consumer.proto", "shared.proto"]);
}
#[test]
fn allow_policies_merged_across_duplicate_deps() {
let result = run("allow_policies_merged_across_duplicate_deps");
assert_output_contains(
&result,
&[
"foo.proto",
"from_foo/model.proto",
"from_root/service.proto",
],
);
}
#[test]
fn duplicate_dep_keeps_content_roots_and_policies_coupled() {
let result = run("duplicate_dep_keeps_content_roots_and_policies_coupled");
assert_output_contains(
&result,
&["bar.proto", "consumer.proto", "nested/foo.proto"],
);
}
#[test]
fn duplicate_dep_same_file_under_different_content_roots() {
let result = run("duplicate_dep_same_file_under_different_content_roots");
assert_output_contains(&result, &["consumer.proto", "nested/shared.proto"]);
}