use paper_gap::{analyse, repos::Repository};
fn repo(url: &str) -> Repository {
Repository {
forge: "github".into(),
url: url.into(),
owner: "a".into(),
name: "b".into(),
description: None,
primary_language: Some("Rust".into()),
stars: Some(1),
forks: Some(0),
license: Some("MIT".into()),
archived: Some(false),
default_branch: Some("main".into()),
last_commit_date: Some("2026-01-01T00:00:00Z".into()),
source_provider: "github".into(),
}
}
#[test]
fn deduplicates_repositories_by_normalized_url() {
let repos = analyse::dedup_repositories(vec![
repo("https://github.com/a/b"),
repo("https://github.com/a/b/"),
repo("https://github.com/a/b.git"),
]);
assert_eq!(repos.len(), 1);
}
#[test]
fn scores_no_public_implementation() {
let assessment = analyse::assess(&[], &[]);
assert_eq!(assessment.components.no_code_score, 50);
assert_eq!(assessment.implementation_gap_score, 50);
assert_eq!(assessment.gaps.len(), 2);
}
#[test]
fn scores_unclear_license_when_all_repos_lack_license() {
let mut repository = repo("https://github.com/a/b");
repository.license = None;
let assessment = analyse::assess(&[], &[repository]);
assert_eq!(assessment.components.license_gap_score, 10);
assert!(assessment.implementation_gap_score >= 10);
}
#[test]
fn detects_archived_repository() {
let mut repository = repo("https://github.com/a/b");
repository.archived = Some(true);
let analysis = analyse::analyze_repository(&repository);
assert!(analysis.is_archived);
let assessment = analyse::assess(&[], &[repository]);
assert_eq!(assessment.components.stale_repo_score, 15);
}
#[test]
fn detects_quality_signals_from_paths() {
let paths = vec![
"README.md".to_string(),
"LICENSE".to_string(),
".github/workflows/ci.yml".to_string(),
"Cargo.toml".to_string(),
"tests/integration.rs".to_string(),
"docs/usage.md".to_string(),
"examples/demo.rs".to_string(),
"scripts/reproduce.sh".to_string(),
"benches/main.rs".to_string(),
];
let analysis = analyse::analyze_repository_with_paths(&repo("https://github.com/a/b"), &paths);
assert!(analysis.has_readme);
assert!(analysis.has_license);
assert!(analysis.has_ci);
assert!(analysis.has_package_manifest);
assert!(analysis.has_tests);
assert!(analysis.has_docs);
assert!(analysis.has_examples);
assert!(analysis.has_reproducibility_scripts);
assert!(analysis.has_benchmarks);
assert!(!analysis.is_notebook_only);
}
#[test]
fn scores_missing_quality_signals_from_analyses() {
let repository = repo("https://github.com/a/b");
let analysis = analyse::analyze_repository_with_paths(&repository, &[]);
let assessment = analyse::assess_with_analyses(&[], &[repository], &[analysis]);
assert_eq!(assessment.components.packaging_gap_score, 10);
assert_eq!(assessment.components.docs_gap_score, 10);
assert_eq!(assessment.components.tests_gap_score, 10);
assert_eq!(assessment.components.reproducibility_gap_score, 10);
}
#[test]
fn detects_notebook_only_repository() {
let paths = vec!["notebooks/demo.ipynb".to_string()];
let analysis = analyse::analyze_repository_with_paths(&repo("https://github.com/a/b"), &paths);
assert!(analysis.is_notebook_only);
}
#[test]
fn provider_failures_do_not_inflate_no_code_score() {
let assessment = analyse::assess_with_evidence(&[], &[], &[], false, false);
assert_eq!(assessment.components.no_code_score, 0);
assert_eq!(assessment.implementation_gap_score, 0);
assert!(assessment.gaps.is_empty());
}
#[test]
fn failed_inspection_does_not_create_quality_gaps() {
let repository = repo("https://github.com/a/b");
let analysis = analyse::analyze_repository(&repository);
assert!(!analysis.inspection_succeeded);
let assessment = analyse::assess_with_analyses(&[], &[repository], &[analysis]);
assert_eq!(assessment.components.packaging_gap_score, 0);
assert_eq!(assessment.components.docs_gap_score, 0);
assert_eq!(assessment.components.tests_gap_score, 0);
assert_eq!(assessment.components.reproducibility_gap_score, 0);
}
#[test]
fn disabled_repository_search_does_not_prove_absence() {
let assessment = analyse::assess_with_evidence(&[], &[], &[], true, false);
assert_eq!(assessment.components.no_code_score, 10);
assert!(
!assessment
.gaps
.iter()
.any(|gap| matches!(gap.gap_type, analyse::GapType::NoPublicImplementationFound))
);
}