#[cfg(all(test, feature = "hotpath"))]
mod tests {
use std::path::PathBuf;
use std::process::Command;
use hotpath::json::{JsonMeta, JsonReport};
const OTHER_SHA: &str = "2222222222222222222222222222222222222222";
const BASE_SHA: &str = "3333333333333333333333333333333333333333";
const PR_HEAD_SHA: &str = "4444444444444444444444444444444444444444";
fn head_sha() -> String {
let output = Command::new("git")
.args(["rev-parse", "HEAD"])
.output()
.expect("git rev-parse HEAD");
assert!(output.status.success(), "git rev-parse HEAD failed");
String::from_utf8_lossy(&output.stdout).trim().to_string()
}
fn run_example(envs: &[(&str, &str)]) -> JsonMeta {
let mut cmd = Command::new("cargo");
cmd.args([
"run",
"-p",
"test-all-features",
"--example",
"basic_all_features",
"--features",
"hotpath,hotpath-cloud",
])
.env("HOTPATH_OUTPUT_FORMAT", "json")
.env("HOTPATH_REPORT", "functions-timing")
.env_remove("HOTPATH_UPLOAD")
.env_remove("HOTPATH_BENCHMARK");
for var in [
"GITHUB_ACTIONS",
"GITHUB_BASE_REF",
"GITHUB_HEAD_REF",
"GITHUB_SHA",
"GITHUB_REF",
"GITHUB_REPOSITORY",
"GITHUB_REPOSITORY_ID",
"GITHUB_EVENT_NAME",
"GITHUB_EVENT_PATH",
"GITHUB_RUN_ID",
"GITHUB_WORKFLOW",
"GITHUB_ACTOR",
] {
cmd.env_remove(var);
}
cmd.envs(envs.iter().copied());
let output = cmd.output().expect("Failed to execute command");
assert!(
output.status.success(),
"Process did not exit successfully.\n\nstderr:\n{}",
String::from_utf8_lossy(&output.stderr)
);
let stdout = String::from_utf8_lossy(&output.stdout);
let json_start = stdout.find('{').expect("No JSON report in output");
serde_json::Deserializer::from_str(&stdout[json_start..])
.into_iter::<JsonReport>()
.next()
.expect("No JSON value in output")
.expect("Failed to parse JSON report")
.meta
}
fn write_event_payload(name: &str, base_sha: &str) -> PathBuf {
write_event(name, base_sha, Some(PR_HEAD_SHA))
}
fn write_event(name: &str, base_sha: &str, head_sha: Option<&str>) -> PathBuf {
let path = std::env::temp_dir().join(format!(
"hotpath-cloud-meta-event-{}-{name}.json",
std::process::id()
));
let head = head_sha
.map(|sha| format!(r#","head":{{"sha":"{sha}"}}"#))
.unwrap_or_default();
std::fs::write(
&path,
format!(
r#"{{"action":"synchronize","pull_request":{{"number":42,"base":{{"ref":"main","sha":"{base_sha}"}}{head}}}}}"#
),
)
.expect("event payload written");
path
}
#[test]
fn meta_outside_ci_describes_the_checkout() {
let meta = run_example(&[]);
assert!(
meta.ci.is_none(),
"no CI provider outside CI: {:?}",
meta.ci
);
assert_eq!(meta.benchmark.as_deref(), Some("default"));
let git = meta.git.expect("git info from the checkout");
assert_eq!(git.sha.len(), 40, "sha: {}", git.sha);
assert_eq!(git.repository.as_deref(), Some("pawurb/hotpath-rs"));
assert_eq!(git.base_sha, None);
}
#[test]
fn meta_in_github_actions_describes_the_run() {
let head = head_sha();
let event_path = write_event_payload("default-checkout", BASE_SHA);
let meta = run_example(&[
("GITHUB_ACTIONS", "true"),
("GITHUB_SHA", &head),
("GITHUB_REF", "refs/pull/42/merge"),
("GITHUB_REPOSITORY", "pawurb/hotpath-rs"),
("GITHUB_REPOSITORY_ID", "123456"),
("GITHUB_EVENT_NAME", "pull_request"),
("GITHUB_EVENT_PATH", &event_path.to_string_lossy()),
("GITHUB_BASE_REF", "main"),
("GITHUB_HEAD_REF", "feature-x"),
("GITHUB_RUN_ID", "987654321"),
("GITHUB_WORKFLOW", "benchmarks"),
("GITHUB_ACTOR", "pawurb"),
("HOTPATH_BENCHMARK", "timing-linux"),
]);
let _ = std::fs::remove_file(&event_path);
let git = meta.git.expect("git info");
assert_eq!(git.sha, head);
assert_eq!(git.r#ref.as_deref(), Some("refs/pull/42/merge"));
assert_eq!(git.base_sha.as_deref(), Some(BASE_SHA));
assert_eq!(git.repository.as_deref(), Some("pawurb/hotpath-rs"));
let ci = meta.ci.expect("ci info");
assert_eq!(ci.provider, "github-actions");
assert_eq!(ci.event, "pull_request");
let pr = ci.pull_request.expect("pull request info");
assert_eq!(pr.number, 42);
assert_eq!(pr.base_ref, "main");
assert_eq!(pr.head_ref, "feature-x");
assert_eq!(pr.head_sha.as_deref(), Some(PR_HEAD_SHA));
assert_eq!(ci.run_id.as_deref(), Some("987654321"));
assert_eq!(ci.workflow.as_deref(), Some("benchmarks"));
assert_eq!(ci.actor.as_deref(), Some("pawurb"));
assert_eq!(ci.repository_id.as_deref(), Some("123456"));
assert_eq!(meta.benchmark.as_deref(), Some("timing-linux"));
}
#[test]
fn head_checkout_wins_over_the_environment() {
let event_path = write_event_payload("head-checkout", BASE_SHA);
let meta = run_example(&[
("GITHUB_ACTIONS", "true"),
("GITHUB_SHA", OTHER_SHA),
("GITHUB_REF", "refs/pull/42/merge"),
("GITHUB_REPOSITORY", "pawurb/renamed-repo"),
("GITHUB_EVENT_NAME", "pull_request"),
("GITHUB_EVENT_PATH", &event_path.to_string_lossy()),
("GITHUB_BASE_REF", "main"),
("GITHUB_HEAD_REF", "feature-x"),
]);
let _ = std::fs::remove_file(&event_path);
let git = meta.git.expect("git info");
assert_eq!(git.sha, head_sha());
assert_ne!(git.r#ref.as_deref(), Some("refs/pull/42/merge"));
assert_eq!(git.base_sha.as_deref(), Some(BASE_SHA));
assert_eq!(git.repository.as_deref(), Some("pawurb/hotpath-rs"));
let ci = meta.ci.expect("ci info");
assert_eq!(ci.event, "pull_request");
let pr = ci.pull_request.expect("pull request info");
assert_eq!(pr.number, 42);
assert_eq!(pr.base_ref, "main");
assert_eq!(pr.head_ref, "feature-x");
assert_eq!(pr.head_sha.as_deref(), Some(PR_HEAD_SHA));
}
#[test]
fn base_checkout_carries_no_base_sha() {
let head = head_sha();
let event_path = write_event_payload("base-checkout", &head);
let meta = run_example(&[
("GITHUB_ACTIONS", "true"),
("GITHUB_SHA", OTHER_SHA),
("GITHUB_REF", "refs/pull/42/merge"),
("GITHUB_EVENT_NAME", "pull_request"),
("GITHUB_EVENT_PATH", &event_path.to_string_lossy()),
("GITHUB_BASE_REF", "main"),
("GITHUB_HEAD_REF", "feature-x"),
]);
let _ = std::fs::remove_file(&event_path);
let git = meta.git.expect("git info");
assert_eq!(git.sha, head);
assert_eq!(git.base_sha, None);
let ci = meta.ci.expect("ci info");
assert_eq!(ci.event, "pull_request");
assert_eq!(ci.pull_request.expect("pull request info").base_ref, "main");
}
#[test]
fn unreadable_event_file_keeps_the_pull_request_branches() {
let head = head_sha();
let meta = run_example(&[
("GITHUB_ACTIONS", "true"),
("GITHUB_SHA", &head),
("GITHUB_REF", "refs/pull/42/merge"),
("GITHUB_EVENT_NAME", "pull_request"),
("GITHUB_EVENT_PATH", "/nonexistent/event.json"),
("GITHUB_BASE_REF", "main"),
("GITHUB_HEAD_REF", "feature-x"),
]);
assert_eq!(meta.git.expect("git info").base_sha, None);
let ci = meta.ci.expect("ci info");
let pr = ci.pull_request.expect("pull request info");
assert_eq!(pr.number, 42);
assert_eq!(pr.base_ref, "main");
assert_eq!(pr.head_ref, "feature-x");
assert_eq!(pr.head_sha, None);
}
#[test]
fn event_payload_without_head_keeps_the_rest() {
let head = head_sha();
let event_path = write_event("no-head", BASE_SHA, None);
let meta = run_example(&[
("GITHUB_ACTIONS", "true"),
("GITHUB_SHA", &head),
("GITHUB_REF", "refs/pull/42/merge"),
("GITHUB_EVENT_NAME", "pull_request"),
("GITHUB_EVENT_PATH", &event_path.to_string_lossy()),
("GITHUB_BASE_REF", "main"),
("GITHUB_HEAD_REF", "feature-x"),
]);
let _ = std::fs::remove_file(&event_path);
assert_eq!(
meta.git.expect("git info").base_sha.as_deref(),
Some(BASE_SHA)
);
let pr = meta
.ci
.expect("ci info")
.pull_request
.expect("pull request info");
assert_eq!(pr.number, 42);
assert_eq!(pr.base_ref, "main");
assert_eq!(pr.head_ref, "feature-x");
assert_eq!(pr.head_sha, None);
}
#[test]
fn push_runs_carry_no_base_sha() {
let head = head_sha();
let meta = run_example(&[
("GITHUB_ACTIONS", "true"),
("GITHUB_SHA", &head),
("GITHUB_REF", "refs/heads/main"),
("GITHUB_EVENT_NAME", "push"),
("GITHUB_EVENT_PATH", "/nonexistent/event.json"),
]);
let git = meta.git.expect("git info");
assert_eq!(git.sha, head);
assert_eq!(git.r#ref.as_deref(), Some("refs/heads/main"));
assert_eq!(git.base_sha, None);
let ci = meta.ci.expect("ci info");
assert_eq!(ci.event, "push");
assert!(ci.pull_request.is_none());
}
}