use std::collections::BTreeMap;
use crate::trust::determine_trust_tier;
use super::{
base64_decode, create_lane_repo, execute_lane, format_duration, format_lane_toml,
lane_defaults, list_lanes, load_lane_config, parse_import_source, validate_lane,
validate_lanes, FledgeFileWithLanes, LaneDef, ParallelItem, Step,
};
fn parse_config(toml_str: &str) -> FledgeFileWithLanes {
toml::from_str(toml_str).unwrap()
}
#[test]
fn parse_sequential_lane() {
let config = parse_config(
r#"
[tasks]
lint = "cargo clippy"
test = "cargo test"
build = "cargo build"
[lanes.ci]
description = "CI pipeline"
steps = ["lint", "test", "build"]
"#,
);
assert_eq!(config.lanes.len(), 1);
assert_eq!(config.lanes["ci"].steps.len(), 3);
assert!(config.lanes["ci"].fail_fast);
}
#[test]
fn parse_inline_step() {
let config = parse_config(
r#"
[tasks]
test = "cargo test"
[lanes.release]
description = "Release"
steps = [
"test",
{ run = "cargo build --release" },
]
"#,
);
assert_eq!(config.lanes["release"].steps.len(), 2);
match &config.lanes["release"].steps[1] {
Step::Inline { run: cmd } => assert_eq!(cmd, "cargo build --release"),
_ => panic!("expected inline step"),
}
}
#[test]
fn parse_parallel_step() {
let config = parse_config(
r#"
[tasks]
lint = "cargo clippy"
fmt = "cargo fmt --check"
test = "cargo test"
[lanes.check]
description = "Quick check"
steps = [
{ parallel = ["lint", "fmt"] },
"test"
]
"#,
);
assert_eq!(config.lanes["check"].steps.len(), 2);
match &config.lanes["check"].steps[0] {
Step::Parallel { parallel } => {
assert_eq!(parallel.len(), 2);
assert!(matches!(¶llel[0], ParallelItem::TaskRef(n) if n == "lint"));
assert!(matches!(¶llel[1], ParallelItem::TaskRef(n) if n == "fmt"));
}
_ => panic!("expected parallel step"),
}
}
#[test]
fn parse_fail_fast_false() {
let config = parse_config(
r#"
[tasks]
a = "echo a"
b = "echo b"
[lanes.audit]
description = "Audit"
fail_fast = false
steps = ["a", "b"]
"#,
);
assert!(!config.lanes["audit"].fail_fast);
}
#[test]
fn parse_fail_fast_default_true() {
let config = parse_config(
r#"
[tasks]
a = "echo a"
[lanes.ci]
steps = ["a"]
"#,
);
assert!(config.lanes["ci"].fail_fast);
}
#[test]
fn validate_unknown_task_ref() {
let config = parse_config(
r#"
[tasks]
lint = "cargo clippy"
[lanes.ci]
steps = ["lint", "nonexistent"]
"#,
);
let result = validate_lane("ci", &config.lanes["ci"], &config.tasks);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("nonexistent"));
}
#[test]
fn validate_unknown_parallel_ref() {
let config = parse_config(
r#"
[tasks]
lint = "cargo clippy"
[lanes.check]
steps = [{ parallel = ["lint", "ghost"] }]
"#,
);
let result = validate_lane("check", &config.lanes["check"], &config.tasks);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("ghost"));
}
#[test]
fn validate_inline_always_ok() {
let config = parse_config(
r#"
[tasks]
[lanes.ci]
steps = [{ run = "echo hello" }]
"#,
);
let result = validate_lane("ci", &config.lanes["ci"], &config.tasks);
assert!(result.is_ok());
}
#[test]
fn validate_all_valid_refs() {
let config = parse_config(
r#"
[tasks]
lint = "cargo clippy"
test = "cargo test"
build = "cargo build"
[lanes.ci]
steps = ["lint", "test", "build"]
"#,
);
let result = validate_lane("ci", &config.lanes["ci"], &config.tasks);
assert!(result.is_ok());
}
#[test]
fn validate_circular_deps() {
let config = parse_config(
r#"
[tasks.a]
cmd = "echo a"
deps = ["b"]
[tasks.b]
cmd = "echo b"
deps = ["a"]
[lanes.ci]
steps = ["a"]
"#,
);
let result = validate_lane("ci", &config.lanes["ci"], &config.tasks);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(
err.contains("circular"),
"expected circular error, got: {err}"
);
}
#[test]
fn validate_no_cycle_with_shared_deps() {
let config = parse_config(
r#"
[tasks]
common = "echo common"
[tasks.a]
cmd = "echo a"
deps = ["common"]
[tasks.b]
cmd = "echo b"
deps = ["common"]
[lanes.ci]
steps = ["a", "b"]
"#,
);
let result = validate_lane("ci", &config.lanes["ci"], &config.tasks);
assert!(result.is_ok());
}
#[test]
fn parse_multiple_lanes() {
let config = parse_config(
r#"
[tasks]
lint = "cargo clippy"
test = "cargo test"
build = "cargo build"
[lanes.ci]
description = "CI"
steps = ["lint", "test", "build"]
[lanes.quick]
description = "Quick"
steps = ["lint"]
"#,
);
assert_eq!(config.lanes.len(), 2);
assert!(config.lanes.contains_key("ci"));
assert!(config.lanes.contains_key("quick"));
}
#[test]
fn parse_no_lanes_section() {
let config = parse_config(
r#"
[tasks]
build = "cargo build"
"#,
);
assert!(config.lanes.is_empty());
}
#[test]
fn parse_empty_lanes_section() {
let config = parse_config(
r#"
[tasks]
build = "cargo build"
[lanes]
"#,
);
assert!(config.lanes.is_empty());
}
#[test]
fn parse_mixed_step_types() {
let config = parse_config(
r#"
[tasks]
test = "cargo test"
lint = "cargo clippy"
[lanes.full]
steps = [
"test",
{ run = "echo done" },
{ parallel = ["test", "lint"] },
]
"#,
);
assert_eq!(config.lanes["full"].steps.len(), 3);
assert!(matches!(&config.lanes["full"].steps[0], Step::TaskRef(_)));
assert!(matches!(
&config.lanes["full"].steps[1],
Step::Inline { .. }
));
assert!(matches!(
&config.lanes["full"].steps[2],
Step::Parallel { .. }
));
}
#[test]
fn execute_sequential_lane_echo() {
let config = parse_config(
r#"
[tasks]
a = "echo step-a"
b = "echo step-b"
[lanes.seq]
description = "Sequential"
steps = ["a", "b"]
"#,
);
let project_dir = std::env::current_dir().unwrap();
let result = execute_lane(
"seq",
&config.lanes["seq"],
&config.tasks,
&project_dir,
false,
);
assert!(result.is_ok());
}
#[test]
fn execute_inline_step() {
let config = parse_config(
r#"
[tasks]
[lanes.inline]
steps = [{ run = "echo inline-works" }]
"#,
);
let project_dir = std::env::current_dir().unwrap();
let result = execute_lane(
"inline",
&config.lanes["inline"],
&config.tasks,
&project_dir,
false,
);
assert!(result.is_ok());
}
#[test]
fn execute_parallel_step() {
let config = parse_config(
r#"
[tasks]
a = "echo parallel-a"
b = "echo parallel-b"
[lanes.par]
steps = [{ parallel = ["a", "b"] }]
"#,
);
let project_dir = std::env::current_dir().unwrap();
let result = execute_lane(
"par",
&config.lanes["par"],
&config.tasks,
&project_dir,
false,
);
assert!(result.is_ok());
}
#[test]
fn parse_parallel_inline_items() {
let config = parse_config(
r#"
[tasks]
lint = "cargo clippy"
[lanes.mixed]
description = "Mixed parallel"
steps = [
{ parallel = ["lint", { run = "echo inline" }] },
]
"#,
);
match &config.lanes["mixed"].steps[0] {
Step::Parallel { parallel } => {
assert_eq!(parallel.len(), 2);
assert!(matches!(¶llel[0], ParallelItem::TaskRef(n) if n == "lint"));
assert!(matches!(¶llel[1], ParallelItem::Inline { run } if run == "echo inline"));
}
_ => panic!("expected parallel step"),
}
}
#[test]
fn execute_parallel_with_inline() {
let config = parse_config(
r#"
[tasks]
a = "echo task-a"
[lanes.mixed]
steps = [{ parallel = ["a", { run = "echo inline-b" }] }]
"#,
);
let project_dir = std::env::current_dir().unwrap();
let result = execute_lane(
"mixed",
&config.lanes["mixed"],
&config.tasks,
&project_dir,
false,
);
assert!(result.is_ok());
}
#[test]
fn execute_parallel_all_inline() {
let config = parse_config(
r#"
[tasks]
[lanes.inlines]
steps = [{ parallel = [{ run = "echo one" }, { run = "echo two" }] }]
"#,
);
let project_dir = std::env::current_dir().unwrap();
let result = execute_lane(
"inlines",
&config.lanes["inlines"],
&config.tasks,
&project_dir,
false,
);
assert!(result.is_ok());
}
#[test]
fn validate_parallel_inline_no_task_check() {
let config = parse_config(
r#"
[tasks]
[lanes.ci]
steps = [{ parallel = [{ run = "echo hello" }] }]
"#,
);
let result = validate_lane("ci", &config.lanes["ci"], &config.tasks);
assert!(result.is_ok());
}
#[test]
fn format_lane_toml_with_parallel_inline() {
let lane = LaneDef {
description: None,
steps: vec![Step::Parallel {
parallel: vec![
ParallelItem::TaskRef("lint".to_string()),
ParallelItem::Inline {
run: "echo done".to_string(),
},
],
}],
fail_fast: true,
source: None,
};
let toml = format_lane_toml("mixed", &lane);
assert!(toml.contains("\"lint\""));
assert!(toml.contains("{ run = \"echo done\" }"));
}
#[test]
fn execute_fail_fast_stops() {
let config = parse_config(
r#"
[tasks]
fail = "exit 1"
ok = "echo ok"
[lanes.ff]
fail_fast = true
steps = ["fail", "ok"]
"#,
);
let project_dir = std::env::current_dir().unwrap();
let result = execute_lane(
"ff",
&config.lanes["ff"],
&config.tasks,
&project_dir,
false,
);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("failed at step 1"));
}
#[test]
fn execute_no_fail_fast_continues() {
let config = parse_config(
r#"
[tasks]
fail = "exit 1"
ok = "echo ok"
[lanes.noff]
fail_fast = false
steps = ["fail", "ok"]
"#,
);
let project_dir = std::env::current_dir().unwrap();
let result = execute_lane(
"noff",
&config.lanes["noff"],
&config.tasks,
&project_dir,
false,
);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("1 failure"));
}
#[test]
fn execute_task_deps_in_lane() {
let config = parse_config(
r#"
[tasks.build]
cmd = "echo building"
deps = ["prep"]
[tasks.prep]
cmd = "echo preparing"
[lanes.ci]
steps = ["build"]
"#,
);
let project_dir = std::env::current_dir().unwrap();
let result = execute_lane(
"ci",
&config.lanes["ci"],
&config.tasks,
&project_dir,
false,
);
assert!(result.is_ok());
}
#[test]
fn lane_defaults_are_valid_toml() {
for project_type in &["rust", "node", "go", "python", "generic"] {
let tasks = match *project_type {
"rust" => {
"[tasks]\nfmt = \"cargo fmt\"\nlint = \"cargo clippy\"\ntest = \"cargo test\"\nbuild = \"cargo build\"\ntypecheck = \"echo ok\"\n"
}
"node" => {
"[tasks]\nlint = \"echo lint\"\ntest = \"echo test\"\nbuild = \"echo build\"\n"
}
"go" => {
"[tasks]\nfmt = \"echo fmt\"\nlint = \"echo lint\"\ntest = \"echo test\"\nbuild = \"echo build\"\n"
}
"python" => {
"[tasks]\nfmt = \"echo fmt\"\nlint = \"echo lint\"\ntypecheck = \"echo tc\"\ntest = \"echo test\"\n"
}
_ => {
"[tasks]\nlint = \"echo lint\"\ntest = \"echo test\"\nbuild = \"echo build\"\n"
}
};
let defaults = lane_defaults(project_type);
let toml_str = format!("{}{}", tasks, defaults);
let result: Result<FledgeFileWithLanes, _> = toml::from_str(&toml_str);
assert!(
result.is_ok(),
"Invalid TOML for {}: {:?}",
project_type,
result.err()
);
}
}
#[test]
fn parse_import_source_basic() {
let (owner, repo, subpath, git_ref) = parse_import_source("CorvidLabs/fledge-lanes");
assert_eq!(owner, "CorvidLabs");
assert_eq!(repo, "fledge-lanes");
assert!(subpath.is_none());
assert!(git_ref.is_none());
}
#[test]
fn parse_import_source_with_ref() {
let (owner, repo, subpath, git_ref) = parse_import_source("CorvidLabs/fledge-lanes@v1.0.0");
assert_eq!(owner, "CorvidLabs");
assert_eq!(repo, "fledge-lanes");
assert!(subpath.is_none());
assert_eq!(git_ref.unwrap(), "v1.0.0");
}
#[test]
fn parse_import_source_with_subpath() {
let (owner, repo, subpath, git_ref) = parse_import_source("CorvidLabs/fledge-lanes/rust");
assert_eq!(owner, "CorvidLabs");
assert_eq!(repo, "fledge-lanes");
assert_eq!(subpath.unwrap(), "rust");
assert!(git_ref.is_none());
}
#[test]
fn parse_import_source_with_subpath_and_ref() {
let (owner, repo, subpath, git_ref) = parse_import_source("CorvidLabs/fledge-lanes/rust@main");
assert_eq!(owner, "CorvidLabs");
assert_eq!(repo, "fledge-lanes");
assert_eq!(subpath.unwrap(), "rust");
assert_eq!(git_ref.unwrap(), "main");
}
#[test]
fn parse_import_source_full_url() {
let (owner, repo, subpath, git_ref) =
parse_import_source("https://github.com/CorvidLabs/fledge-lanes.git");
assert_eq!(owner, "CorvidLabs");
assert_eq!(repo, "fledge-lanes");
assert!(subpath.is_none());
assert!(git_ref.is_none());
}
#[test]
fn parse_import_source_url_with_ref() {
let (owner, repo, subpath, git_ref) =
parse_import_source("https://github.com/CorvidLabs/fledge-lanes@main");
assert_eq!(owner, "CorvidLabs");
assert_eq!(repo, "fledge-lanes");
assert!(subpath.is_none());
assert_eq!(git_ref.unwrap(), "main");
}
#[test]
fn format_lane_toml_sequential() {
let lane = LaneDef {
description: Some("CI pipeline".to_string()),
steps: vec![
Step::TaskRef("lint".to_string()),
Step::TaskRef("test".to_string()),
],
fail_fast: true,
source: None,
};
let toml = format_lane_toml("ci", &lane);
assert!(toml.contains("[lanes.ci]"));
assert!(toml.contains("description = \"CI pipeline\""));
assert!(toml.contains("\"lint\""));
assert!(toml.contains("\"test\""));
assert!(!toml.contains("fail_fast"));
}
#[test]
fn format_lane_toml_with_fail_fast_false() {
let lane = LaneDef {
description: None,
steps: vec![Step::TaskRef("audit".to_string())],
fail_fast: false,
source: None,
};
let toml = format_lane_toml("audit", &lane);
assert!(toml.contains("fail_fast = false"));
}
#[test]
fn format_lane_toml_with_inline() {
let lane = LaneDef {
description: None,
steps: vec![Step::Inline {
run: "echo hello".to_string(),
}],
fail_fast: true,
source: None,
};
let toml = format_lane_toml("test", &lane);
assert!(toml.contains("{ run = \"echo hello\" }"));
}
#[test]
fn format_lane_toml_with_parallel() {
let lane = LaneDef {
description: None,
steps: vec![Step::Parallel {
parallel: vec![
ParallelItem::TaskRef("lint".to_string()),
ParallelItem::TaskRef("fmt".to_string()),
],
}],
fail_fast: true,
source: None,
};
let toml = format_lane_toml("check", &lane);
assert!(toml.contains("parallel"));
assert!(toml.contains("\"lint\""));
assert!(toml.contains("\"fmt\""));
}
#[test]
fn format_lane_toml_roundtrips() {
let lane = LaneDef {
description: Some("Full CI".to_string()),
steps: vec![
Step::TaskRef("lint".to_string()),
Step::TaskRef("test".to_string()),
Step::TaskRef("build".to_string()),
],
fail_fast: true,
source: None,
};
let toml_str = format!(
"[tasks]\nlint = \"echo lint\"\ntest = \"echo test\"\nbuild = \"echo build\"\n{}",
format_lane_toml("ci", &lane)
);
let parsed: FledgeFileWithLanes = toml::from_str(&toml_str).unwrap();
assert!(parsed.lanes.contains_key("ci"));
assert_eq!(parsed.lanes["ci"].steps.len(), 3);
}
#[test]
fn base64_decode_basic() {
let encoded = "SGVsbG8gV29ybGQ=";
let decoded = base64_decode(encoded).unwrap();
assert_eq!(String::from_utf8(decoded).unwrap(), "Hello World");
}
#[test]
fn base64_decode_no_padding() {
let encoded = "Zm9v";
let decoded = base64_decode(encoded).unwrap();
assert_eq!(String::from_utf8(decoded).unwrap(), "foo");
}
#[test]
fn base64_decode_empty() {
let decoded = base64_decode("").unwrap();
assert!(decoded.is_empty());
}
#[test]
fn base64_decode_with_newlines() {
let encoded = "SGVs\nbG8=";
let cleaned: String = encoded.chars().filter(|c| !c.is_whitespace()).collect();
let decoded = base64_decode(&cleaned).unwrap();
assert_eq!(String::from_utf8(decoded).unwrap(), "Hello");
}
#[test]
fn format_duration_millis() {
let d = std::time::Duration::from_millis(42);
assert_eq!(format_duration(d), "42ms");
}
#[test]
fn format_duration_seconds() {
let d = std::time::Duration::from_millis(3456);
assert_eq!(format_duration(d), "3.456s");
}
#[test]
fn format_duration_minutes() {
let d = std::time::Duration::from_secs(125) + std::time::Duration::from_millis(100);
assert_eq!(format_duration(d), "2m 5.100s");
}
#[test]
fn format_duration_zero() {
let d = std::time::Duration::from_millis(0);
assert_eq!(format_duration(d), "0ms");
}
#[test]
fn merge_imported_lanes() {
let mut base = parse_config(
r#"
[tasks]
lint = "cargo clippy"
[lanes.ci]
steps = ["lint"]
"#,
);
let imported = parse_config(
r#"
[tasks]
lint = "overridden"
test = "cargo test"
[lanes.ci]
steps = ["lint", "test"]
[lanes.deploy]
steps = ["test"]
"#,
);
for (name, task) in imported.tasks {
base.tasks.entry(name).or_insert(task);
}
for (name, lane) in imported.lanes {
base.lanes.entry(name).or_insert(lane);
}
assert_eq!(base.tasks["lint"].cmd(), "cargo clippy");
assert_eq!(base.tasks["test"].cmd(), "cargo test");
assert_eq!(base.lanes["ci"].steps.len(), 1);
assert!(base.lanes.contains_key("deploy"));
}
#[test]
fn create_lane_repo_scaffolds_files() {
let tmp = tempfile::TempDir::new().unwrap();
create_lane_repo("my-lanes", tmp.path(), Some("Test lanes"), true, false).unwrap();
let target = tmp.path().join("my-lanes");
assert!(target.join("fledge.toml").exists());
assert!(target.join("README.md").exists());
assert!(target.join(".gitignore").exists());
let content = std::fs::read_to_string(target.join("fledge.toml")).unwrap();
let parsed: FledgeFileWithLanes = toml::from_str(&content).unwrap();
assert!(!parsed.lanes.is_empty());
assert!(!parsed.tasks.is_empty());
}
#[test]
fn create_lane_repo_fails_if_exists() {
let tmp = tempfile::TempDir::new().unwrap();
std::fs::create_dir(tmp.path().join("existing")).unwrap();
let result = create_lane_repo("existing", tmp.path(), None, true, false);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("already exists"));
}
#[test]
fn validate_valid_lanes() {
let tmp = tempfile::TempDir::new().unwrap();
std::fs::write(
tmp.path().join("fledge.toml"),
r#"
[tasks]
lint = "cargo clippy"
test = "cargo test"
[lanes.ci]
description = "CI pipeline"
steps = ["lint", "test"]
"#,
)
.unwrap();
let result = validate_lanes(tmp.path(), false, false);
assert!(result.is_ok());
}
#[test]
fn validate_undefined_task_ref() {
let tmp = tempfile::TempDir::new().unwrap();
std::fs::write(
tmp.path().join("fledge.toml"),
r#"
[tasks]
lint = "cargo clippy"
[lanes.ci]
description = "CI"
steps = ["lint", "nonexistent"]
"#,
)
.unwrap();
let result = validate_lanes(tmp.path(), false, false);
assert!(result.is_err());
}
#[test]
fn validate_empty_steps() {
let tmp = tempfile::TempDir::new().unwrap();
std::fs::write(
tmp.path().join("fledge.toml"),
r#"
[lanes.empty]
description = "Empty"
steps = []
"#,
)
.unwrap();
let result = validate_lanes(tmp.path(), false, false);
assert!(result.is_err());
}
#[test]
fn validate_missing_description_warns() {
let tmp = tempfile::TempDir::new().unwrap();
std::fs::write(
tmp.path().join("fledge.toml"),
r#"
[tasks]
lint = "cargo clippy"
[lanes.ci]
steps = ["lint"]
"#,
)
.unwrap();
let result = validate_lanes(tmp.path(), false, false);
assert!(result.is_ok());
let result = validate_lanes(tmp.path(), true, false);
assert!(result.is_err());
}
#[test]
fn validate_no_lanes_is_error() {
let tmp = tempfile::TempDir::new().unwrap();
std::fs::write(
tmp.path().join("fledge.toml"),
r#"
[tasks]
lint = "cargo clippy"
"#,
)
.unwrap();
let result = validate_lanes(tmp.path(), false, false);
assert!(result.is_err());
}
#[test]
fn validate_json_output() {
let tmp = tempfile::TempDir::new().unwrap();
std::fs::write(
tmp.path().join("fledge.toml"),
r#"
[tasks]
lint = "cargo clippy"
[lanes.ci]
description = "CI"
steps = ["lint"]
"#,
)
.unwrap();
let result = validate_lanes(tmp.path(), false, true);
assert!(result.is_ok());
}
#[test]
fn imported_lanes_get_source_tracked() {
let tmp = tempfile::TempDir::new().unwrap();
let fledge_toml = tmp.path().join("fledge.toml");
std::fs::write(
&fledge_toml,
"[tasks]\nlint = \"echo lint\"\n\n[lanes.local]\nsteps = [\"lint\"]\n",
)
.unwrap();
let lanes_dir = tmp.path().join(".fledge").join("lanes");
std::fs::create_dir_all(&lanes_dir).unwrap();
std::fs::write(
lanes_dir.join("corvidlabs-fledge-lanes.toml"),
"# Imported from CorvidLabs/fledge-lanes\n\n[tasks]\ntest = \"echo test\"\n\n[lanes.ci]\ndescription = \"CI\"\nsteps = [\"lint\", \"test\"]\n",
)
.unwrap();
std::fs::write(
lanes_dir.join("someuser-lanes.toml"),
"# Imported from someuser/lanes\n\n[lanes.deploy]\nsteps = [{ run = \"echo deploy\" }]\n",
)
.unwrap();
let _guard = crate::test_support::cwd_lock();
let prev = std::env::current_dir().unwrap();
std::env::set_current_dir(tmp.path()).unwrap();
let config = load_lane_config().unwrap();
std::env::set_current_dir(&prev).unwrap();
assert!(config.lanes["local"].source.is_none());
let ci_source = config.lanes["ci"].source.as_deref();
assert_eq!(ci_source, Some("CorvidLabs/fledge-lanes"));
assert_eq!(
determine_trust_tier(ci_source.unwrap()),
crate::trust::TrustTier::Official
);
let deploy_source = config.lanes["deploy"].source.as_deref();
assert_eq!(deploy_source, Some("someuser/lanes"));
assert_eq!(
determine_trust_tier(deploy_source.unwrap()),
crate::trust::TrustTier::Unverified
);
}
#[test]
fn list_lanes_json_includes_trust_tier() {
let mut lanes = BTreeMap::new();
lanes.insert(
"local".to_string(),
LaneDef {
description: Some("Local lane".to_string()),
steps: vec![Step::TaskRef("lint".to_string())],
fail_fast: true,
source: None,
},
);
lanes.insert(
"imported".to_string(),
LaneDef {
description: Some("Remote lane".to_string()),
steps: vec![Step::TaskRef("test".to_string())],
fail_fast: true,
source: Some("CorvidLabs/fledge-lanes".to_string()),
},
);
lanes.insert(
"third_party".to_string(),
LaneDef {
description: Some("Third party".to_string()),
steps: vec![Step::TaskRef("deploy".to_string())],
fail_fast: true,
source: Some("someuser/lanes".to_string()),
},
);
let result = list_lanes(&lanes, true);
assert!(result.is_ok());
}