refactor: move embedded tests into dedicated test directory

This commit is contained in:
Valdemar Lorenzen
2026-10-16 16:30:07 +02:00
parent de6a57e67d
commit 2772e6878a
55 changed files with 45752 additions and 45858 deletions
+2 -427
View File
@@ -812,430 +812,5 @@ pub(crate) fn resolve(
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
fn parse(url: &str, provider: Option<&str>) -> Result<Forge, String> {
Forge::parse(url, provider, |destination, _| {
Ok(destination.rsplit('@').next().unwrap().to_owned())
})
}
#[test]
fn pages_are_bounded_newest_first_summaries_without_historical_metadata() {
for (provider, url, query) in [
(
Provider::Github,
"https://github.com/owner/repo",
"state=open&per_page=50&sort=created&direction=desc&page=3",
),
(
Provider::Gitlab,
"https://gitlab.com/owner/repo",
"state=opened&scope=all&per_page=50&order_by=created_at&sort=desc&page=3",
),
(
Provider::Gitea,
"https://codeberg.org/owner/repo",
"state=open&limit=50&page=3",
),
] {
let forge = parse(url, None).unwrap();
let mut calls = 0;
let page = forge.page_with(false, 3, |url| {
calls += 1;
assert_eq!(url, format!("{}?{query}", forge.endpoint));
Ok(json!([
{"number": 1, "iid": 1, "title": "Older", "state": if provider == Provider::Gitlab { "opened" } else { "open" },
"user": null, "author": null, "merged_at": null, "merged": false, "created_at": "2024-01-01T01:00:00+01:00"},
{"number": 2, "iid": 2, "title": "Newer", "state": if provider == Provider::Gitlab { "opened" } else { "open" },
"user": {"login": "author"}, "author": {"username": "author"}, "merged_at": null, "merged": false,
"created_at": "2024-01-01T00:00:00.001Z", "diff_refs": null, "head": {"sha": "not-a-commit"}}
]))
}).unwrap();
assert_eq!(calls, 1);
assert_eq!(
page.entries.iter().map(|p| p.number).collect::<Vec<_>>(),
[2, 1]
);
assert_eq!(page.entries[0].description, "author · open");
assert!(page.entries[1].description.contains("deleted user"));
assert!(
page.has_more,
"A short, server-capped page is not exhaustion"
);
let empty = forge.page_with(false, 4, |_| Ok(json!([]))).unwrap();
assert!(empty.entries.is_empty() && !empty.has_more);
assert!(
forge
.page_with(false, 0, |_| panic!("Page zero must not request"))
.is_err()
);
}
}
#[test]
fn gitlab_closed_combines_closed_and_merged_pages_without_open_requests() {
let forge = parse("https://gitlab.com/group/repo", None).unwrap();
let mut all = Vec::new();
for page in 1..=3 {
let mut states = Vec::new();
let result = forge
.page_with(true, page, |url| {
assert!(url.ends_with(&format!("&page={page}")));
let state = if url.contains("?state=closed&") {
"closed"
} else {
assert!(url.contains("?state=merged&"));
"merged"
};
states.push(state);
let (id, date) = match (state, page) {
("closed", 1) => (1, "2024-01-01T00:00:00Z"),
("merged", 1) => (3, "2024-03-01T00:00:00Z"),
("merged", 2) => (2, "2024-02-01T00:00:00Z"),
_ => return Ok(json!([])),
};
Ok(json!([{"iid": id, "title": state, "state": state,
"author": null, "created_at": date, "diff_refs": null}]))
})
.unwrap();
assert_eq!(states, ["closed", "merged"]);
assert_eq!(result.has_more, page != 3);
assert!(result.entries.iter().all(|p| p.closed));
if page == 1 {
assert_eq!(
result.entries.iter().map(|p| p.number).collect::<Vec<_>>(),
[3, 1]
);
}
// The second merged page still precedes the first closed page.
all.extend(result.entries);
}
all.sort_unstable_by(|a, b| b.created_at.cmp(&a.created_at));
assert_eq!(all.iter().map(|p| p.number).collect::<Vec<_>>(), [3, 2, 1]);
let raced = forge.page_with(true, 1, |url| {
Ok(json!([{"iid": 7, "title": "Raced merge", "state": if url.contains("?state=merged&") { "merged" } else { "closed" },
"author": null, "created_at": "2024-01-01T00:00:00Z"}]))
}).unwrap();
assert_eq!(raced.entries.len(), 1);
assert!(raced.entries[0].description.contains("merged"));
}
#[test]
fn timestamps_normalize_offsets_precision_and_calendar_boundaries() {
for (input, expected) in [
(
"2024-03-01T00:30:12.5+01:00",
"2024-02-29T23:30:12.500000000Z",
),
(
"2023-12-31T23:30:00-01:00",
"2024-01-01T00:30:00.000000000Z",
),
(
"2024-01-01T00:00:00.000000001Z",
"2024-01-01T00:00:00.000000001Z",
),
] {
assert_eq!(created_at(&json!({"created_at": input})).unwrap(), expected);
}
for input in [
"",
"not a date",
"2023-02-29T00:00:00Z",
"2024-13-01T00:00:00Z",
"2024-01-01T24:00:00Z",
"2024-01-01T00:00:00",
"2024-01-01T00:00:00+24:00",
"2024-01-01T00:00:00.Z",
"2024-01-01T00:00:00.1234567890Z",
"0001-01-01T00:00:00+01:00",
"9999-12-31T23:00:00-02:00",
] {
assert!(
created_at(&json!({"created_at": input})).is_err(),
"{input}"
);
}
}
#[test]
fn resolution_reads_only_selected_detail_and_exact_preserved_head() {
for (url, provider) in [
("https://github.com/owner/repo", Provider::Github),
("https://gitlab.com/group/repo", Provider::Gitlab),
("https://codeberg.org/owner/repo", Provider::Gitea),
] {
let forge = parse(url, None).unwrap();
let mut calls = Vec::new();
let pr = forge.resolve_with(7, |url| {
calls.push(url.to_owned());
if url == format!("{}/7", forge.endpoint) {
Ok(json!({"number": 7, "iid": 7, "title": "Selected", "state": if provider == Provider::Gitlab { "merged" } else { "closed" },
"merged_at": "2024-01-01T00:00:00Z", "merged": true, "user": null, "author": null,
"base": {"ref": "main", "sha": "1".repeat(40)}, "head": {"ref": "topic", "sha": "2".repeat(40)},
"merge_base": "3".repeat(40), "target_branch": "main", "source_branch": "topic",
"diff_refs": {"start_sha": null, "base_sha": "4".repeat(40), "head_sha": "5".repeat(40)},
"url": "https://evil.example/detail", "references": "https://evil.example/refs"}))
} else {
assert_eq!(url, format!("{}/git/refs/pull/7/head", forge.endpoint.strip_suffix("/pulls").unwrap()));
Ok(json!([{"ref": "refs/pull/7/head", "object": {"type": "commit", "sha": "6".repeat(40)}}]))
}
}).unwrap();
assert_eq!(calls.len(), if provider == Provider::Gitea { 2 } else { 1 });
let revisions = pr.revisions.unwrap();
assert_eq!(
revisions.base_sha,
match provider {
Provider::Github => "1",
Provider::Gitlab => "4",
Provider::Gitea => "3",
}
.repeat(40)
);
assert_eq!(
revisions.head_sha,
match provider {
Provider::Github => "2",
Provider::Gitlab => "5",
Provider::Gitea => "6",
}
.repeat(40)
);
assert!(
forge
.resolve_with(0, |_| panic!("Zero must not request"))
.is_err()
);
let mut calls = 0;
assert!(
forge
.resolve_with(7, |_| {
calls += 1;
Ok(json!({"number": 8, "iid": 8}))
})
.is_err()
);
assert_eq!(
calls, 1,
"Reject wrong detail identity before reading historical refs"
);
}
}
#[test]
fn gitea_preserved_heads_reject_missing_ambiguous_or_foreign_refs() {
let reference =
json!({"ref": "refs/pull/7/head", "object": {"type": "commit", "sha": "a".repeat(40)}});
assert_eq!(gitea_head(&reference, 7).unwrap(), "a".repeat(40));
assert_eq!(
gitea_head(&json!([reference.clone()]), 7).unwrap(),
"a".repeat(40)
);
for bad in [
json!([]),
json!([reference.clone(), reference.clone()]),
json!({"ref": "refs/pull/8/head", "object": reference["object"]}),
json!({"ref": "refs/pull/7/head", "object": {"type": "tag", "sha": "a".repeat(40)}}),
json!({"ref": "refs/pull/7/head", "object": {"type": "commit", "sha": "0".repeat(40)}}),
] {
assert!(gitea_head(&bad, 7).is_err());
}
}
#[test]
fn ssh_aliases_use_effective_hostname_and_preserve_user_matching() {
use std::{fs, io::Write, path::PathBuf};
struct Config(PathBuf);
impl Drop for Config {
fn drop(&mut self) {
let _ = fs::remove_file(&self.0);
}
}
let path = std::env::temp_dir().join(format!("vamp-forge-ssh-{}.conf", std::process::id()));
let mut file = fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(&path)
.unwrap();
let config = Config(path);
file.write_all(b"Host gitea-personal\n HostName gitea.example.org\n Port 2222\nHost github-personal\n HostName ssh.github.com\nHost user-match\n Port 2222\nMatch originalhost user-match user git\n HostName gitlab.example.org\n").unwrap();
for (url, expected) in [
(
"git@gitea-personal:owner/repo.git",
"https://gitea.example.org/api/v1/repos/owner/repo/pulls",
),
(
"ssh://git@gitea-personal:2200/owner/repo.git",
"https://gitea.example.org/api/v1/repos/owner/repo/pulls",
),
(
"git@github-personal:owner/repo.git",
"https://api.github.com/repos/owner/repo/pulls",
),
(
"git@user-match:group/sub/repo.git",
"https://gitlab.example.org/api/v4/projects/group%2Fsub%2Frepo/merge_requests",
),
] {
let forge = Forge::parse(url, None, |destination, port| {
let mut command = Command::new("ssh");
command.arg("-F").arg(&config.0);
ssh_hostname(command, destination, port, &AtomicBool::new(false))
})
.unwrap();
assert_eq!(forge.endpoint, expected);
}
assert!(
Forge::parse("git@alias:owner/repo", Some("gitea"), |_, _| Ok(
"evil.example/path".into()
))
.is_err()
);
assert!(
Forge::parse(
"ssh://user:secret@alias/owner/repo",
Some("gitea"),
|_, _| { panic!("Password-bearing SSH URLs must be rejected before starting SSH") }
)
.is_err()
);
assert_eq!(
Forge::parse("https://github.com/owner/repo", None, |_, _| panic!(
"HTTPS must not invoke SSH"
))
.unwrap()
.host,
"github.com"
);
}
#[test]
fn remote_urls_preserve_namespaces_and_keep_ssh_ports_out_of_https() {
let gitlab = parse("ssh://git@gitlab.example.org:2222/group/sub/repo.git", None).unwrap();
assert_eq!(
gitlab.endpoint,
"https://gitlab.example.org/api/v4/projects/group%2Fsub%2Frepo/merge_requests"
);
assert_eq!(
parse("git@github.com:owner/repo.git", None)
.unwrap()
.endpoint,
"https://api.github.com/repos/owner/repo/pulls"
);
assert_eq!(
parse(
"https://user:secret@forge.example:8443/owner/repo.git",
Some("gitea")
)
.unwrap()
.endpoint,
"https://forge.example:8443/api/v1/repos/owner/repo/pulls"
);
assert!(
parse("https://forge.example/owner/repo", None)
.err()
.unwrap()
.contains("git config remote.<name>.vamp-forge github")
);
for url in [
"https://github.com/owner/repo?token=secret",
"https://github.com/owner/%2e%2e",
"https://github.com/owner/repo%0a",
"https://github.com:0/owner/repo",
"file:///owner/repo",
] {
assert!(parse(url, Some("github")).is_err(), "{url}");
}
}
#[test]
fn fork_pull_requests_pin_historical_shas_not_merge_commits_or_response_urls() {
let value = json!({"number": 7, "title": "Merged fork", "state": "closed", "merged_at": "2026-01-01T00:00:00Z", "merged": true,
"user": {"login": "author"}, "base": {"ref": "release/next", "sha": "1".repeat(40)},
"head": {"ref": "feature", "label": "fork:topic", "sha": "2".repeat(40), "repo": null}, "merge_base": "4".repeat(40), "merge_commit_sha": "3".repeat(40), "url": "https://evil.example/token"});
for provider in [Provider::Github, Provider::Gitea] {
let pr = pull_request(provider, &value, Some("5".repeat(40))).unwrap();
let revisions = pr.revisions.as_ref().unwrap();
assert_eq!(
(revisions.base_ref.as_str(), revisions.head_ref.as_str()),
("refs/heads/release/next", "refs/pull/7/head")
);
assert_eq!(
revisions.base_sha,
if provider == Provider::Gitea {
"4"
} else {
"1"
}
.repeat(40)
);
assert_eq!(
revisions.head_sha,
if provider == Provider::Gitea {
"5"
} else {
"2"
}
.repeat(40)
);
if provider == Provider::Gitea {
let mut deleted = value.clone();
deleted["head"]["sha"] = Value::String(String::new());
assert_eq!(
pull_request(provider, &deleted, Some("5".repeat(40)))
.unwrap()
.revisions
.unwrap()
.head_sha,
"5".repeat(40)
);
let unavailable = pull_request(provider, &deleted, None).unwrap();
assert!(unavailable.revisions.is_err());
}
}
}
#[test]
fn gitlab_uses_target_start_sha_and_rejects_unprepared_or_unsafe_metadata() {
let mut value = json!({"iid": 9, "title": "MR", "state": "merged", "author": {"username": "author"},
"target_branch": "main", "source_branch": "topic", "diff_refs": {"base_sha": "1".repeat(40), "start_sha": "2".repeat(40), "head_sha": "3".repeat(40)}});
let pr = pull_request(Provider::Gitlab, &value, None).unwrap();
let revisions = pr.revisions.unwrap();
assert_eq!(revisions.base_sha, "2".repeat(40));
assert_eq!(revisions.head_ref, "refs/merge-requests/9/head");
value["diff_refs"]["start_sha"] = Value::Null;
assert_eq!(
pull_request(Provider::Gitlab, &value, None)
.unwrap()
.revisions
.unwrap()
.base_sha,
"1".repeat(40)
);
value["target_branch"] = json!("main:refs/heads/injected");
assert!(
pull_request(Provider::Gitlab, &value, None)
.unwrap()
.revisions
.is_err()
);
value["target_branch"] = json!("main");
value["diff_refs"]["head_sha"] = json!("HEAD");
assert!(
pull_request(Provider::Gitlab, &value, None)
.unwrap()
.revisions
.is_err()
);
value["diff_refs"] = Value::Null;
assert!(
pull_request(Provider::Gitlab, &value, None)
.unwrap()
.revisions
.is_err()
);
}
}
#[path = "../tests/unit/forge.rs"]
mod tests;