#[cfg(test)]
mod tests {
use super::super::*;
use indoc::{formatdoc, indoc};
use tempfile::tempdir;
fn spec(id: &str, extra: &str) -> serde_json::Value {
serde_json::from_str(&format!(r#"{{"id": "{id}"{extra}}}"#)).expect("valid json")
}
#[test]
fn scope_selected_defaults_to_true_when_empty() {
assert!(scope_selected(&spec("a", r#", "scope": ["api"]"#), &[]));
}
#[test]
fn scope_selected_matches_short_name_in_spec_scope() {
let api = ["api".to_string()];
assert!(scope_selected(&spec("a", r#", "scope": ["api"]"#), &api));
assert!(!scope_selected(
&spec("a", r#", "scope": ["frontend"]"#),
&api
));
assert!(!scope_selected(&spec("a", ""), &api));
}
#[test]
fn id_selected_defaults_to_true_when_empty() {
assert!(id_selected(&spec("a", ""), &[]));
}
#[test]
fn id_selected_filters_to_given_ids() {
let ids = vec!["a".to_string(), "b".to_string()];
assert!(id_selected(&spec("a", ""), &ids));
assert!(!id_selected(&spec("c", ""), &ids));
}
fn write_project(dir: &std::path::Path) {
std::fs::write(
dir.join("spex.yaml"),
indoc! {"
project_name: demo
cli_version: 0.0.0
layout: multi-repo
agent_root: .
packages:
- package_name: demo-api
package_short_name: api
path: ../demo-api
- package_name: demo-frontend
package_short_name: frontend
path: ../demo-frontend
"},
)
.expect("write spex.yaml");
std::fs::create_dir_all(dir.join("specs")).expect("specs dir");
std::fs::write(
dir.join("specs/place-pizza-order.yaml"),
indoc! {r#"
id: place-pizza-order
title: Place a pizza order
description: Lets a customer place an order.
acceptance_criteria:
- "[ ] MUST accept a cart"
scope: [api]
"#},
)
.expect("write spec");
std::fs::write(
dir.join("specs/list-pizza-menu.yaml"),
indoc! {r#"
id: list-pizza-menu
title: List the pizza menu
description: Lets a customer browse the menu.
acceptance_criteria:
- "[ ] MUST list every active pizza"
scope: [frontend]
"#},
)
.expect("write spec");
}
fn project(dir: &std::path::Path) -> Spex {
let config = crate::config::SpexConfig::load(&dir.join(crate::config::SPEX_CONFIG_FILE))
.expect("load config");
Spex::from_config(dir.to_path_buf(), config)
}
fn args(ai: bool, id: &[&str], scope: &[&str]) -> SpecsArgs {
SpecsArgs {
ai,
id: id.iter().map(|s| s.to_string()).collect(),
scope: scope.iter().map(|s| s.to_string()).collect(),
output: None,
}
}
#[test]
fn render_with_no_flags_prints_every_spec() {
let dir = tempdir().expect("temp dir");
write_project(dir.path());
let project = project(dir.path());
let yaml = render(&project, &args(false, &[], &[])).expect("render");
assert_eq!(
yaml,
indoc! {"
- id: list-pizza-menu
title: List the pizza menu
description: Lets a customer browse the menu.
acceptance_criteria:
- '[ ] MUST list every active pizza'
scope:
- frontend
- id: place-pizza-order
title: Place a pizza order
description: Lets a customer place an order.
acceptance_criteria:
- '[ ] MUST accept a cart'
scope:
- api
"}
);
}
#[test]
fn render_with_ai_flag_adds_path() {
let dir = tempdir().expect("temp dir");
write_project(dir.path());
let project = project(dir.path());
let yaml = render(&project, &args(true, &["place-pizza-order"], &[])).expect("render");
assert_eq!(
yaml,
formatdoc! {"
- id: place-pizza-order
path: {dir}/specs/place-pizza-order.yaml
title: Place a pizza order
description: Lets a customer place an order.
acceptance_criteria:
- '[ ] MUST accept a cart'
scope:
- api
", dir = dir.path().display()}
);
}
#[test]
fn render_with_single_scope_returns_only_matching_specs() {
let dir = tempdir().expect("temp dir");
write_project(dir.path());
let project = project(dir.path());
let yaml = render(&project, &args(false, &[], &["frontend"])).expect("render");
assert_eq!(
yaml,
indoc! {"
- id: list-pizza-menu
title: List the pizza menu
description: Lets a customer browse the menu.
acceptance_criteria:
- '[ ] MUST list every active pizza'
scope:
- frontend
"}
);
}
#[test]
fn render_with_full_package_name_does_not_match_scope() {
let dir = tempdir().expect("temp dir");
write_project(dir.path());
let project = project(dir.path());
let result = render(&project, &args(false, &[], &["demo-api"]));
assert!(result.is_err());
}
#[test]
fn render_errors_on_unknown_scope() {
let dir = tempdir().expect("temp dir");
write_project(dir.path());
let project = project(dir.path());
let result = render(&project, &args(false, &[], &["missing"]));
assert!(result.is_err());
}
#[test]
fn render_errors_on_unknown_id() {
let dir = tempdir().expect("temp dir");
write_project(dir.path());
let project = project(dir.path());
let result = render(&project, &args(false, &["missing"], &[]));
assert!(result.is_err());
}
}