use serde::de::DeserializeOwned;
use tga::{
classify::{
rules::{CategoryDef, Rule, RuleSet},
sources::{
ConfluenceSourceConfig, DatadogSourceConfig, GithubIssuesSourceConfig,
JiraSourceConfig, LinearSourceConfig, ShortcutSourceConfig,
},
taxonomy::{SubcategoryDef, TopLevelCategory},
},
core::config::{
AliasFile, AzureDevOpsConfig, DeveloperAliasEntry, FailureSignal, RepositoryConfig,
TeamMember,
},
};
type Row = (&'static str, fn() -> Option<String>);
fn parse<T: DeserializeOwned>(yaml: &str) -> T {
serde_yaml::from_str(yaml).unwrap_or_else(|e| panic!("minimal YAML {yaml:?} parses: {e}"))
}
fn eq<T: PartialEq + std::fmt::Debug>(built: T, parsed: T) -> Option<String> {
(built != parsed).then(|| format!("built {built:?}\n != yaml {parsed:?}"))
}
fn debug_eq<T: std::fmt::Debug>(built: T, parsed: T) -> Option<String> {
let (b, p) = (format!("{built:?}"), format!("{parsed:?}"));
(b != p).then(|| format!("built {b}\n != yaml {p}"))
}
const ROWS: &[Row] = &[
("JiraSourceConfig::new", || {
eq(
JiraSourceConfig::new("https://acme.atlassian.net"),
parse("base_url: https://acme.atlassian.net"),
)
}),
("GithubIssuesSourceConfig::new", || {
eq(
GithubIssuesSourceConfig::new("acme/widgets"),
parse("repo: acme/widgets"),
)
}),
("ConfluenceSourceConfig::new", || {
eq(
ConfluenceSourceConfig::new("https://acme.atlassian.net/wiki"),
parse("base_url: https://acme.atlassian.net/wiki"),
)
}),
("LinearSourceConfig::default", || {
eq(LinearSourceConfig::default(), parse("{}"))
}),
("ShortcutSourceConfig::default", || {
eq(ShortcutSourceConfig::default(), parse("{}"))
}),
("DatadogSourceConfig::default", || {
eq(DatadogSourceConfig::default(), parse("{}"))
}),
("CategoryDef::new", || {
eq(CategoryDef::new("feature"), parse("name: feature"))
}),
("SubcategoryDef::new", || {
debug_eq(
SubcategoryDef::new("payments", TopLevelCategory::Feature),
parse("name: payments\nparent: feature"),
)
}),
("Rule::new", || {
debug_eq(
Rule::new("r1", "feature"),
parse("id: r1\ncategory: feature"),
)
}),
("RuleSet::default", || {
debug_eq(RuleSet::default(), parse("rules: []"))
}),
("AliasFile::default", || {
debug_eq(AliasFile::default(), parse("developers: []"))
}),
("RepositoryConfig::new", || {
debug_eq(RepositoryConfig::new("/src/app"), parse("path: /src/app"))
}),
("TeamMember::new", || {
debug_eq(
TeamMember::new("Alice", "alice@acme.com"),
parse("name: Alice\nemail: alice@acme.com"),
)
}),
("FailureSignal::default", || {
debug_eq(FailureSignal::default(), parse("{}"))
}),
("AzureDevOpsConfig::new", || {
let b = AzureDevOpsConfig::new("https://dev.azure.com/acme", "pat-value");
let p: AzureDevOpsConfig =
parse("organization_url: https://dev.azure.com/acme\npat: pat-value");
if b.pat != p.pat {
return Some("pat differs".to_string());
}
debug_eq(b, p)
}),
("DeveloperAliasEntry::new", || {
debug_eq(
DeveloperAliasEntry::new("John Doe", "john@acme.com"),
parse("name: John Doe\nprimary_email: john@acme.com"),
)
}),
];
#[test]
fn every_public_construction_path_matches_minimal_yaml() {
let failures: Vec<String> = ROWS
.iter()
.filter_map(|(name, check)| check().map(|diff| format!("{name}: {diff}")))
.collect();
assert!(failures.is_empty(), "{}", failures.join("\n"));
}