use chrono::NaiveDate;
use release_tool::command::SystemCommandRunner;
use release_tool::config::Config;
use release_tool::domain::ReleaseIntent;
use release_tool::git::RepositorySnapshot;
use release_tool::lifecycle::resolve_plan;
use release_tool::tag::TagCatalog;
use std::sync::Arc;
#[test]
fn selected_targets_cannot_claim_the_same_remote_artifact_identity() {
let config = Config::parse(
r#"required_version = "0.1.0"
[repository]
github = "computed-parameter/example"
branch = "main"
[publishers.release]
kind = "github_release"
title = "Example {version}"
prerelease = true
[[targets]]
name = "one"
kind = "docker_archive"
publisher = "release"
default = true
platform = "linux/amd64"
image = "one:{version}"
asset = "shared-{version}.tar.xz"
build = ["just", "one"]
local_check = ["just", "check-one"]
[[targets]]
name = "two"
kind = "docker_archive"
publisher = "release"
default = true
platform = "linux/amd64"
image = "two:{version}"
asset = "shared-{version}.tar.xz"
build = ["just", "two"]
local_check = ["just", "check-two"]
"#,
)
.unwrap();
let error = resolve(&config, &[], false).unwrap_err();
assert!(error.to_string().contains("same artifact identity"));
assert!(error.to_string().contains("one"));
assert!(error.to_string().contains("two"));
}
#[test]
fn selected_targets_include_dependencies_in_stable_topological_order() {
let config = Config::parse(
r#"required_version = "0.3.0"
[repository]
github = "computed-parameter/example"
branch = "main"
[[targets]]
name = "app"
kind = "oci_image"
default = true
depends_on = ["base-b", "base-a"]
image = "ghcr.io/computed-parameter/app:{version}"
platform = "linux/amd64"
reuse_check = ["check", "app"]
build = ["build", "app"]
[[targets]]
name = "base-b"
kind = "oci_image"
image = "ghcr.io/computed-parameter/base-b:{version}"
platform = "linux/amd64"
reuse_check = ["check", "base-b"]
build = ["build", "base-b"]
[[targets]]
name = "base-a"
kind = "oci_image"
depends_on = ["foundation"]
image = "ghcr.io/computed-parameter/base-a:{version}"
platform = "linux/amd64"
reuse_check = ["check", "base-a"]
build = ["build", "base-a"]
[[targets]]
name = "foundation"
kind = "oci_image"
image = "ghcr.io/computed-parameter/foundation:{version}"
platform = "linux/amd64"
reuse_check = ["check", "foundation"]
build = ["build", "foundation"]
"#,
)
.unwrap();
let plan = resolve(&config, &[], false).unwrap();
assert_eq!(
plan.targets
.iter()
.map(|target| target.name.as_str())
.collect::<Vec<_>>(),
["base-b", "foundation", "base-a", "app"]
);
assert!(
plan.targets
.iter()
.all(|target| target.publisher == "oci_registry")
);
let base_only = resolve(&config, &["base-b".to_owned()], false).unwrap();
assert_eq!(base_only.targets.len(), 1);
assert_eq!(base_only.targets[0].name, "base-b");
let all = resolve_all(&config).unwrap();
assert_eq!(
all.targets
.iter()
.map(|target| target.name.as_str())
.collect::<Vec<_>>(),
["base-b", "foundation", "base-a", "app"]
);
}
#[test]
fn oci_platforms_cannot_claim_the_same_repository_tag() {
let config = Config::parse(
r#"required_version = "0.3.0"
[repository]
github = "computed-parameter/example"
branch = "main"
[[targets]]
name = "amd"
kind = "oci_image"
default = true
image = "ghcr.io/computed-parameter/fixture:{version}"
platform = "linux/amd64"
reuse_check = ["check", "amd"]
build = ["build", "amd"]
[[targets]]
name = "arm"
kind = "oci_image"
default = true
image = "ghcr.io/computed-parameter/fixture:{version}"
platform = "linux/arm64"
reuse_check = ["check", "arm"]
build = ["build", "arm"]
"#,
)
.unwrap();
let error = resolve(&config, &[], false).unwrap_err();
assert!(error.to_string().contains("same OCI tag"));
assert!(error.to_string().contains("platform"));
assert!(error.to_string().contains("amd"));
assert!(error.to_string().contains("arm"));
}
fn resolve(
config: &Config,
requested: &[String],
all: bool,
) -> anyhow::Result<release_tool::domain::ReleasePlan> {
let snapshot = RepositorySnapshot {
root: ".".into(),
branch: "main".to_owned(),
head: "commit01".to_owned(),
remote_branch_head: "commit01".to_owned(),
tags: TagCatalog::default(),
};
resolve_plan(
config,
&snapshot,
ReleaseIntent::New,
NaiveDate::from_ymd_opt(2026, 8, 24).unwrap(),
requested,
all,
Arc::new(SystemCommandRunner),
)
}
fn resolve_all(config: &Config) -> anyhow::Result<release_tool::domain::ReleasePlan> {
let snapshot = RepositorySnapshot {
root: ".".into(),
branch: "main".to_owned(),
head: "commit01".to_owned(),
remote_branch_head: "commit01".to_owned(),
tags: TagCatalog::default(),
};
resolve_plan(
config,
&snapshot,
ReleaseIntent::New,
NaiveDate::from_ymd_opt(2026, 8, 24).unwrap(),
&[],
true,
Arc::new(SystemCommandRunner),
)
}