use std::path::PathBuf;
use crate::source::{
ConfigSource, ConfigSourceChain, ConfigSourceKind, EnvMetadataTag, FigmentNameTag,
FigmentNameTagKind, FigmentSourceKind, FigmentSourceTag,
};
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum ShikumiError {
#[error("config file not found; tried: {}", tried.iter().map(|p| p.display().to_string()).collect::<Vec<_>>().join(", "))]
NotFound { tried: Vec<PathBuf> },
#[error("config parse error: {0}")]
Parse(String),
#[error("file watch error: {0}")]
Watch(#[from] notify::Error),
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("figment error: {0}")]
Figment(#[from] Box<figment::Error>),
#[error(
"config extraction failed [layers: {}]{}{}: {error}",
display_sources(sources),
display_failing_source(sources, error),
display_field_path(&error.path)
)]
Extract {
sources: Vec<ConfigSource>,
#[source]
error: Box<figment::Error>,
},
}
fn display_sources(sources: &[ConfigSource]) -> String {
if sources.is_empty() {
"<empty>".to_owned()
} else {
sources
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(" -> ")
}
}
fn display_field_path(path: &[String]) -> String {
if path.is_empty() {
String::new()
} else {
format!(" at field `{}`", dotted_field_path(path))
}
}
pub(crate) fn dotted_field_path(path: &[String]) -> String {
path.join(".")
}
fn display_failing_source(sources: &[ConfigSource], error: &figment::Error) -> String {
resolve_failing_source(error, sources)
.map(|a| format!(" from {}", a.source))
.unwrap_or_default()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ShikumiErrorKind {
NotFound,
Parse,
Watch,
Io,
Figment,
Extract,
}
impl ShikumiErrorKind {
pub const ALL: &'static [Self] = &[
Self::NotFound,
Self::Parse,
Self::Watch,
Self::Io,
Self::Figment,
Self::Extract,
];
#[must_use]
pub fn is_figment_bearing(self) -> bool {
match self {
Self::Extract | Self::Figment => true,
Self::NotFound | Self::Parse | Self::Watch | Self::Io => false,
}
}
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::NotFound => "not-found",
Self::Parse => "parse",
Self::Watch => "watch",
Self::Io => "io",
Self::Figment => "figment",
Self::Extract => "extract",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum FieldPathLocalization {
Localized,
FigmentUnlocalized,
NotApplicable,
}
impl FieldPathLocalization {
pub const ALL: &'static [Self] = &[
Self::Localized,
Self::FigmentUnlocalized,
Self::NotApplicable,
];
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Localized => "localized",
Self::FigmentUnlocalized => "figment-unlocalized",
Self::NotApplicable => "not-applicable",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum AttributionRule {
FileBySource,
FileByMetadataName,
EnvByPrefix,
EnvByUniqueness,
DefaultsByCodeUniqueness,
}
impl AttributionRule {
pub const ALL: &'static [Self] = &[
Self::FileBySource,
Self::FileByMetadataName,
Self::EnvByPrefix,
Self::EnvByUniqueness,
Self::DefaultsByCodeUniqueness,
];
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::FileBySource => "file-by-source",
Self::FileByMetadataName => "file-by-metadata-name",
Self::EnvByPrefix => "env-by-prefix",
Self::EnvByUniqueness => "env-by-uniqueness",
Self::DefaultsByCodeUniqueness => "defaults-by-code-uniqueness",
}
}
#[must_use]
pub fn confidence(self) -> AttributionConfidence {
match self {
Self::FileBySource | Self::FileByMetadataName | Self::EnvByPrefix => {
AttributionConfidence::Exact
}
Self::EnvByUniqueness | Self::DefaultsByCodeUniqueness => {
AttributionConfidence::Fallback
}
}
}
#[must_use]
pub fn is_exact(self) -> bool {
matches!(self.confidence(), AttributionConfidence::Exact)
}
#[must_use]
pub fn is_fallback(self) -> bool {
matches!(self.confidence(), AttributionConfidence::Fallback)
}
#[must_use]
pub fn layer_kind(self) -> ConfigSourceKind {
match self {
Self::FileBySource | Self::FileByMetadataName => ConfigSourceKind::File,
Self::EnvByPrefix | Self::EnvByUniqueness => ConfigSourceKind::Env,
Self::DefaultsByCodeUniqueness => ConfigSourceKind::Defaults,
}
}
#[must_use]
pub fn metadata_axis(self) -> AttributionAxis {
match self {
Self::FileBySource | Self::DefaultsByCodeUniqueness => AttributionAxis::MetadataSource,
Self::FileByMetadataName | Self::EnvByPrefix | Self::EnvByUniqueness => {
AttributionAxis::MetadataName
}
}
}
#[must_use]
pub fn figment_source_kind(self) -> Option<FigmentSourceKind> {
match self {
Self::FileBySource => Some(FigmentSourceKind::File),
Self::DefaultsByCodeUniqueness => Some(FigmentSourceKind::Code),
Self::FileByMetadataName | Self::EnvByPrefix | Self::EnvByUniqueness => None,
}
}
#[must_use]
pub fn figment_name_tag_kind(self) -> Option<FigmentNameTagKind> {
match self {
Self::FileByMetadataName => Some(FigmentNameTagKind::Format),
Self::EnvByPrefix | Self::EnvByUniqueness => Some(FigmentNameTagKind::Env),
Self::FileBySource | Self::DefaultsByCodeUniqueness => None,
}
}
#[must_use]
pub fn file_provenance(self) -> Option<crate::FormatProvenance> {
match self {
Self::FileBySource => Some(crate::FormatProvenance::FigmentBuiltin),
Self::FileByMetadataName => Some(crate::FormatProvenance::ShikumiBuilt),
Self::EnvByPrefix | Self::EnvByUniqueness | Self::DefaultsByCodeUniqueness => None,
}
}
#[must_use]
pub fn attribution_source_kind_coordinates(self) -> Option<AttributionSourceKindCoordinates> {
self.figment_source_kind()
.map(|figment_source_kind| AttributionSourceKindCoordinates {
figment_source_kind,
layer_kind: self.layer_kind(),
})
}
#[must_use]
pub fn attribution_name_kind_coordinates(self) -> Option<AttributionNameKindCoordinates> {
self.figment_name_tag_kind()
.map(|figment_name_tag_kind| AttributionNameKindCoordinates {
figment_name_tag_kind,
layer_kind: self.layer_kind(),
})
}
#[must_use]
pub fn coordinates(self) -> AttributionCoordinates {
AttributionCoordinates {
axis: self.metadata_axis(),
layer_kind: self.layer_kind(),
confidence: self.confidence(),
}
}
#[must_use]
pub fn from_coordinates(coords: AttributionCoordinates) -> Option<Self> {
match (coords.axis, coords.layer_kind, coords.confidence) {
(
AttributionAxis::MetadataSource,
ConfigSourceKind::File,
AttributionConfidence::Exact,
) => Some(Self::FileBySource),
(
AttributionAxis::MetadataName,
ConfigSourceKind::File,
AttributionConfidence::Exact,
) => Some(Self::FileByMetadataName),
(
AttributionAxis::MetadataName,
ConfigSourceKind::Env,
AttributionConfidence::Exact,
) => Some(Self::EnvByPrefix),
(
AttributionAxis::MetadataName,
ConfigSourceKind::Env,
AttributionConfidence::Fallback,
) => Some(Self::EnvByUniqueness),
(
AttributionAxis::MetadataSource,
ConfigSourceKind::Defaults,
AttributionConfidence::Fallback,
) => Some(Self::DefaultsByCodeUniqueness),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct AttributionCoordinates {
pub axis: AttributionAxis,
pub layer_kind: ConfigSourceKind,
pub confidence: AttributionConfidence,
}
impl AttributionCoordinates {
pub const ALL: &'static [Self] = &[
Self {
axis: AttributionAxis::MetadataSource,
layer_kind: ConfigSourceKind::Defaults,
confidence: AttributionConfidence::Exact,
},
Self {
axis: AttributionAxis::MetadataSource,
layer_kind: ConfigSourceKind::Defaults,
confidence: AttributionConfidence::Fallback,
},
Self {
axis: AttributionAxis::MetadataSource,
layer_kind: ConfigSourceKind::Env,
confidence: AttributionConfidence::Exact,
},
Self {
axis: AttributionAxis::MetadataSource,
layer_kind: ConfigSourceKind::Env,
confidence: AttributionConfidence::Fallback,
},
Self {
axis: AttributionAxis::MetadataSource,
layer_kind: ConfigSourceKind::File,
confidence: AttributionConfidence::Exact,
},
Self {
axis: AttributionAxis::MetadataSource,
layer_kind: ConfigSourceKind::File,
confidence: AttributionConfidence::Fallback,
},
Self {
axis: AttributionAxis::MetadataName,
layer_kind: ConfigSourceKind::Defaults,
confidence: AttributionConfidence::Exact,
},
Self {
axis: AttributionAxis::MetadataName,
layer_kind: ConfigSourceKind::Defaults,
confidence: AttributionConfidence::Fallback,
},
Self {
axis: AttributionAxis::MetadataName,
layer_kind: ConfigSourceKind::Env,
confidence: AttributionConfidence::Exact,
},
Self {
axis: AttributionAxis::MetadataName,
layer_kind: ConfigSourceKind::Env,
confidence: AttributionConfidence::Fallback,
},
Self {
axis: AttributionAxis::MetadataName,
layer_kind: ConfigSourceKind::File,
confidence: AttributionConfidence::Exact,
},
Self {
axis: AttributionAxis::MetadataName,
layer_kind: ConfigSourceKind::File,
confidence: AttributionConfidence::Fallback,
},
];
#[must_use]
pub fn is_realizable(self) -> bool {
AttributionRule::from_coordinates(self).is_some()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct ErrorLocalizationCoordinates {
pub kind: ShikumiErrorKind,
pub localization: FieldPathLocalization,
}
impl ErrorLocalizationCoordinates {
pub const ALL: &'static [Self] = &[
Self {
kind: ShikumiErrorKind::NotFound,
localization: FieldPathLocalization::Localized,
},
Self {
kind: ShikumiErrorKind::NotFound,
localization: FieldPathLocalization::FigmentUnlocalized,
},
Self {
kind: ShikumiErrorKind::NotFound,
localization: FieldPathLocalization::NotApplicable,
},
Self {
kind: ShikumiErrorKind::Parse,
localization: FieldPathLocalization::Localized,
},
Self {
kind: ShikumiErrorKind::Parse,
localization: FieldPathLocalization::FigmentUnlocalized,
},
Self {
kind: ShikumiErrorKind::Parse,
localization: FieldPathLocalization::NotApplicable,
},
Self {
kind: ShikumiErrorKind::Watch,
localization: FieldPathLocalization::Localized,
},
Self {
kind: ShikumiErrorKind::Watch,
localization: FieldPathLocalization::FigmentUnlocalized,
},
Self {
kind: ShikumiErrorKind::Watch,
localization: FieldPathLocalization::NotApplicable,
},
Self {
kind: ShikumiErrorKind::Io,
localization: FieldPathLocalization::Localized,
},
Self {
kind: ShikumiErrorKind::Io,
localization: FieldPathLocalization::FigmentUnlocalized,
},
Self {
kind: ShikumiErrorKind::Io,
localization: FieldPathLocalization::NotApplicable,
},
Self {
kind: ShikumiErrorKind::Figment,
localization: FieldPathLocalization::Localized,
},
Self {
kind: ShikumiErrorKind::Figment,
localization: FieldPathLocalization::FigmentUnlocalized,
},
Self {
kind: ShikumiErrorKind::Figment,
localization: FieldPathLocalization::NotApplicable,
},
Self {
kind: ShikumiErrorKind::Extract,
localization: FieldPathLocalization::Localized,
},
Self {
kind: ShikumiErrorKind::Extract,
localization: FieldPathLocalization::FigmentUnlocalized,
},
Self {
kind: ShikumiErrorKind::Extract,
localization: FieldPathLocalization::NotApplicable,
},
];
#[must_use]
pub fn is_realizable(self) -> bool {
self.kind.is_figment_bearing()
== !matches!(self.localization, FieldPathLocalization::NotApplicable)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct AttributionSourceKindCoordinates {
pub figment_source_kind: FigmentSourceKind,
pub layer_kind: ConfigSourceKind,
}
impl AttributionSourceKindCoordinates {
pub const ALL: &'static [Self] = &[
Self {
figment_source_kind: FigmentSourceKind::File,
layer_kind: ConfigSourceKind::Defaults,
},
Self {
figment_source_kind: FigmentSourceKind::File,
layer_kind: ConfigSourceKind::Env,
},
Self {
figment_source_kind: FigmentSourceKind::File,
layer_kind: ConfigSourceKind::File,
},
Self {
figment_source_kind: FigmentSourceKind::Code,
layer_kind: ConfigSourceKind::Defaults,
},
Self {
figment_source_kind: FigmentSourceKind::Code,
layer_kind: ConfigSourceKind::Env,
},
Self {
figment_source_kind: FigmentSourceKind::Code,
layer_kind: ConfigSourceKind::File,
},
Self {
figment_source_kind: FigmentSourceKind::Custom,
layer_kind: ConfigSourceKind::Defaults,
},
Self {
figment_source_kind: FigmentSourceKind::Custom,
layer_kind: ConfigSourceKind::Env,
},
Self {
figment_source_kind: FigmentSourceKind::Custom,
layer_kind: ConfigSourceKind::File,
},
];
#[must_use]
pub fn is_realizable(self) -> bool {
matches!(
(self.figment_source_kind, self.layer_kind),
(FigmentSourceKind::File, ConfigSourceKind::File)
| (FigmentSourceKind::Code, ConfigSourceKind::Defaults)
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct AttributionNameKindCoordinates {
pub figment_name_tag_kind: FigmentNameTagKind,
pub layer_kind: ConfigSourceKind,
}
impl AttributionNameKindCoordinates {
pub const ALL: &'static [Self] = &[
Self {
figment_name_tag_kind: FigmentNameTagKind::Format,
layer_kind: ConfigSourceKind::Defaults,
},
Self {
figment_name_tag_kind: FigmentNameTagKind::Format,
layer_kind: ConfigSourceKind::Env,
},
Self {
figment_name_tag_kind: FigmentNameTagKind::Format,
layer_kind: ConfigSourceKind::File,
},
Self {
figment_name_tag_kind: FigmentNameTagKind::Env,
layer_kind: ConfigSourceKind::Defaults,
},
Self {
figment_name_tag_kind: FigmentNameTagKind::Env,
layer_kind: ConfigSourceKind::Env,
},
Self {
figment_name_tag_kind: FigmentNameTagKind::Env,
layer_kind: ConfigSourceKind::File,
},
];
#[must_use]
pub fn is_realizable(self) -> bool {
matches!(
(self.figment_name_tag_kind, self.layer_kind),
(FigmentNameTagKind::Format, ConfigSourceKind::File)
| (FigmentNameTagKind::Env, ConfigSourceKind::Env)
)
}
}
impl crate::ClosedAxis for ShikumiErrorKind {
const ALL: &'static [Self] = Self::ALL;
}
impl crate::ClosedAxisLabel for ShikumiErrorKind {
fn as_str(self) -> &'static str {
Self::as_str(self)
}
}
impl crate::ClosedAxis for FieldPathLocalization {
const ALL: &'static [Self] = Self::ALL;
}
impl crate::ClosedAxisLabel for FieldPathLocalization {
fn as_str(self) -> &'static str {
Self::as_str(self)
}
}
impl crate::ClosedAxis for AttributionRule {
const ALL: &'static [Self] = Self::ALL;
}
impl crate::ClosedAxisLabel for AttributionRule {
fn as_str(self) -> &'static str {
Self::as_str(self)
}
}
impl crate::ClosedAxis for AttributionConfidence {
const ALL: &'static [Self] = Self::ALL;
}
impl crate::ClosedAxis for AttributionAxis {
const ALL: &'static [Self] = Self::ALL;
}
impl crate::ClosedAxis for AttributionCoordinates {
const ALL: &'static [Self] = Self::ALL;
}
impl crate::ProductCube for AttributionCoordinates {
fn is_realizable(self) -> bool {
Self::is_realizable(self)
}
}
impl crate::PartialInverseCube for AttributionCoordinates {
type Image = AttributionRule;
fn invert(self) -> Option<AttributionRule> {
AttributionRule::from_coordinates(self)
}
fn forward(image: AttributionRule) -> Self {
image.coordinates()
}
}
impl crate::ClosedAxis for ErrorLocalizationCoordinates {
const ALL: &'static [Self] = Self::ALL;
}
impl crate::ProductCube for ErrorLocalizationCoordinates {
fn is_realizable(self) -> bool {
Self::is_realizable(self)
}
}
impl crate::ClosedAxis for AttributionSourceKindCoordinates {
const ALL: &'static [Self] = Self::ALL;
}
impl crate::ProductCube for AttributionSourceKindCoordinates {
fn is_realizable(self) -> bool {
Self::is_realizable(self)
}
}
impl crate::ClosedAxis for AttributionNameKindCoordinates {
const ALL: &'static [Self] = Self::ALL;
}
impl crate::ProductCube for AttributionNameKindCoordinates {
fn is_realizable(self) -> bool {
Self::is_realizable(self)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum AttributionConfidence {
Exact,
Fallback,
}
impl AttributionConfidence {
pub const ALL: &'static [Self] = &[Self::Exact, Self::Fallback];
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Exact => "exact",
Self::Fallback => "fallback",
}
}
}
impl crate::ClosedAxisLabel for AttributionConfidence {
fn as_str(self) -> &'static str {
Self::as_str(self)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum AttributionAxis {
MetadataSource,
MetadataName,
}
impl AttributionAxis {
pub const ALL: &'static [Self] = &[Self::MetadataSource, Self::MetadataName];
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::MetadataSource => "metadata-source",
Self::MetadataName => "metadata-name",
}
}
}
impl crate::ClosedAxisLabel for AttributionAxis {
fn as_str(self) -> &'static str {
Self::as_str(self)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct FailingSourceAttribution<'a> {
pub source: &'a ConfigSource,
pub rule: AttributionRule,
}
impl<'a> FailingSourceAttribution<'a> {
pub(crate) fn new(source: &'a ConfigSource, rule: AttributionRule) -> Self {
Self { source, rule }
}
#[must_use]
pub fn confidence(self) -> AttributionConfidence {
self.rule.confidence()
}
#[must_use]
pub fn layer_kind(self) -> ConfigSourceKind {
self.rule.layer_kind()
}
#[must_use]
pub fn metadata_axis(self) -> AttributionAxis {
self.rule.metadata_axis()
}
#[must_use]
pub fn figment_source_kind(self) -> Option<FigmentSourceKind> {
self.rule.figment_source_kind()
}
#[must_use]
pub fn figment_name_tag_kind(self) -> Option<FigmentNameTagKind> {
self.rule.figment_name_tag_kind()
}
#[must_use]
pub fn attribution_source_kind_coordinates(self) -> Option<AttributionSourceKindCoordinates> {
self.rule.attribution_source_kind_coordinates()
}
#[must_use]
pub fn attribution_name_kind_coordinates(self) -> Option<AttributionNameKindCoordinates> {
self.rule.attribution_name_kind_coordinates()
}
#[must_use]
pub fn coordinates(self) -> AttributionCoordinates {
self.rule.coordinates()
}
#[must_use]
pub fn file_provenance(self) -> Option<crate::FormatProvenance> {
self.rule.file_provenance()
}
}
fn resolve_failing_source<'a>(
error: &figment::Error,
chain: &'a [ConfigSource],
) -> Option<FailingSourceAttribution<'a>> {
let md = error.metadata.as_ref()?;
let source_tag = md.source.as_ref().and_then(FigmentSourceTag::classify);
if let Some(FigmentSourceTag::File(p)) = source_tag
&& let Some(hit) = chain.find_file(p)
{
return Some(FailingSourceAttribution::new(
hit,
AttributionRule::FileBySource,
));
}
match FigmentNameTag::classify(md.name.as_ref()) {
Some(FigmentNameTag::Format(tag)) => {
if let Some(hit) = chain.find_file(tag.path) {
return Some(FailingSourceAttribution::new(
hit,
AttributionRule::FileByMetadataName,
));
}
}
Some(FigmentNameTag::Env(env_tag)) => {
if let EnvMetadataTag::Prefixed(prefix_upper) = env_tag
&& let Some(hit) = chain.find_env_by_prefix(prefix_upper)
{
return Some(FailingSourceAttribution::new(
hit,
AttributionRule::EnvByPrefix,
));
}
if let Some(only) = chain.unique_of_kind(ConfigSourceKind::Env) {
return Some(FailingSourceAttribution::new(
only,
AttributionRule::EnvByUniqueness,
));
}
}
None => {}
}
if matches!(source_tag, Some(FigmentSourceTag::Code(_)))
&& let Some(only) = chain.unique_of_kind(ConfigSourceKind::Defaults)
{
return Some(FailingSourceAttribution::new(
only,
AttributionRule::DefaultsByCodeUniqueness,
));
}
None
}
impl ShikumiError {
#[must_use]
pub fn kind(&self) -> ShikumiErrorKind {
match self {
Self::NotFound { .. } => ShikumiErrorKind::NotFound,
Self::Parse(_) => ShikumiErrorKind::Parse,
Self::Watch(_) => ShikumiErrorKind::Watch,
Self::Io(_) => ShikumiErrorKind::Io,
Self::Figment(_) => ShikumiErrorKind::Figment,
Self::Extract { .. } => ShikumiErrorKind::Extract,
}
}
#[must_use]
pub fn is_not_found(&self) -> bool {
matches!(self.kind(), ShikumiErrorKind::NotFound)
}
#[must_use]
pub fn is_parse(&self) -> bool {
matches!(self.kind(), ShikumiErrorKind::Parse)
}
#[must_use]
pub fn tried_paths(&self) -> Option<&[PathBuf]> {
match self {
Self::NotFound { tried } => Some(tried),
_ => None,
}
}
#[must_use]
pub fn sources(&self) -> Option<&[ConfigSource]> {
match self {
Self::Extract { sources, .. } => Some(sources),
_ => None,
}
}
#[must_use]
pub fn field_path(&self) -> Option<&[String]> {
match self {
Self::Extract { error, .. } | Self::Figment(error) => Some(&error.path),
_ => None,
}
}
#[must_use]
pub fn field_path_dotted(&self) -> Option<String> {
self.field_path().map(dotted_field_path)
}
#[must_use]
pub fn field_path_localization(&self) -> FieldPathLocalization {
match self.field_path() {
Some(path) if !path.is_empty() => FieldPathLocalization::Localized,
Some(_) => FieldPathLocalization::FigmentUnlocalized,
None => FieldPathLocalization::NotApplicable,
}
}
#[must_use]
pub fn error_localization_coordinates(&self) -> ErrorLocalizationCoordinates {
ErrorLocalizationCoordinates {
kind: self.kind(),
localization: self.field_path_localization(),
}
}
#[must_use]
pub fn failing_source(&self) -> Option<&ConfigSource> {
self.failing_attribution().map(|a| a.source)
}
#[must_use]
pub fn failing_attribution(&self) -> Option<FailingSourceAttribution<'_>> {
match self {
Self::Extract { sources, error } => resolve_failing_source(error, sources),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn not_found_display_lists_paths() {
let err = ShikumiError::NotFound {
tried: vec![PathBuf::from("/a/b.yaml"), PathBuf::from("/c/d.toml")],
};
let msg = err.to_string();
assert!(msg.contains("/a/b.yaml"), "error should list first path");
assert!(msg.contains("/c/d.toml"), "error should list second path");
assert!(msg.contains(", "), "paths should be comma-separated");
}
#[test]
fn not_found_empty_tried() {
let err = ShikumiError::NotFound { tried: vec![] };
let msg = err.to_string();
assert!(msg.contains("config file not found"));
}
#[test]
fn parse_error_display() {
let err = ShikumiError::Parse("unexpected token".to_owned());
let msg = err.to_string();
assert!(msg.contains("unexpected token"));
}
#[test]
fn figment_error_from_conversion() {
let figment = figment::Figment::new();
let result: Result<String, figment::Error> = figment.extract();
let figment_err = result.unwrap_err();
let shikumi_err: ShikumiError = Box::new(figment_err).into();
assert!(
matches!(shikumi_err, ShikumiError::Figment(_)),
"expected Figment variant"
);
let msg = shikumi_err.to_string();
assert!(!msg.is_empty());
}
#[test]
fn is_not_found_helper() {
let err = ShikumiError::NotFound {
tried: vec![PathBuf::from("/a")],
};
assert!(err.is_not_found());
assert!(!err.is_parse());
}
#[test]
fn is_parse_helper() {
let err = ShikumiError::Parse("bad".to_owned());
assert!(err.is_parse());
assert!(!err.is_not_found());
}
#[test]
fn tried_paths_returns_paths_for_not_found() {
let paths = vec![PathBuf::from("/x"), PathBuf::from("/y")];
let err = ShikumiError::NotFound {
tried: paths.clone(),
};
assert_eq!(err.tried_paths(), Some(paths.as_slice()));
let parse_err = ShikumiError::Parse("bad".to_owned());
assert_eq!(parse_err.tried_paths(), None);
}
#[test]
fn io_error_from_conversion() {
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file gone");
let shikumi_err: ShikumiError = io_err.into();
assert!(matches!(shikumi_err, ShikumiError::Io(_)));
assert!(shikumi_err.to_string().contains("file gone"));
}
#[test]
fn error_is_debug_printable() {
let err = ShikumiError::Parse("test".to_owned());
let debug = format!("{err:?}");
assert!(debug.contains("Parse"));
}
#[test]
fn watch_error_from_conversion() {
let notify_err = notify::Error::generic("test watcher error");
let shikumi_err: ShikumiError = notify_err.into();
assert!(
matches!(shikumi_err, ShikumiError::Watch(_)),
"expected Watch variant"
);
let msg = shikumi_err.to_string();
assert!(msg.contains("test watcher error"));
}
#[test]
fn watch_error_display() {
let notify_err = notify::Error::generic("poll failed");
let err: ShikumiError = notify_err.into();
let msg = err.to_string();
assert!(msg.contains("file watch error"));
assert!(msg.contains("poll failed"));
}
#[test]
fn figment_error_display_contains_context() {
let figment = figment::Figment::new();
let result: Result<String, figment::Error> = figment.extract();
let figment_err = result.unwrap_err();
let err: ShikumiError = Box::new(figment_err).into();
let msg = err.to_string();
assert!(msg.contains("figment error"), "should have figment prefix");
}
#[test]
fn error_source_chain() {
use std::error::Error;
let notify_err = notify::Error::generic("test");
let err: ShikumiError = notify_err.into();
assert!(err.source().is_some(), "Watch variant should have a source");
let parse_err = ShikumiError::Parse("test".to_owned());
assert!(
parse_err.source().is_none(),
"Parse variant should not have a source"
);
}
#[test]
fn not_found_single_path() {
let err = ShikumiError::NotFound {
tried: vec![PathBuf::from("/only/one.yaml")],
};
let msg = err.to_string();
assert!(msg.contains("/only/one.yaml"));
assert!(!msg.contains(", "), "single path should have no comma");
}
fn fake_figment_error() -> Box<figment::Error> {
let figment = figment::Figment::new();
let result: Result<String, figment::Error> = figment.extract();
Box::new(result.unwrap_err())
}
#[test]
fn extract_display_lists_layers_in_order() {
let err = ShikumiError::Extract {
sources: vec![
ConfigSource::Defaults,
ConfigSource::Env("APP_".to_owned()),
ConfigSource::File(PathBuf::from("/etc/app.yaml")),
],
error: fake_figment_error(),
};
let msg = err.to_string();
assert!(msg.contains("config extraction failed"));
assert!(msg.contains("defaults"));
assert!(msg.contains("env(APP_)"));
assert!(msg.contains("file(/etc/app.yaml)"));
let d = msg.find("defaults").unwrap();
let e = msg.find("env(APP_)").unwrap();
let f = msg.find("file(/etc/app.yaml)").unwrap();
assert!(d < e && e < f, "layers must render in merge order");
}
#[test]
fn extract_display_with_empty_sources() {
let err = ShikumiError::Extract {
sources: vec![],
error: fake_figment_error(),
};
let msg = err.to_string();
assert!(msg.contains("config extraction failed"));
assert!(msg.contains("<empty>"));
}
#[test]
fn extract_carries_source_chain_via_helper() {
let chain = vec![
ConfigSource::Env("APP_".to_owned()),
ConfigSource::File(PathBuf::from("/x.yaml")),
];
let err = ShikumiError::Extract {
sources: chain.clone(),
error: fake_figment_error(),
};
assert_eq!(err.sources(), Some(chain.as_slice()));
}
#[test]
fn sources_helper_returns_none_for_other_variants() {
assert!(ShikumiError::Parse("x".to_owned()).sources().is_none());
assert!(
ShikumiError::NotFound {
tried: vec![PathBuf::from("/a")]
}
.sources()
.is_none()
);
assert!(
ShikumiError::Figment(fake_figment_error())
.sources()
.is_none()
);
}
#[test]
fn extract_source_chain_preserves_figment_error() {
use std::error::Error;
let err = ShikumiError::Extract {
sources: vec![ConfigSource::Defaults],
error: fake_figment_error(),
};
let src = err.source().expect("Extract should expose a #[source]");
assert!(!format!("{src}").is_empty());
}
#[test]
fn extract_is_distinct_from_figment_variant() {
let extract = ShikumiError::Extract {
sources: vec![ConfigSource::Defaults],
error: fake_figment_error(),
};
let figment = ShikumiError::Figment(fake_figment_error());
assert!(matches!(extract, ShikumiError::Extract { .. }));
assert!(matches!(figment, ShikumiError::Figment(_)));
assert_ne!(extract.to_string(), figment.to_string());
}
fn extract_error_with_typed_field_path() -> ShikumiError {
use crate::provider::ProviderChain;
use serde::Deserialize;
#[derive(Deserialize, Debug)]
struct Cfg {
#[allow(dead_code)]
count: u32,
}
let dir = tempfile::TempDir::new().unwrap();
let file = dir.path().join("typed.yaml");
std::fs::write(&file, "count: not_a_number\n").unwrap();
let err = ProviderChain::new()
.with_file(&file)
.extract::<Cfg>()
.unwrap_err();
drop(dir);
err
}
#[test]
fn field_path_none_for_non_figment_variants() {
assert!(ShikumiError::Parse("x".to_owned()).field_path().is_none());
assert!(
ShikumiError::NotFound {
tried: vec![PathBuf::from("/a")]
}
.field_path()
.is_none()
);
let io = std::io::Error::new(std::io::ErrorKind::NotFound, "x");
let io_err: ShikumiError = io.into();
assert!(io_err.field_path().is_none());
}
#[test]
fn field_path_some_empty_for_extract_without_localized_field() {
let err = ShikumiError::Extract {
sources: vec![],
error: fake_figment_error(),
};
let path = err
.field_path()
.expect("Extract always exposes a (possibly empty) field path");
assert!(
path.is_empty(),
"no localized field, but accessor is Some(&[])"
);
}
#[test]
fn field_path_some_empty_for_figment_variant_without_localized_field() {
let err = ShikumiError::Figment(fake_figment_error());
let path = err
.field_path()
.expect("Figment always exposes a (possibly empty) field path");
assert!(path.is_empty());
}
#[test]
fn field_path_carries_offending_field_for_typed_failure() {
let err = extract_error_with_typed_field_path();
let path = err.field_path().expect("Extract exposes field path");
assert_eq!(
path,
&["count".to_owned()],
"figment should localize the offending key"
);
}
#[test]
fn extract_display_includes_field_path_segment_when_localized() {
let err = extract_error_with_typed_field_path();
let msg = err.to_string();
assert!(
msg.contains("at field `count`"),
"rendered error must cite the failing field; got: {msg}"
);
}
fn extract_error_with_nested_field_path() -> ShikumiError {
use crate::provider::ProviderChain;
use serde::Deserialize;
#[derive(Deserialize, Debug)]
struct Inner {
#[allow(dead_code)]
padding: u32,
}
#[derive(Deserialize, Debug)]
struct Cfg {
#[allow(dead_code)]
options: Inner,
}
let dir = tempfile::TempDir::new().unwrap();
let file = dir.path().join("nested.yaml");
std::fs::write(&file, "options:\n padding: not_a_number\n").unwrap();
let err = ProviderChain::new()
.with_file(&file)
.extract::<Cfg>()
.unwrap_err();
drop(dir);
err
}
#[test]
fn field_path_dotted_none_for_non_figment_variants() {
assert!(
ShikumiError::Parse("x".to_owned())
.field_path_dotted()
.is_none()
);
assert!(
ShikumiError::NotFound {
tried: vec![PathBuf::from("/a")]
}
.field_path_dotted()
.is_none()
);
let io = std::io::Error::new(std::io::ErrorKind::NotFound, "x");
let io_err: ShikumiError = io.into();
assert!(io_err.field_path_dotted().is_none());
}
#[test]
fn field_path_dotted_some_empty_for_unlocalized_extract() {
let err = ShikumiError::Extract {
sources: vec![],
error: fake_figment_error(),
};
assert_eq!(err.field_path_dotted().as_deref(), Some(""));
}
#[test]
fn field_path_dotted_joins_single_segment() {
let err = extract_error_with_typed_field_path();
assert_eq!(err.field_path_dotted().as_deref(), Some("count"));
}
#[test]
fn field_path_dotted_joins_nested_segments_with_dot() {
let err = extract_error_with_nested_field_path();
assert_eq!(
err.field_path_dotted().as_deref(),
Some("options.padding"),
"nested key must render dotted"
);
}
#[test]
fn field_path_dotted_matches_embedded_display_segment() {
let err = extract_error_with_nested_field_path();
let dotted = err.field_path_dotted().expect("localized");
let msg = err.to_string();
assert!(
msg.contains(&format!("at field `{dotted}`")),
"display segment must quote the accessor's dotted path; got: {msg}"
);
}
#[test]
fn extract_display_omits_field_path_segment_when_empty() {
let err = ShikumiError::Extract {
sources: vec![ConfigSource::Defaults],
error: fake_figment_error(),
};
let msg = err.to_string();
assert!(!msg.contains("at field"), "no path → no `at field` segment");
assert!(msg.contains("[layers: defaults]:"));
}
fn extract_error_with_file_path_failure() -> (tempfile::TempDir, ShikumiError) {
use crate::provider::ProviderChain;
#[derive(serde::Deserialize, Debug)]
struct Cfg {
#[allow(dead_code)]
count: u32,
}
let dir = tempfile::TempDir::new().unwrap();
let file = dir.path().join("typed.yaml");
std::fs::write(&file, "count: not_a_number\n").unwrap();
let err = ProviderChain::new()
.with_env("FAILING_SRC_FILE_NOTSET_")
.with_file(&file)
.extract::<Cfg>()
.unwrap_err();
(dir, err)
}
#[test]
fn failing_source_pins_file_layer_for_typed_file_failure() {
let (dir, err) = extract_error_with_file_path_failure();
let s = err
.failing_source()
.expect("Extract attributes failure to a recorded source");
assert!(s.is_file(), "expected failing source to be a file layer");
assert_eq!(s.as_path(), Some(dir.path().join("typed.yaml").as_path()));
}
#[test]
fn failing_source_pins_env_layer_when_env_provides_offending_field() {
use crate::provider::ProviderChain;
#[derive(serde::Deserialize, Debug)]
struct Cfg {
#[allow(dead_code)]
count: u32,
}
let var = "FAILSRC_ENV_COUNT";
unsafe { std::env::set_var(var, "not_a_number") };
let err = ProviderChain::new()
.with_env("FAILSRC_ENV_")
.extract::<Cfg>()
.unwrap_err();
unsafe { std::env::remove_var(var) };
let s = err
.failing_source()
.expect("env-only failure must attribute to the env layer");
assert!(s.is_env(), "expected failing source to be the env layer");
assert_eq!(s.as_env_prefix(), Some("FAILSRC_ENV_"));
}
#[test]
fn failing_source_distinguishes_env_from_file_in_layered_chain() {
use crate::provider::ProviderChain;
#[derive(serde::Deserialize, Debug)]
struct Cfg {
#[allow(dead_code)]
count: u32,
}
let dir = tempfile::TempDir::new().unwrap();
let file = dir.path().join("ok.yaml");
std::fs::write(&file, "name: present\n").unwrap();
let var = "FAILSRC_DISCRIM_COUNT";
unsafe { std::env::set_var(var, "not_a_number") };
let err = ProviderChain::new()
.with_file(&file)
.with_env("FAILSRC_DISCRIM_")
.extract::<Cfg>()
.unwrap_err();
unsafe { std::env::remove_var(var) };
let s = err
.failing_source()
.expect("Extract must attribute the failure");
assert_eq!(
s.as_env_prefix(),
Some("FAILSRC_DISCRIM_"),
"env (the actual offender) must win over the unrelated file layer"
);
}
#[test]
fn failing_source_none_for_figment_variant() {
let err = ShikumiError::Figment(fake_figment_error());
assert!(err.failing_source().is_none());
}
#[test]
fn failing_source_none_for_non_figment_variants() {
assert!(
ShikumiError::Parse("x".to_owned())
.failing_source()
.is_none()
);
assert!(
ShikumiError::NotFound {
tried: vec![PathBuf::from("/a")]
}
.failing_source()
.is_none()
);
let io = std::io::Error::new(std::io::ErrorKind::NotFound, "x");
let io_err: ShikumiError = io.into();
assert!(io_err.failing_source().is_none());
}
#[test]
fn failing_source_none_when_no_metadata_attached() {
let err = ShikumiError::Extract {
sources: vec![ConfigSource::Defaults, ConfigSource::Env("X_".to_owned())],
error: fake_figment_error(),
};
assert!(
err.failing_source().is_none(),
"no metadata → no attribution"
);
}
#[test]
fn failing_source_none_when_chain_missing_matching_entry() {
let (_dir, real) = extract_error_with_file_path_failure();
let ShikumiError::Extract { error: inner, .. } = real else {
unreachable!();
};
let err = ShikumiError::Extract {
sources: vec![ConfigSource::Defaults], error: inner,
};
assert!(err.failing_source().is_none());
}
#[test]
fn extract_display_includes_failing_source_segment_when_known() {
let (dir, err) = extract_error_with_file_path_failure();
let path_disp = dir.path().join("typed.yaml").display().to_string();
let msg = err.to_string();
assert!(
msg.contains(&format!("from file({path_disp})")),
"rendered error must cite the failing layer; got: {msg}"
);
}
#[test]
fn extract_display_omits_failing_source_segment_when_unknown() {
let err = ShikumiError::Extract {
sources: vec![ConfigSource::Defaults],
error: fake_figment_error(),
};
let msg = err.to_string();
assert!(
!msg.contains(" from "),
"no attribution → no `from` segment; got: {msg}"
);
}
#[test]
fn extract_display_orders_segments_layers_then_from_then_field() {
let (_dir, err) = extract_error_with_file_path_failure();
let msg = err.to_string();
let l = msg.find("[layers:").expect("layers segment");
let f = msg.find(" from ").expect("from segment");
let a = msg.find(" at field ").expect("field segment");
assert!(l < f && f < a, "segment order: layers -> from -> at field");
}
#[test]
fn failing_source_env_match_is_case_insensitive() {
use crate::provider::ProviderChain;
#[derive(serde::Deserialize, Debug)]
struct Cfg {
#[allow(dead_code)]
count: u32,
}
let var = "FAILSRC_CASE_COUNT";
unsafe { std::env::set_var(var, "not_a_number") };
let err = ProviderChain::new()
.with_env("failsrc_case_") .extract::<Cfg>()
.unwrap_err();
unsafe { std::env::remove_var(var) };
let s = err.failing_source().expect("env attribution");
assert_eq!(s.as_env_prefix(), Some("failsrc_case_"));
}
#[test]
fn field_path_preserves_dotted_segments_via_with_path() {
let raw = figment::Error::from("typed".to_owned()).with_path("window.size");
let err = ShikumiError::Extract {
sources: vec![],
error: Box::new(raw),
};
let path = err.field_path().expect("Extract exposes field path");
assert_eq!(
path,
&["window".to_owned(), "size".to_owned()],
"segments must be preserved, not collapsed"
);
assert!(err.to_string().contains("at field `window.size`"));
}
fn synthetic_error_with_metadata_name(name: &'static str) -> Box<figment::Error> {
let mut e = figment::Error::from("synth".to_owned());
e.metadata = Some(figment::Metadata::named(name));
Box::new(e)
}
#[test]
fn failing_attribution_rule_file_by_source_for_yaml_extract() {
let (dir, err) = extract_error_with_file_path_failure();
let attr = err
.failing_attribution()
.expect("typed file failure must attribute");
assert_eq!(attr.rule, AttributionRule::FileBySource);
assert_eq!(
attr.source.as_path(),
Some(dir.path().join("typed.yaml").as_path())
);
}
#[test]
fn failing_attribution_rule_file_by_metadata_name_for_shikumi_provider() {
let path = PathBuf::from("/etc/app/app.nix");
let name = "nix: /etc/app/app.nix";
let chain = vec![ConfigSource::Defaults, ConfigSource::File(path.clone())];
let err = ShikumiError::Extract {
sources: chain,
error: synthetic_error_with_metadata_name(name),
};
let attr = err
.failing_attribution()
.expect("shikumi-provider tag must attribute");
assert_eq!(attr.rule, AttributionRule::FileByMetadataName);
assert_eq!(attr.source.as_path(), Some(path.as_path()));
}
#[test]
fn failing_attribution_rule_env_by_prefix_when_chain_has_matching_env() {
let chain = vec![
ConfigSource::Defaults,
ConfigSource::Env("MYAPP_".to_owned()),
ConfigSource::Env("OTHER_".to_owned()),
];
let err = ShikumiError::Extract {
sources: chain,
error: synthetic_error_with_metadata_name("`MYAPP_` environment variable(s)"),
};
let attr = err
.failing_attribution()
.expect("env-prefix tag must attribute");
assert_eq!(attr.rule, AttributionRule::EnvByPrefix);
assert_eq!(attr.source.as_env_prefix(), Some("MYAPP_"));
}
#[test]
fn failing_attribution_rule_env_by_uniqueness_for_unmatched_prefix() {
let chain = vec![
ConfigSource::Defaults,
ConfigSource::Env("ONLY_".to_owned()),
ConfigSource::File(PathBuf::from("/etc/app.yaml")),
];
let err = ShikumiError::Extract {
sources: chain,
error: synthetic_error_with_metadata_name("`UNRELATED_` environment variable(s)"),
};
let attr = err
.failing_attribution()
.expect("unique-env fallback must attribute");
assert_eq!(attr.rule, AttributionRule::EnvByUniqueness);
assert_eq!(attr.source.as_env_prefix(), Some("ONLY_"));
}
#[test]
fn failing_attribution_rule_env_by_uniqueness_for_bare_env_tag() {
let chain = vec![ConfigSource::Env("BARE_".to_owned())];
let err = ShikumiError::Extract {
sources: chain,
error: synthetic_error_with_metadata_name("environment variable(s)"),
};
let attr = err.failing_attribution().expect("bare-env must attribute");
assert_eq!(attr.rule, AttributionRule::EnvByUniqueness);
assert_eq!(attr.source.as_env_prefix(), Some("BARE_"));
}
#[test]
fn failing_attribution_rule_defaults_by_code_uniqueness_for_serialized() {
use crate::provider::ProviderChain;
use serde::Serialize;
#[derive(Serialize)]
struct Bad {
count: String, }
#[derive(serde::Deserialize, Debug)]
struct Cfg {
#[allow(dead_code)]
count: u32,
}
let err = ProviderChain::new()
.with_defaults(&Bad {
count: "not_a_number".into(),
})
.extract::<Cfg>()
.unwrap_err();
let attr = err
.failing_attribution()
.expect("defaults-only failure must attribute");
assert_eq!(attr.rule, AttributionRule::DefaultsByCodeUniqueness);
assert!(attr.source.is_defaults());
}
#[test]
fn failing_attribution_none_for_no_metadata() {
let err = ShikumiError::Extract {
sources: vec![ConfigSource::Defaults, ConfigSource::Env("X_".to_owned())],
error: fake_figment_error(),
};
assert!(err.failing_attribution().is_none());
}
#[test]
fn failing_attribution_none_when_chain_lacks_matching_entry() {
let err = ShikumiError::Extract {
sources: vec![ConfigSource::File(PathBuf::from("/other.yaml"))],
error: synthetic_error_with_metadata_name("nix: /etc/app/app.nix"),
};
assert!(err.failing_attribution().is_none());
}
#[test]
fn failing_attribution_borrows_into_chain() {
let chain = vec![
ConfigSource::Defaults,
ConfigSource::Env("BORROWED_".to_owned()),
];
let err = ShikumiError::Extract {
sources: chain,
error: synthetic_error_with_metadata_name("`BORROWED_` environment variable(s)"),
};
let ShikumiError::Extract {
sources: ref recorded,
..
} = err
else {
unreachable!();
};
let recorded_ptr = recorded.as_ptr();
let attr = err.failing_attribution().expect("attribution");
let attr_ptr = std::ptr::from_ref::<ConfigSource>(attr.source);
unsafe {
assert_eq!(attr_ptr, recorded_ptr.add(1));
}
}
#[test]
fn failing_source_agrees_with_failing_attribution_source() {
let (_dir, err) = extract_error_with_file_path_failure();
let attr = err.failing_attribution().expect("attribution");
let legacy = err.failing_source().expect("legacy attribution");
assert!(std::ptr::eq(attr.source, legacy));
}
#[test]
fn failing_attribution_rule_resolution_order_prefers_file_by_source_over_name() {
let path_a = PathBuf::from("/a/app.yaml");
let path_b = PathBuf::from("/b/app.nix");
let chain = vec![
ConfigSource::File(path_a.clone()),
ConfigSource::File(path_b.clone()),
];
let mut e = figment::Error::from("synth".to_owned());
let mut md = figment::Metadata::named("nix: /b/app.nix");
md.source = Some(figment::Source::File(path_a.clone()));
e.metadata = Some(md);
let err = ShikumiError::Extract {
sources: chain,
error: Box::new(e),
};
let attr = err.failing_attribution().expect("attribution");
assert_eq!(attr.rule, AttributionRule::FileBySource);
assert_eq!(attr.source.as_path(), Some(path_a.as_path()));
}
#[test]
fn attribution_rule_is_copy_and_hashable() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(AttributionRule::FileBySource);
set.insert(AttributionRule::FileByMetadataName);
set.insert(AttributionRule::EnvByPrefix);
set.insert(AttributionRule::EnvByUniqueness);
set.insert(AttributionRule::DefaultsByCodeUniqueness);
assert_eq!(set.len(), AttributionRule::ALL.len());
let r = AttributionRule::FileBySource;
let r2 = r;
let r3 = r;
assert_eq!(r, r2);
assert_eq!(r2, r3);
}
#[test]
fn attribution_rule_confidence_exact_for_equality_rules() {
for rule in [
AttributionRule::FileBySource,
AttributionRule::FileByMetadataName,
AttributionRule::EnvByPrefix,
] {
assert_eq!(rule.confidence(), AttributionConfidence::Exact);
assert!(rule.is_exact());
assert!(!rule.is_fallback());
}
}
#[test]
fn attribution_rule_confidence_fallback_for_uniqueness_rules() {
for rule in [
AttributionRule::EnvByUniqueness,
AttributionRule::DefaultsByCodeUniqueness,
] {
assert_eq!(rule.confidence(), AttributionConfidence::Fallback);
assert!(rule.is_fallback());
assert!(!rule.is_exact());
}
}
#[test]
fn attribution_rule_confidence_partitions_every_variant() {
for rule in AttributionRule::ALL.iter().copied() {
assert_ne!(
rule.is_exact(),
rule.is_fallback(),
"rule {rule:?} must be exactly one of exact / fallback"
);
}
}
#[test]
fn attribution_confidence_is_copy_and_hashable() {
use std::collections::HashSet;
let mut set = HashSet::new();
for c in AttributionConfidence::ALL.iter().copied() {
set.insert(c);
}
set.insert(AttributionConfidence::Exact); assert_eq!(set.len(), AttributionConfidence::ALL.len());
let c = AttributionConfidence::Exact;
let c2 = c;
let c3 = c;
assert_eq!(c, c2);
assert_eq!(c2, c3);
}
#[test]
fn failing_source_attribution_confidence_mirrors_rule_confidence() {
for rule in AttributionRule::ALL.iter().copied() {
let src = ConfigSource::Defaults;
let attr = FailingSourceAttribution::new(&src, rule);
assert_eq!(attr.confidence(), rule.confidence());
}
}
#[test]
fn failing_attribution_confidence_exact_for_yaml_extract() {
let (_dir, err) = extract_error_with_file_path_failure();
let attr = err.failing_attribution().expect("attribution");
assert_eq!(attr.confidence(), AttributionConfidence::Exact);
assert!(attr.confidence() == attr.rule.confidence());
}
#[test]
fn attribution_confidence_all_has_no_duplicates() {
use std::collections::HashSet;
let set: HashSet<AttributionConfidence> =
AttributionConfidence::ALL.iter().copied().collect();
assert_eq!(
set.len(),
AttributionConfidence::ALL.len(),
"AttributionConfidence::ALL must contain no duplicates; got: {:?}",
AttributionConfidence::ALL,
);
}
#[test]
fn attribution_confidence_all_covers_every_rule_confidence() {
use std::collections::HashSet;
let declared: HashSet<AttributionConfidence> =
AttributionConfidence::ALL.iter().copied().collect();
let observed: HashSet<AttributionConfidence> = AttributionRule::ALL
.iter()
.copied()
.map(AttributionRule::confidence)
.collect();
assert!(
observed.is_subset(&declared),
"AttributionRule::confidence image must lie in AttributionConfidence::ALL; \
observed: {observed:?}, declared: {declared:?}",
);
}
#[test]
fn attribution_confidence_all_equals_rule_confidence_image() {
use std::collections::HashSet;
let declared: HashSet<AttributionConfidence> =
AttributionConfidence::ALL.iter().copied().collect();
let observed: HashSet<AttributionConfidence> = AttributionRule::ALL
.iter()
.copied()
.map(AttributionRule::confidence)
.collect();
assert_eq!(
observed, declared,
"AttributionRule::confidence image must equal AttributionConfidence::ALL",
);
}
#[test]
fn attribution_confidence_all_cardinality_matches_partition() {
use std::collections::HashSet;
let cells: HashSet<AttributionConfidence> = AttributionRule::ALL
.iter()
.copied()
.map(AttributionRule::confidence)
.collect();
assert_eq!(
AttributionConfidence::ALL.len(),
cells.len(),
"AttributionConfidence::ALL cardinality must match partition cell count",
);
}
#[test]
fn attribution_confidence_all_declaration_order_is_exact_then_fallback() {
assert_eq!(
AttributionConfidence::ALL,
&[
AttributionConfidence::Exact,
AttributionConfidence::Fallback,
],
);
}
#[test]
fn attribution_confidence_all_partition_is_exact_xor_fallback() {
for confidence in AttributionConfidence::ALL.iter().copied() {
let witnessing_rule = AttributionRule::ALL
.iter()
.copied()
.find(|rule| rule.confidence() == confidence)
.expect("every confidence cell must be witnessed by some rule");
match confidence {
AttributionConfidence::Exact => {
assert!(witnessing_rule.is_exact());
assert!(!witnessing_rule.is_fallback());
}
AttributionConfidence::Fallback => {
assert!(witnessing_rule.is_fallback());
assert!(!witnessing_rule.is_exact());
}
}
}
}
#[test]
fn attribution_confidence_as_str_yields_canonical_lowercase_names() {
assert_eq!(AttributionConfidence::Exact.as_str(), "exact");
assert_eq!(AttributionConfidence::Fallback.as_str(), "fallback");
}
#[test]
fn attribution_confidence_from_canonical_str_round_trips_through_trait() {
use crate::ClosedAxisLabel;
for c in AttributionConfidence::ALL.iter().copied() {
assert_eq!(
<AttributionConfidence as ClosedAxisLabel>::from_canonical_str(c.as_str()),
Some(c),
"trait from_canonical_str must round-trip for {c:?}",
);
}
assert_eq!(
<AttributionConfidence as ClosedAxisLabel>::from_canonical_str("Exact"),
Some(AttributionConfidence::Exact),
);
assert_eq!(
<AttributionConfidence as ClosedAxisLabel>::from_canonical_str("FALLBACK"),
Some(AttributionConfidence::Fallback),
);
assert_eq!(
<AttributionConfidence as ClosedAxisLabel>::from_canonical_str("exact "),
None,
);
assert_eq!(
<AttributionConfidence as ClosedAxisLabel>::from_canonical_str("fall"),
None,
);
}
#[test]
fn attribution_axis_as_str_yields_canonical_kebab_case_names() {
assert_eq!(AttributionAxis::MetadataSource.as_str(), "metadata-source");
assert_eq!(AttributionAxis::MetadataName.as_str(), "metadata-name");
}
#[test]
fn attribution_axis_from_canonical_str_round_trips_through_trait() {
use crate::ClosedAxisLabel;
for axis in AttributionAxis::ALL.iter().copied() {
assert_eq!(
<AttributionAxis as ClosedAxisLabel>::from_canonical_str(axis.as_str()),
Some(axis),
"trait from_canonical_str must round-trip for {axis:?}",
);
}
assert_eq!(
<AttributionAxis as ClosedAxisLabel>::from_canonical_str("Metadata-Source"),
Some(AttributionAxis::MetadataSource),
);
assert_eq!(
<AttributionAxis as ClosedAxisLabel>::from_canonical_str("METADATA-NAME"),
Some(AttributionAxis::MetadataName),
);
assert_eq!(
<AttributionAxis as ClosedAxisLabel>::from_canonical_str("metadata-source "),
None,
);
assert_eq!(
<AttributionAxis as ClosedAxisLabel>::from_canonical_str("source"),
None,
);
assert_eq!(
<AttributionAxis as ClosedAxisLabel>::from_canonical_str("metadata_source"),
None,
);
}
#[test]
fn shikumi_error_kind_as_str_yields_canonical_lowercase_names() {
assert_eq!(ShikumiErrorKind::NotFound.as_str(), "not-found");
assert_eq!(ShikumiErrorKind::Parse.as_str(), "parse");
assert_eq!(ShikumiErrorKind::Watch.as_str(), "watch");
assert_eq!(ShikumiErrorKind::Io.as_str(), "io");
assert_eq!(ShikumiErrorKind::Figment.as_str(), "figment");
assert_eq!(ShikumiErrorKind::Extract.as_str(), "extract");
}
#[test]
fn shikumi_error_kind_from_canonical_str_round_trips_through_trait() {
use crate::ClosedAxisLabel;
for kind in ShikumiErrorKind::ALL.iter().copied() {
assert_eq!(
<ShikumiErrorKind as ClosedAxisLabel>::from_canonical_str(kind.as_str()),
Some(kind),
"trait from_canonical_str must round-trip for {kind:?}",
);
}
assert_eq!(
<ShikumiErrorKind as ClosedAxisLabel>::from_canonical_str("Not-Found"),
Some(ShikumiErrorKind::NotFound),
);
assert_eq!(
<ShikumiErrorKind as ClosedAxisLabel>::from_canonical_str("PARSE"),
Some(ShikumiErrorKind::Parse),
);
assert_eq!(
<ShikumiErrorKind as ClosedAxisLabel>::from_canonical_str("IO"),
Some(ShikumiErrorKind::Io),
);
assert_eq!(
<ShikumiErrorKind as ClosedAxisLabel>::from_canonical_str("notfound"),
None,
);
assert_eq!(
<ShikumiErrorKind as ClosedAxisLabel>::from_canonical_str("parse "),
None,
);
assert_eq!(
<ShikumiErrorKind as ClosedAxisLabel>::from_canonical_str("err"),
None,
);
}
#[test]
fn field_path_localization_as_str_yields_canonical_lowercase_names() {
assert_eq!(FieldPathLocalization::Localized.as_str(), "localized");
assert_eq!(
FieldPathLocalization::FigmentUnlocalized.as_str(),
"figment-unlocalized",
);
assert_eq!(
FieldPathLocalization::NotApplicable.as_str(),
"not-applicable",
);
}
#[test]
fn field_path_localization_from_canonical_str_round_trips_through_trait() {
use crate::ClosedAxisLabel;
for loc in FieldPathLocalization::ALL.iter().copied() {
assert_eq!(
<FieldPathLocalization as ClosedAxisLabel>::from_canonical_str(loc.as_str()),
Some(loc),
"trait from_canonical_str must round-trip for {loc:?}",
);
}
assert_eq!(
<FieldPathLocalization as ClosedAxisLabel>::from_canonical_str("Localized"),
Some(FieldPathLocalization::Localized),
);
assert_eq!(
<FieldPathLocalization as ClosedAxisLabel>::from_canonical_str("FIGMENT-UNLOCALIZED"),
Some(FieldPathLocalization::FigmentUnlocalized),
);
assert_eq!(
<FieldPathLocalization as ClosedAxisLabel>::from_canonical_str("Not-Applicable"),
Some(FieldPathLocalization::NotApplicable),
);
assert_eq!(
<FieldPathLocalization as ClosedAxisLabel>::from_canonical_str("figmentunlocalized"),
None,
);
assert_eq!(
<FieldPathLocalization as ClosedAxisLabel>::from_canonical_str("localized "),
None,
);
assert_eq!(
<FieldPathLocalization as ClosedAxisLabel>::from_canonical_str("unlocalized"),
None,
);
}
#[test]
fn attribution_rule_as_str_yields_canonical_lowercase_names() {
assert_eq!(AttributionRule::FileBySource.as_str(), "file-by-source");
assert_eq!(
AttributionRule::FileByMetadataName.as_str(),
"file-by-metadata-name",
);
assert_eq!(AttributionRule::EnvByPrefix.as_str(), "env-by-prefix");
assert_eq!(
AttributionRule::EnvByUniqueness.as_str(),
"env-by-uniqueness",
);
assert_eq!(
AttributionRule::DefaultsByCodeUniqueness.as_str(),
"defaults-by-code-uniqueness",
);
}
#[test]
fn attribution_rule_from_canonical_str_round_trips_through_trait() {
use crate::ClosedAxisLabel;
for rule in AttributionRule::ALL.iter().copied() {
assert_eq!(
<AttributionRule as ClosedAxisLabel>::from_canonical_str(rule.as_str()),
Some(rule),
"trait from_canonical_str must round-trip for {rule:?}",
);
}
assert_eq!(
<AttributionRule as ClosedAxisLabel>::from_canonical_str("File-By-Source"),
Some(AttributionRule::FileBySource),
);
assert_eq!(
<AttributionRule as ClosedAxisLabel>::from_canonical_str("ENV-BY-PREFIX"),
Some(AttributionRule::EnvByPrefix),
);
assert_eq!(
<AttributionRule as ClosedAxisLabel>::from_canonical_str("Defaults-By-Code-Uniqueness"),
Some(AttributionRule::DefaultsByCodeUniqueness),
);
assert_eq!(
<AttributionRule as ClosedAxisLabel>::from_canonical_str("filebysource"),
None,
);
assert_eq!(
<AttributionRule as ClosedAxisLabel>::from_canonical_str("file-by-source "),
None,
);
assert_eq!(
<AttributionRule as ClosedAxisLabel>::from_canonical_str("file-by"),
None,
);
}
#[test]
fn failing_source_attribution_confidence_image_lies_in_all() {
use std::collections::HashSet;
let src = ConfigSource::Defaults;
let observed: HashSet<AttributionConfidence> = AttributionRule::ALL
.iter()
.copied()
.map(|rule| FailingSourceAttribution::new(&src, rule).confidence())
.collect();
let declared: HashSet<AttributionConfidence> =
AttributionConfidence::ALL.iter().copied().collect();
assert!(
observed.is_subset(&declared),
"every confidence surfaced by FailingSourceAttribution::confidence must lie in \
AttributionConfidence::ALL; observed: {observed:?}, declared: {declared:?}",
);
}
#[test]
fn attribution_rule_layer_kind_file_for_file_axis_rules() {
for rule in [
AttributionRule::FileBySource,
AttributionRule::FileByMetadataName,
] {
assert_eq!(rule.layer_kind(), ConfigSourceKind::File);
}
}
#[test]
fn attribution_rule_layer_kind_env_for_env_axis_rules() {
for rule in [
AttributionRule::EnvByPrefix,
AttributionRule::EnvByUniqueness,
] {
assert_eq!(rule.layer_kind(), ConfigSourceKind::Env);
}
}
#[test]
fn attribution_rule_layer_kind_defaults_for_defaults_axis_rule() {
assert_eq!(
AttributionRule::DefaultsByCodeUniqueness.layer_kind(),
ConfigSourceKind::Defaults,
);
}
#[test]
fn attribution_rule_layer_kind_partitions_every_variant() {
let cases = [
(AttributionRule::FileBySource, ConfigSourceKind::File),
(AttributionRule::FileByMetadataName, ConfigSourceKind::File),
(AttributionRule::EnvByPrefix, ConfigSourceKind::Env),
(AttributionRule::EnvByUniqueness, ConfigSourceKind::Env),
(
AttributionRule::DefaultsByCodeUniqueness,
ConfigSourceKind::Defaults,
),
];
for (rule, expected) in cases {
assert_eq!(rule.layer_kind(), expected, "rule {rule:?}");
}
}
#[test]
fn attribution_rule_layer_kind_orthogonal_to_confidence() {
use std::collections::HashSet;
let mut pairs: HashSet<(ConfigSourceKind, AttributionConfidence)> = HashSet::new();
for rule in AttributionRule::ALL.iter().copied() {
pairs.insert((rule.layer_kind(), rule.confidence()));
}
assert!(
pairs.len() >= 3,
"kind × confidence must span ≥3 cells; got: {pairs:?}"
);
}
#[test]
fn failing_source_attribution_layer_kind_mirrors_rule_layer_kind() {
for rule in AttributionRule::ALL.iter().copied() {
let src = ConfigSource::Defaults;
let attr = FailingSourceAttribution::new(&src, rule);
assert_eq!(attr.layer_kind(), rule.layer_kind());
}
}
#[test]
fn attribution_rule_layer_kind_agrees_with_source_kind() {
use crate::provider::ProviderChain;
use serde::Serialize;
#[derive(serde::Deserialize, Debug)]
struct Cfg {
#[allow(dead_code)]
count: u32,
}
#[derive(Serialize)]
struct Bad {
count: String,
}
let dir = tempfile::TempDir::new().unwrap();
let file = dir.path().join("kind_invariant.yaml");
std::fs::write(&file, "count: not_a_number\n").unwrap();
let err_file = ProviderChain::new()
.with_file(&file)
.extract::<Cfg>()
.unwrap_err();
let attr_file = err_file.failing_attribution().expect("file attribution");
assert_eq!(attr_file.layer_kind(), attr_file.source.kind());
assert_eq!(attr_file.layer_kind(), ConfigSourceKind::File);
let chain_env = vec![
ConfigSource::Defaults,
ConfigSource::Env("KIND_INV_".to_owned()),
];
let err_env = ShikumiError::Extract {
sources: chain_env,
error: synthetic_error_with_metadata_name("`KIND_INV_` environment variable(s)"),
};
let attr_env = err_env.failing_attribution().expect("env attribution");
assert_eq!(attr_env.layer_kind(), attr_env.source.kind());
assert_eq!(attr_env.layer_kind(), ConfigSourceKind::Env);
let chain_unique_env = vec![
ConfigSource::Defaults,
ConfigSource::Env("ONLY_".to_owned()),
ConfigSource::File(PathBuf::from("/etc/app.yaml")),
];
let err_unique = ShikumiError::Extract {
sources: chain_unique_env,
error: synthetic_error_with_metadata_name("`UNRELATED_` environment variable(s)"),
};
let attr_unique = err_unique
.failing_attribution()
.expect("env-uniqueness attribution");
assert_eq!(attr_unique.layer_kind(), attr_unique.source.kind());
assert_eq!(attr_unique.layer_kind(), ConfigSourceKind::Env);
let path_meta = PathBuf::from("/etc/app/app.nix");
let chain_meta = vec![ConfigSource::File(path_meta.clone())];
let err_meta = ShikumiError::Extract {
sources: chain_meta,
error: synthetic_error_with_metadata_name("nix: /etc/app/app.nix"),
};
let attr_meta = err_meta
.failing_attribution()
.expect("file-by-name attribution");
assert_eq!(attr_meta.layer_kind(), attr_meta.source.kind());
assert_eq!(attr_meta.layer_kind(), ConfigSourceKind::File);
let err_defaults = ProviderChain::new()
.with_defaults(&Bad {
count: "not_a_number".into(),
})
.extract::<Cfg>()
.unwrap_err();
let attr_defaults = err_defaults
.failing_attribution()
.expect("defaults attribution");
assert_eq!(attr_defaults.layer_kind(), attr_defaults.source.kind());
assert_eq!(attr_defaults.layer_kind(), ConfigSourceKind::Defaults);
}
#[test]
fn failing_attribution_confidence_fallback_for_unmatched_env_prefix() {
let chain = vec![
ConfigSource::Defaults,
ConfigSource::Env("ONLY_".to_owned()),
ConfigSource::File(PathBuf::from("/etc/app.yaml")),
];
let err = ShikumiError::Extract {
sources: chain,
error: synthetic_error_with_metadata_name("`UNRELATED_` environment variable(s)"),
};
let attr = err.failing_attribution().expect("attribution");
assert_eq!(attr.rule, AttributionRule::EnvByUniqueness);
assert_eq!(attr.confidence(), AttributionConfidence::Fallback);
}
fn one_per_kind() -> Vec<(ShikumiErrorKind, ShikumiError)> {
vec![
(
ShikumiErrorKind::NotFound,
ShikumiError::NotFound {
tried: vec![PathBuf::from("/a")],
},
),
(
ShikumiErrorKind::Parse,
ShikumiError::Parse("bad".to_owned()),
),
(
ShikumiErrorKind::Watch,
ShikumiError::from(notify::Error::generic("test")),
),
(
ShikumiErrorKind::Io,
ShikumiError::from(std::io::Error::new(std::io::ErrorKind::NotFound, "x")),
),
(
ShikumiErrorKind::Figment,
ShikumiError::Figment(fake_figment_error()),
),
(
ShikumiErrorKind::Extract,
ShikumiError::Extract {
sources: vec![ConfigSource::Defaults],
error: fake_figment_error(),
},
),
]
}
#[test]
fn kind_classifies_every_variant() {
for (expected, err) in one_per_kind() {
assert_eq!(
err.kind(),
expected,
"kind() must classify {err:?} as {expected:?}"
);
}
}
#[test]
fn kind_partitions_every_variant() {
for (expected, err) in one_per_kind() {
let matches: Vec<_> = ShikumiErrorKind::ALL
.iter()
.filter(|k| err.kind() == **k)
.collect();
assert_eq!(
matches.len(),
1,
"{err:?} must match exactly one kind (got {matches:?}, expected {expected:?})"
);
}
}
#[test]
fn kind_agrees_with_is_not_found_pointwise() {
for (_, err) in one_per_kind() {
assert_eq!(
err.is_not_found(),
err.kind() == ShikumiErrorKind::NotFound,
"is_not_found must agree with kind() for {err:?}"
);
}
}
#[test]
fn kind_agrees_with_is_parse_pointwise() {
for (_, err) in one_per_kind() {
assert_eq!(
err.is_parse(),
err.kind() == ShikumiErrorKind::Parse,
"is_parse must agree with kind() for {err:?}"
);
}
}
#[test]
fn shikumi_error_kind_is_copy_and_hashable() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(ShikumiErrorKind::NotFound);
set.insert(ShikumiErrorKind::Parse);
set.insert(ShikumiErrorKind::Watch);
set.insert(ShikumiErrorKind::Io);
set.insert(ShikumiErrorKind::Figment);
set.insert(ShikumiErrorKind::Extract);
set.insert(ShikumiErrorKind::NotFound); assert_eq!(set.len(), 6, "every kind must hash distinctly");
let k = ShikumiErrorKind::Extract;
let k2 = k;
let k3 = k;
assert_eq!(k, k2);
assert_eq!(k2, k3);
}
#[test]
fn shikumi_error_kind_all_has_no_duplicates() {
use std::collections::HashSet;
let unique: HashSet<ShikumiErrorKind> = ShikumiErrorKind::ALL.iter().copied().collect();
assert_eq!(
unique.len(),
ShikumiErrorKind::ALL.len(),
"ShikumiErrorKind::ALL must contain no duplicates",
);
}
#[test]
fn shikumi_error_kind_all_covers_every_constructed_variant() {
use std::collections::HashSet;
let produced: HashSet<ShikumiErrorKind> =
one_per_kind().into_iter().map(|(k, _)| k).collect();
let listed: HashSet<ShikumiErrorKind> = ShikumiErrorKind::ALL.iter().copied().collect();
assert_eq!(
produced, listed,
"ShikumiErrorKind::ALL must equal the kind set produced by ShikumiError::kind",
);
}
#[test]
fn shikumi_error_kind_all_cardinality_matches_construction_table() {
assert_eq!(
ShikumiErrorKind::ALL.len(),
one_per_kind().len(),
"ALL.len() must equal one_per_kind().len()",
);
}
#[test]
fn shikumi_error_kind_all_iterates_in_declaration_order() {
assert_eq!(
ShikumiErrorKind::ALL,
&[
ShikumiErrorKind::NotFound,
ShikumiErrorKind::Parse,
ShikumiErrorKind::Watch,
ShikumiErrorKind::Io,
ShikumiErrorKind::Figment,
ShikumiErrorKind::Extract,
],
"ALL must list variants in declaration order",
);
}
#[test]
fn shikumi_error_kind_all_partitions_figment_bearing_axis() {
let bearing = ShikumiErrorKind::ALL
.iter()
.filter(|k| k.is_figment_bearing())
.count();
let non_bearing = ShikumiErrorKind::ALL
.iter()
.filter(|k| !k.is_figment_bearing())
.count();
assert_eq!(bearing, 2, "two ALL variants bear figment");
assert_eq!(
bearing + non_bearing,
ShikumiErrorKind::ALL.len(),
"the figment-bearing partition must cover ALL exactly once",
);
}
#[test]
fn kind_partitions_distinguish_extract_from_figment() {
let extract = ShikumiError::Extract {
sources: vec![ConfigSource::Defaults],
error: fake_figment_error(),
};
let figment = ShikumiError::Figment(fake_figment_error());
assert_eq!(extract.kind(), ShikumiErrorKind::Extract);
assert_eq!(figment.kind(), ShikumiErrorKind::Figment);
assert_ne!(extract.kind(), figment.kind());
}
#[test]
fn is_figment_bearing_true_for_extract_and_figment() {
assert!(ShikumiErrorKind::Extract.is_figment_bearing());
assert!(ShikumiErrorKind::Figment.is_figment_bearing());
}
#[test]
fn is_figment_bearing_false_for_non_figment_kinds() {
for kind in [
ShikumiErrorKind::NotFound,
ShikumiErrorKind::Parse,
ShikumiErrorKind::Watch,
ShikumiErrorKind::Io,
] {
assert!(!kind.is_figment_bearing(), "{kind:?} must not bear figment");
}
}
#[test]
fn is_figment_bearing_partitions_every_kind() {
let bearing: Vec<_> = ShikumiErrorKind::ALL
.iter()
.filter(|k| k.is_figment_bearing())
.collect();
assert_eq!(
bearing.len(),
2,
"exactly two kinds bear figment; got: {bearing:?}"
);
}
#[test]
fn is_figment_bearing_agrees_with_field_path_some_pointwise() {
for (kind, err) in one_per_kind() {
assert_eq!(
kind.is_figment_bearing(),
err.field_path().is_some(),
"is_figment_bearing must mirror field_path-some for {kind:?}"
);
}
}
#[test]
fn field_path_localization_localized_for_extract_with_typed_field() {
let err = extract_error_with_typed_field_path();
assert_eq!(
err.field_path_localization(),
FieldPathLocalization::Localized
);
}
#[test]
fn field_path_localization_unlocalized_for_extract_without_field() {
let err = ShikumiError::Extract {
sources: vec![],
error: fake_figment_error(),
};
assert_eq!(
err.field_path_localization(),
FieldPathLocalization::FigmentUnlocalized
);
}
#[test]
fn field_path_localization_unlocalized_for_figment_without_field() {
let err = ShikumiError::Figment(fake_figment_error());
assert_eq!(
err.field_path_localization(),
FieldPathLocalization::FigmentUnlocalized
);
}
#[test]
fn field_path_localization_localized_for_figment_with_field() {
let raw = figment::Error::from("typed".to_owned()).with_path("a.b");
let err = ShikumiError::Figment(Box::new(raw));
assert_eq!(
err.field_path_localization(),
FieldPathLocalization::Localized
);
}
#[test]
fn field_path_localization_not_applicable_for_non_figment_variants() {
for err in [
ShikumiError::Parse("x".to_owned()),
ShikumiError::NotFound {
tried: vec![PathBuf::from("/a")],
},
ShikumiError::from(notify::Error::generic("w")),
ShikumiError::from(std::io::Error::new(std::io::ErrorKind::NotFound, "x")),
] {
assert_eq!(
err.field_path_localization(),
FieldPathLocalization::NotApplicable,
"non-figment variant must classify as NotApplicable: {err:?}"
);
}
}
fn one_per_localization() -> Vec<(ShikumiError, FieldPathLocalization)> {
vec![
(
ShikumiError::Extract {
sources: vec![],
error: Box::new(figment::Error::from("t".to_owned()).with_path("k")),
},
FieldPathLocalization::Localized,
),
(
ShikumiError::Extract {
sources: vec![],
error: fake_figment_error(),
},
FieldPathLocalization::FigmentUnlocalized,
),
(
ShikumiError::Parse("x".to_owned()),
FieldPathLocalization::NotApplicable,
),
]
}
#[test]
fn field_path_localization_partitions_every_variant() {
for (err, expected) in one_per_localization() {
let matches: Vec<_> = FieldPathLocalization::ALL
.iter()
.filter(|loc| err.field_path_localization() == **loc)
.collect();
assert_eq!(
matches.len(),
1,
"{err:?} must classify into exactly one cell (got {matches:?}, expected {expected:?})"
);
assert_eq!(err.field_path_localization(), expected);
}
}
#[test]
fn field_path_localization_agrees_with_field_path_pointwise() {
for (_, err) in one_per_kind() {
let expected = match err.field_path() {
Some(p) if !p.is_empty() => FieldPathLocalization::Localized,
Some(_) => FieldPathLocalization::FigmentUnlocalized,
None => FieldPathLocalization::NotApplicable,
};
assert_eq!(
err.field_path_localization(),
expected,
"field_path_localization must mirror field_path() for {err:?}"
);
}
}
#[test]
fn field_path_localization_is_copy_and_hashable() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(FieldPathLocalization::Localized);
set.insert(FieldPathLocalization::FigmentUnlocalized);
set.insert(FieldPathLocalization::NotApplicable);
set.insert(FieldPathLocalization::Localized); assert_eq!(set.len(), 3, "every localization must hash distinctly");
let l = FieldPathLocalization::Localized;
let l2 = l;
let l3 = l;
assert_eq!(l, l2);
assert_eq!(l2, l3);
}
#[test]
fn field_path_localization_all_has_no_duplicates() {
use std::collections::HashSet;
let unique: HashSet<FieldPathLocalization> =
FieldPathLocalization::ALL.iter().copied().collect();
assert_eq!(
unique.len(),
FieldPathLocalization::ALL.len(),
"FieldPathLocalization::ALL must contain no duplicates",
);
}
#[test]
fn field_path_localization_all_covers_every_constructed_localization() {
use std::collections::HashSet;
let produced: HashSet<FieldPathLocalization> = one_per_localization()
.into_iter()
.map(|(_, loc)| loc)
.collect();
let listed: HashSet<FieldPathLocalization> =
FieldPathLocalization::ALL.iter().copied().collect();
assert_eq!(
produced, listed,
"FieldPathLocalization::ALL must equal the cell set produced by field_path_localization()",
);
}
#[test]
fn field_path_localization_all_cardinality_matches_canonical_table() {
assert_eq!(
FieldPathLocalization::ALL.len(),
one_per_localization().len(),
"ALL.len() must equal one_per_localization().len()",
);
}
#[test]
fn field_path_localization_all_iterates_in_declaration_order() {
assert_eq!(
FieldPathLocalization::ALL,
&[
FieldPathLocalization::Localized,
FieldPathLocalization::FigmentUnlocalized,
FieldPathLocalization::NotApplicable,
],
"ALL must list variants in declaration order",
);
}
#[test]
fn field_path_localization_all_partitions_figment_bearing_axis() {
let bearing_side: usize = FieldPathLocalization::ALL
.iter()
.filter(|loc| {
matches!(
loc,
FieldPathLocalization::Localized | FieldPathLocalization::FigmentUnlocalized,
)
})
.count();
let non_bearing_side: usize = FieldPathLocalization::ALL
.iter()
.filter(|loc| matches!(loc, FieldPathLocalization::NotApplicable))
.count();
assert_eq!(
bearing_side, 2,
"two ALL cells sit on the figment-bearing side"
);
assert_eq!(
bearing_side + non_bearing_side,
FieldPathLocalization::ALL.len(),
"the figment-bearing-side partition must cover ALL exactly once",
);
}
#[test]
fn field_path_localization_all_covers_every_kind_axis_classification() {
use std::collections::HashSet;
let listed: HashSet<FieldPathLocalization> =
FieldPathLocalization::ALL.iter().copied().collect();
for (_, err) in one_per_kind() {
let loc = err.field_path_localization();
assert!(
listed.contains(&loc),
"kind-axis sample {err:?} produced localization {loc:?} not in ALL",
);
}
}
#[test]
fn field_path_localization_localized_implies_kind_figment_bearing() {
for (_, err) in one_per_kind() {
let loc = err.field_path_localization();
let bearing = err.kind().is_figment_bearing();
match loc {
FieldPathLocalization::Localized | FieldPathLocalization::FigmentUnlocalized => {
assert!(
bearing,
"Localized/FigmentUnlocalized → kind must bear figment ({err:?})"
);
}
FieldPathLocalization::NotApplicable => {
assert!(
!bearing,
"NotApplicable → kind must not bear figment ({err:?})"
);
}
}
}
}
#[test]
fn attribution_rule_metadata_axis_metadata_source_for_source_axis_rules() {
for rule in [
AttributionRule::FileBySource,
AttributionRule::DefaultsByCodeUniqueness,
] {
assert_eq!(rule.metadata_axis(), AttributionAxis::MetadataSource);
}
}
#[test]
fn attribution_rule_metadata_axis_metadata_name_for_name_axis_rules() {
for rule in [
AttributionRule::FileByMetadataName,
AttributionRule::EnvByPrefix,
AttributionRule::EnvByUniqueness,
] {
assert_eq!(rule.metadata_axis(), AttributionAxis::MetadataName);
}
}
#[test]
fn attribution_rule_metadata_axis_partitions_every_variant() {
let cases = [
(
AttributionRule::FileBySource,
AttributionAxis::MetadataSource,
),
(
AttributionRule::FileByMetadataName,
AttributionAxis::MetadataName,
),
(AttributionRule::EnvByPrefix, AttributionAxis::MetadataName),
(
AttributionRule::EnvByUniqueness,
AttributionAxis::MetadataName,
),
(
AttributionRule::DefaultsByCodeUniqueness,
AttributionAxis::MetadataSource,
),
];
for (rule, expected) in cases {
assert_eq!(rule.metadata_axis(), expected, "rule {rule:?}");
}
}
#[test]
fn attribution_axis_is_copy_and_hashable() {
use std::collections::HashSet;
let mut set = HashSet::new();
for axis in AttributionAxis::ALL.iter().copied() {
set.insert(axis);
}
set.insert(AttributionAxis::MetadataSource); assert_eq!(
set.len(),
AttributionAxis::ALL.len(),
"every axis must hash distinctly"
);
let a = AttributionAxis::MetadataSource;
let a2 = a;
let a3 = a;
assert_eq!(a, a2);
assert_eq!(a2, a3);
}
#[test]
fn attribution_axis_all_has_no_duplicates() {
use std::collections::HashSet;
let set: HashSet<AttributionAxis> = AttributionAxis::ALL.iter().copied().collect();
assert_eq!(
set.len(),
AttributionAxis::ALL.len(),
"AttributionAxis::ALL must list every variant exactly once; got duplicates in {:?}",
AttributionAxis::ALL,
);
}
#[test]
fn attribution_axis_all_covers_every_rule_axis() {
use std::collections::HashSet;
let produced: HashSet<AttributionAxis> = AttributionRule::ALL
.iter()
.copied()
.map(AttributionRule::metadata_axis)
.collect();
let declared: HashSet<AttributionAxis> = AttributionAxis::ALL.iter().copied().collect();
assert!(
produced.is_subset(&declared),
"every axis reached by AttributionRule::metadata_axis must lie in \
AttributionAxis::ALL; produced: {produced:?}, declared: {declared:?}",
);
}
#[test]
fn attribution_axis_all_equals_rule_axis_image() {
use std::collections::HashSet;
let produced: HashSet<AttributionAxis> = AttributionRule::ALL
.iter()
.copied()
.map(AttributionRule::metadata_axis)
.collect();
let declared: HashSet<AttributionAxis> = AttributionAxis::ALL.iter().copied().collect();
assert_eq!(
produced, declared,
"AttributionAxis::ALL must equal the image of (rule → axis); \
produced: {produced:?}, declared: {declared:?}",
);
}
#[test]
fn attribution_axis_all_cardinality_matches_partition() {
use std::collections::HashSet;
let distinct: HashSet<AttributionAxis> = AttributionRule::ALL
.iter()
.copied()
.map(AttributionRule::metadata_axis)
.collect();
assert_eq!(
AttributionAxis::ALL.len(),
distinct.len(),
"AttributionAxis::ALL.len() ({}) must match the partition cardinality ({})",
AttributionAxis::ALL.len(),
distinct.len(),
);
}
#[test]
fn attribution_axis_all_lists_variants_in_declaration_order() {
assert_eq!(
AttributionAxis::ALL,
&[
AttributionAxis::MetadataSource,
AttributionAxis::MetadataName
],
"AttributionAxis::ALL must list variants in declaration order",
);
}
#[test]
fn attribution_axis_all_covers_failing_source_attribution_axes() {
use std::collections::HashSet;
let src = ConfigSource::Defaults;
let observed: HashSet<AttributionAxis> = AttributionRule::ALL
.iter()
.copied()
.map(|rule| FailingSourceAttribution::new(&src, rule).metadata_axis())
.collect();
let declared: HashSet<AttributionAxis> = AttributionAxis::ALL.iter().copied().collect();
assert!(
observed.is_subset(&declared),
"every axis surfaced by FailingSourceAttribution::metadata_axis must lie in \
AttributionAxis::ALL; observed: {observed:?}, declared: {declared:?}",
);
}
#[test]
fn attribution_rule_metadata_axis_orthogonal_to_confidence() {
use std::collections::HashSet;
let mut pairs: HashSet<(AttributionAxis, AttributionConfidence)> = HashSet::new();
for rule in AttributionRule::ALL.iter().copied() {
pairs.insert((rule.metadata_axis(), rule.confidence()));
}
assert_eq!(
pairs.len(),
4,
"axis × confidence must span all four cells; got: {pairs:?}"
);
}
#[test]
fn attribution_rule_metadata_axis_orthogonal_to_layer_kind() {
use std::collections::HashSet;
let mut pairs: HashSet<(AttributionAxis, ConfigSourceKind)> = HashSet::new();
for rule in AttributionRule::ALL.iter().copied() {
pairs.insert((rule.metadata_axis(), rule.layer_kind()));
}
assert!(
pairs.len() >= 3,
"axis × layer_kind must span ≥3 cells; got: {pairs:?}"
);
}
#[test]
fn attribution_rule_metadata_axis_three_axis_product_is_rule_identity() {
use std::collections::HashSet;
let mut triples: HashSet<(AttributionAxis, ConfigSourceKind, AttributionConfidence)> =
HashSet::new();
for rule in AttributionRule::ALL.iter().copied() {
triples.insert((rule.metadata_axis(), rule.layer_kind(), rule.confidence()));
}
assert_eq!(
triples.len(),
AttributionRule::ALL.len(),
"triple (axis × kind × confidence) must distinguish every rule; got: {triples:?}"
);
}
#[test]
fn failing_source_attribution_metadata_axis_mirrors_rule_metadata_axis() {
for rule in AttributionRule::ALL.iter().copied() {
let src = ConfigSource::Defaults;
let attr = FailingSourceAttribution::new(&src, rule);
assert_eq!(attr.metadata_axis(), rule.metadata_axis());
}
}
#[test]
fn attribution_rule_figment_source_kind_some_for_file_by_source() {
assert_eq!(
AttributionRule::FileBySource.figment_source_kind(),
Some(FigmentSourceKind::File),
);
}
#[test]
fn attribution_rule_figment_source_kind_some_for_defaults_by_code_uniqueness() {
assert_eq!(
AttributionRule::DefaultsByCodeUniqueness.figment_source_kind(),
Some(FigmentSourceKind::Code),
);
}
#[test]
fn attribution_rule_figment_source_kind_none_for_name_axis_rules() {
for rule in [
AttributionRule::FileByMetadataName,
AttributionRule::EnvByPrefix,
AttributionRule::EnvByUniqueness,
] {
assert_eq!(
rule.figment_source_kind(),
None,
"name-axis rule {rule:?} must not pin a FigmentSourceKind",
);
}
}
#[test]
fn attribution_rule_figment_source_kind_partitions_every_variant() {
let cases = [
(AttributionRule::FileBySource, Some(FigmentSourceKind::File)),
(AttributionRule::FileByMetadataName, None),
(AttributionRule::EnvByPrefix, None),
(AttributionRule::EnvByUniqueness, None),
(
AttributionRule::DefaultsByCodeUniqueness,
Some(FigmentSourceKind::Code),
),
];
for (rule, expected) in cases {
assert_eq!(rule.figment_source_kind(), expected, "rule {rule:?}");
}
}
#[test]
fn attribution_rule_figment_source_kind_some_iff_metadata_axis_source() {
for rule in AttributionRule::ALL.iter().copied() {
assert_eq!(
rule.figment_source_kind().is_some(),
rule.metadata_axis() == AttributionAxis::MetadataSource,
"rule {rule:?}: figment_source_kind.is_some() must equal \
(metadata_axis == MetadataSource)",
);
}
}
#[test]
fn attribution_rule_figment_source_kind_image_is_file_and_code_only() {
use std::collections::HashSet;
let observed: HashSet<FigmentSourceKind> = AttributionRule::ALL
.iter()
.copied()
.filter_map(AttributionRule::figment_source_kind)
.collect();
let expected: HashSet<FigmentSourceKind> =
HashSet::from([FigmentSourceKind::File, FigmentSourceKind::Code]);
assert_eq!(
observed, expected,
"image of figment_source_kind over AttributionRule::ALL must equal \
{{File, Code}}; got: {observed:?}",
);
}
#[test]
fn attribution_rule_figment_source_kind_agrees_with_layer_kind_when_some() {
let cases = [
(
AttributionRule::FileBySource,
FigmentSourceKind::File,
ConfigSourceKind::File,
),
(
AttributionRule::DefaultsByCodeUniqueness,
FigmentSourceKind::Code,
ConfigSourceKind::Defaults,
),
];
for (rule, fk, ck) in cases {
assert_eq!(rule.figment_source_kind(), Some(fk), "rule {rule:?}");
assert_eq!(rule.layer_kind(), ck, "rule {rule:?}");
}
for rule in [
AttributionRule::FileByMetadataName,
AttributionRule::EnvByPrefix,
AttributionRule::EnvByUniqueness,
] {
assert!(
rule.figment_source_kind().is_none(),
"name-axis rule {rule:?} must not lie on the source-axis diagonal",
);
}
}
#[test]
fn attribution_rule_figment_source_kind_image_lies_in_figment_source_kind_all() {
use std::collections::HashSet;
let observed: HashSet<FigmentSourceKind> = AttributionRule::ALL
.iter()
.copied()
.filter_map(AttributionRule::figment_source_kind)
.collect();
let declared: HashSet<FigmentSourceKind> = FigmentSourceKind::ALL.iter().copied().collect();
assert!(
observed.is_subset(&declared),
"image of figment_source_kind must lie in FigmentSourceKind::ALL; \
observed: {observed:?}, declared: {declared:?}",
);
}
#[test]
fn failing_source_attribution_figment_source_kind_mirrors_rule_figment_source_kind() {
for rule in AttributionRule::ALL.iter().copied() {
let src = ConfigSource::Defaults;
let attr = FailingSourceAttribution::new(&src, rule);
assert_eq!(attr.figment_source_kind(), rule.figment_source_kind());
}
}
#[test]
fn failing_source_attribution_figment_source_kind_some_iff_metadata_axis_source() {
for rule in AttributionRule::ALL.iter().copied() {
let src = ConfigSource::Defaults;
let attr = FailingSourceAttribution::new(&src, rule);
assert_eq!(
attr.figment_source_kind().is_some(),
attr.metadata_axis() == AttributionAxis::MetadataSource,
"envelope for rule {rule:?}: figment_source_kind.is_some() must equal \
(metadata_axis == MetadataSource)",
);
}
}
#[test]
fn attribution_rule_figment_name_tag_kind_some_for_file_by_metadata_name() {
assert_eq!(
AttributionRule::FileByMetadataName.figment_name_tag_kind(),
Some(FigmentNameTagKind::Format),
);
}
#[test]
fn attribution_rule_figment_name_tag_kind_some_for_env_by_prefix() {
assert_eq!(
AttributionRule::EnvByPrefix.figment_name_tag_kind(),
Some(FigmentNameTagKind::Env),
);
}
#[test]
fn attribution_rule_figment_name_tag_kind_some_for_env_by_uniqueness() {
assert_eq!(
AttributionRule::EnvByUniqueness.figment_name_tag_kind(),
Some(FigmentNameTagKind::Env),
);
}
#[test]
fn attribution_rule_figment_name_tag_kind_none_for_source_axis_rules() {
for rule in [
AttributionRule::FileBySource,
AttributionRule::DefaultsByCodeUniqueness,
] {
assert_eq!(
rule.figment_name_tag_kind(),
None,
"source-axis rule {rule:?} must not pin a FigmentNameTagKind",
);
}
}
#[test]
fn attribution_rule_figment_name_tag_kind_partitions_every_variant() {
let cases = [
(AttributionRule::FileBySource, None),
(
AttributionRule::FileByMetadataName,
Some(FigmentNameTagKind::Format),
),
(AttributionRule::EnvByPrefix, Some(FigmentNameTagKind::Env)),
(
AttributionRule::EnvByUniqueness,
Some(FigmentNameTagKind::Env),
),
(AttributionRule::DefaultsByCodeUniqueness, None),
];
for (rule, expected) in cases {
assert_eq!(rule.figment_name_tag_kind(), expected, "rule {rule:?}");
}
}
#[test]
fn attribution_rule_figment_name_tag_kind_some_iff_metadata_axis_name() {
for rule in AttributionRule::ALL.iter().copied() {
assert_eq!(
rule.figment_name_tag_kind().is_some(),
rule.metadata_axis() == AttributionAxis::MetadataName,
"rule {rule:?}: figment_name_tag_kind.is_some() must equal \
(metadata_axis == MetadataName)",
);
}
}
#[test]
fn attribution_rule_figment_name_tag_kind_image_equals_figment_name_tag_kind_all() {
use std::collections::HashSet;
let observed: HashSet<FigmentNameTagKind> = AttributionRule::ALL
.iter()
.copied()
.filter_map(AttributionRule::figment_name_tag_kind)
.collect();
let expected: HashSet<FigmentNameTagKind> =
FigmentNameTagKind::ALL.iter().copied().collect();
assert_eq!(
observed, expected,
"image of figment_name_tag_kind over AttributionRule::ALL must equal \
FigmentNameTagKind::ALL; got: {observed:?}",
);
}
#[test]
fn attribution_rule_figment_name_tag_kind_xor_figment_source_kind() {
for rule in AttributionRule::ALL.iter().copied() {
let src_some = rule.figment_source_kind().is_some();
let name_some = rule.figment_name_tag_kind().is_some();
assert!(
src_some ^ name_some,
"rule {rule:?}: exactly one of figment_source_kind / \
figment_name_tag_kind must be Some (got src_some={src_some}, \
name_some={name_some})",
);
}
}
#[test]
fn failing_source_attribution_figment_name_tag_kind_mirrors_rule_figment_name_tag_kind() {
for rule in AttributionRule::ALL.iter().copied() {
let src = ConfigSource::Defaults;
let attr = FailingSourceAttribution::new(&src, rule);
assert_eq!(attr.figment_name_tag_kind(), rule.figment_name_tag_kind());
}
}
#[test]
fn failing_source_attribution_figment_name_tag_kind_some_iff_metadata_axis_name() {
for rule in AttributionRule::ALL.iter().copied() {
let src = ConfigSource::Defaults;
let attr = FailingSourceAttribution::new(&src, rule);
assert_eq!(
attr.figment_name_tag_kind().is_some(),
attr.metadata_axis() == AttributionAxis::MetadataName,
"envelope for rule {rule:?}: figment_name_tag_kind.is_some() must equal \
(metadata_axis == MetadataName)",
);
}
}
#[test]
fn attribution_rule_file_provenance_pins_each_file_rule() {
assert_eq!(
AttributionRule::FileBySource.file_provenance(),
Some(crate::FormatProvenance::FigmentBuiltin),
);
assert_eq!(
AttributionRule::FileByMetadataName.file_provenance(),
Some(crate::FormatProvenance::ShikumiBuilt),
);
}
#[test]
fn attribution_rule_file_provenance_none_for_non_file_rules() {
for rule in [
AttributionRule::EnvByPrefix,
AttributionRule::EnvByUniqueness,
AttributionRule::DefaultsByCodeUniqueness,
] {
assert_eq!(
rule.file_provenance(),
None,
"non-file-axis rule {rule:?} must not project to a FormatProvenance",
);
}
}
#[test]
fn attribution_rule_file_provenance_some_iff_file_layer_kind() {
for rule in AttributionRule::ALL.iter().copied() {
assert_eq!(
rule.file_provenance().is_some(),
rule.layer_kind() == ConfigSourceKind::File,
"rule {rule:?}: file_provenance.is_some() must equal \
(layer_kind == File)",
);
}
}
#[test]
fn attribution_rule_file_provenance_round_trips_through_format_provenance() {
for rule in AttributionRule::ALL.iter().copied() {
if let Some(provenance) = rule.file_provenance() {
assert_eq!(
provenance.file_attribution_rule(),
rule,
"rule {rule:?}: file_provenance → file_attribution_rule \
must round-trip to the originating rule",
);
}
}
}
#[test]
fn format_provenance_file_attribution_rule_round_trips_through_file_provenance() {
for provenance in crate::FormatProvenance::ALL.iter().copied() {
assert_eq!(
provenance.file_attribution_rule().file_provenance(),
Some(provenance),
"provenance {provenance:?}: file_attribution_rule → file_provenance \
must round-trip to the originating provenance",
);
}
}
#[test]
fn attribution_rule_file_provenance_image_equals_format_provenance_all() {
use std::collections::HashSet;
let observed: HashSet<crate::FormatProvenance> = AttributionRule::ALL
.iter()
.copied()
.filter_map(AttributionRule::file_provenance)
.collect();
let expected: HashSet<crate::FormatProvenance> =
crate::FormatProvenance::ALL.iter().copied().collect();
assert_eq!(
observed, expected,
"image of file_provenance over AttributionRule::ALL must equal \
FormatProvenance::ALL; got: {observed:?}",
);
}
#[test]
fn failing_source_attribution_file_provenance_mirrors_rule_file_provenance() {
for rule in AttributionRule::ALL.iter().copied() {
let src = ConfigSource::Defaults;
let attr = FailingSourceAttribution::new(&src, rule);
assert_eq!(attr.file_provenance(), rule.file_provenance());
}
}
#[test]
fn failing_source_attribution_file_provenance_some_iff_file_layer_kind() {
for rule in AttributionRule::ALL.iter().copied() {
let src = ConfigSource::Defaults;
let attr = FailingSourceAttribution::new(&src, rule);
assert_eq!(
attr.file_provenance().is_some(),
attr.layer_kind() == ConfigSourceKind::File,
"envelope for rule {rule:?}: file_provenance.is_some() must equal \
(layer_kind == File)",
);
}
}
#[test]
fn failing_attribution_metadata_axis_metadata_source_for_yaml_extract() {
let (_dir, err) = extract_error_with_file_path_failure();
let attr = err.failing_attribution().expect("attribution");
assert_eq!(attr.metadata_axis(), AttributionAxis::MetadataSource);
assert_eq!(attr.metadata_axis(), attr.rule.metadata_axis());
}
#[test]
fn failing_attribution_metadata_axis_metadata_name_for_synthetic_env_prefix() {
let chain = vec![
ConfigSource::Defaults,
ConfigSource::Env("MAXIS_".to_owned()),
];
let err = ShikumiError::Extract {
sources: chain,
error: synthetic_error_with_metadata_name("`MAXIS_` environment variable(s)"),
};
let attr = err.failing_attribution().expect("attribution");
assert_eq!(attr.rule, AttributionRule::EnvByPrefix);
assert_eq!(attr.metadata_axis(), AttributionAxis::MetadataName);
}
fn rule_coordinate_table() -> [(AttributionRule, AttributionCoordinates); 5] {
[
(
AttributionRule::FileBySource,
AttributionCoordinates {
axis: AttributionAxis::MetadataSource,
layer_kind: ConfigSourceKind::File,
confidence: AttributionConfidence::Exact,
},
),
(
AttributionRule::FileByMetadataName,
AttributionCoordinates {
axis: AttributionAxis::MetadataName,
layer_kind: ConfigSourceKind::File,
confidence: AttributionConfidence::Exact,
},
),
(
AttributionRule::EnvByPrefix,
AttributionCoordinates {
axis: AttributionAxis::MetadataName,
layer_kind: ConfigSourceKind::Env,
confidence: AttributionConfidence::Exact,
},
),
(
AttributionRule::EnvByUniqueness,
AttributionCoordinates {
axis: AttributionAxis::MetadataName,
layer_kind: ConfigSourceKind::Env,
confidence: AttributionConfidence::Fallback,
},
),
(
AttributionRule::DefaultsByCodeUniqueness,
AttributionCoordinates {
axis: AttributionAxis::MetadataSource,
layer_kind: ConfigSourceKind::Defaults,
confidence: AttributionConfidence::Fallback,
},
),
]
}
#[test]
fn attribution_rule_coordinates_returns_expected_triple_per_rule() {
for (rule, expected) in rule_coordinate_table() {
assert_eq!(rule.coordinates(), expected, "rule {rule:?}");
}
}
#[test]
fn attribution_rule_coordinates_agrees_with_three_projection_accessors() {
for (rule, _) in rule_coordinate_table() {
let c = rule.coordinates();
assert_eq!(c.axis, rule.metadata_axis());
assert_eq!(c.layer_kind, rule.layer_kind());
assert_eq!(c.confidence, rule.confidence());
}
}
#[test]
fn attribution_rule_coordinates_distinguishes_every_rule() {
use std::collections::HashSet;
let coords: HashSet<AttributionCoordinates> = rule_coordinate_table()
.iter()
.map(|(rule, _)| rule.coordinates())
.collect();
assert_eq!(
coords.len(),
AttributionRule::ALL.len(),
"every rule must occupy a distinct coordinate cell; got: {coords:?}"
);
}
#[test]
fn attribution_rule_from_coordinates_recognizes_each_rule() {
for (expected_rule, coords) in rule_coordinate_table() {
assert_eq!(
AttributionRule::from_coordinates(coords),
Some(expected_rule),
"from_coordinates must recognize {coords:?} as {expected_rule:?}",
);
}
}
#[test]
fn attribution_rule_from_coordinates_round_trips_with_coordinates() {
for (rule, _) in rule_coordinate_table() {
assert_eq!(
AttributionRule::from_coordinates(rule.coordinates()),
Some(rule),
"round-trip must recover rule {rule:?}",
);
}
}
#[test]
fn attribution_rule_from_coordinates_returns_none_for_unrecognized_cells() {
let recognized: std::collections::HashSet<AttributionCoordinates> =
rule_coordinate_table().iter().map(|(_, c)| *c).collect();
let mut unrecognized_count = 0usize;
for coords in AttributionCoordinates::ALL.iter().copied() {
if recognized.contains(&coords) {
continue;
}
unrecognized_count += 1;
assert_eq!(
AttributionRule::from_coordinates(coords),
None,
"unrecognized cell {coords:?} must not resolve to a rule",
);
}
assert_eq!(
unrecognized_count, 7,
"the 12-cell cube must contain exactly 7 unrecognized cells; got: {unrecognized_count}",
);
}
#[test]
fn attribution_rule_from_coordinates_rejects_specific_unrecognized_cells() {
let unrecognized = [
AttributionCoordinates {
axis: AttributionAxis::MetadataSource,
layer_kind: ConfigSourceKind::Env,
confidence: AttributionConfidence::Exact,
},
AttributionCoordinates {
axis: AttributionAxis::MetadataSource,
layer_kind: ConfigSourceKind::Env,
confidence: AttributionConfidence::Fallback,
},
AttributionCoordinates {
axis: AttributionAxis::MetadataName,
layer_kind: ConfigSourceKind::Defaults,
confidence: AttributionConfidence::Exact,
},
AttributionCoordinates {
axis: AttributionAxis::MetadataName,
layer_kind: ConfigSourceKind::Defaults,
confidence: AttributionConfidence::Fallback,
},
AttributionCoordinates {
axis: AttributionAxis::MetadataSource,
layer_kind: ConfigSourceKind::File,
confidence: AttributionConfidence::Fallback,
},
AttributionCoordinates {
axis: AttributionAxis::MetadataSource,
layer_kind: ConfigSourceKind::Defaults,
confidence: AttributionConfidence::Exact,
},
AttributionCoordinates {
axis: AttributionAxis::MetadataName,
layer_kind: ConfigSourceKind::File,
confidence: AttributionConfidence::Fallback,
},
];
for coords in unrecognized {
assert!(
AttributionRule::from_coordinates(coords).is_none(),
"from_coordinates must reject unrecognized cell {coords:?}",
);
}
}
#[test]
fn attribution_coordinates_is_copy_and_hashable() {
use std::collections::HashSet;
let mut set = HashSet::new();
for (_, coords) in rule_coordinate_table() {
set.insert(coords);
}
assert_eq!(set.len(), 5, "every coordinate triple must hash distinctly");
let c = AttributionCoordinates {
axis: AttributionAxis::MetadataSource,
layer_kind: ConfigSourceKind::File,
confidence: AttributionConfidence::Exact,
};
let c2 = c;
let c3 = c;
assert_eq!(c, c2);
assert_eq!(c2, c3);
}
#[test]
fn attribution_coordinates_all_has_no_duplicates() {
use std::collections::HashSet;
let set: HashSet<AttributionCoordinates> =
AttributionCoordinates::ALL.iter().copied().collect();
assert_eq!(
set.len(),
AttributionCoordinates::ALL.len(),
"AttributionCoordinates::ALL must contain no duplicates; got: {:?}",
AttributionCoordinates::ALL,
);
}
#[test]
fn attribution_coordinates_all_cardinality_matches_product_of_axes() {
assert_eq!(
AttributionCoordinates::ALL.len(),
AttributionAxis::ALL.len()
* ConfigSourceKind::ALL.len()
* AttributionConfidence::ALL.len(),
"AttributionCoordinates::ALL cardinality must equal \
AttributionAxis::ALL.len() * ConfigSourceKind::ALL.len() * \
AttributionConfidence::ALL.len()",
);
assert_eq!(
AttributionCoordinates::ALL.len(),
12,
"AttributionCoordinates::ALL cardinality must be 12 today; \
got: {}",
AttributionCoordinates::ALL.len(),
);
}
#[test]
fn attribution_coordinates_all_equals_axes_cartesian_product() {
use std::collections::HashSet;
let declared: HashSet<AttributionCoordinates> =
AttributionCoordinates::ALL.iter().copied().collect();
let mut product: HashSet<AttributionCoordinates> = HashSet::new();
for axis in AttributionAxis::ALL.iter().copied() {
for layer_kind in ConfigSourceKind::ALL.iter().copied() {
for confidence in AttributionConfidence::ALL.iter().copied() {
product.insert(AttributionCoordinates {
axis,
layer_kind,
confidence,
});
}
}
}
assert_eq!(
declared, product,
"AttributionCoordinates::ALL must equal the cartesian product \
of AttributionAxis::ALL × ConfigSourceKind::ALL × \
AttributionConfidence::ALL; declared: {declared:?}, \
product: {product:?}",
);
}
#[test]
fn attribution_coordinates_all_iterates_in_lexicographic_order() {
let mut expected: Vec<AttributionCoordinates> = Vec::new();
for axis in AttributionAxis::ALL.iter().copied() {
for layer_kind in ConfigSourceKind::ALL.iter().copied() {
for confidence in AttributionConfidence::ALL.iter().copied() {
expected.push(AttributionCoordinates {
axis,
layer_kind,
confidence,
});
}
}
}
assert_eq!(
AttributionCoordinates::ALL.to_vec(),
expected,
"AttributionCoordinates::ALL must list cells in lexicographic \
order over (AttributionAxis::ALL, ConfigSourceKind::ALL, \
AttributionConfidence::ALL)",
);
}
#[test]
fn attribution_coordinates_all_partitions_into_recognized_and_unrecognized() {
let recognized = AttributionCoordinates::ALL
.iter()
.copied()
.filter(|c| AttributionRule::from_coordinates(*c).is_some())
.count();
let unrecognized = AttributionCoordinates::ALL
.iter()
.copied()
.filter(|c| AttributionRule::from_coordinates(*c).is_none())
.count();
assert_eq!(
recognized,
AttributionRule::ALL.len(),
"recognized-cell count must equal AttributionRule::ALL cardinality",
);
assert_eq!(
unrecognized,
AttributionCoordinates::ALL.len() - AttributionRule::ALL.len(),
"unrecognized-cell count must equal cube cardinality minus \
AttributionRule::ALL cardinality",
);
assert_eq!(
recognized + unrecognized,
AttributionCoordinates::ALL.len(),
"the recognized + unrecognized partition must cover \
AttributionCoordinates::ALL exactly",
);
}
#[test]
fn attribution_coordinates_all_recognized_image_equals_rule_coordinates() {
use std::collections::HashSet;
let recognized_in_cube: HashSet<AttributionCoordinates> = AttributionCoordinates::ALL
.iter()
.copied()
.filter(|c| AttributionRule::from_coordinates(*c).is_some())
.collect();
let rule_image: HashSet<AttributionCoordinates> = AttributionRule::ALL
.iter()
.copied()
.map(AttributionRule::coordinates)
.collect();
assert_eq!(
recognized_in_cube, rule_image,
"recognized cells of AttributionCoordinates::ALL must equal \
the image of AttributionRule::coordinates over AttributionRule::ALL",
);
}
#[test]
fn attribution_coordinates_all_round_trips_through_from_coordinates_on_recognized_cells() {
let mut round_tripped = 0usize;
for coords in AttributionCoordinates::ALL.iter().copied() {
if let Some(rule) = AttributionRule::from_coordinates(coords) {
assert_eq!(
rule.coordinates(),
coords,
"recognized cell {coords:?} must round-trip via \
from_coordinates -> coordinates",
);
round_tripped += 1;
}
}
assert_eq!(
round_tripped,
AttributionRule::ALL.len(),
"exactly AttributionRule::ALL.len() cells must round-trip; \
got: {round_tripped}",
);
}
#[test]
fn attribution_coordinates_is_realizable_agrees_with_from_coordinates_some() {
for cell in AttributionCoordinates::ALL.iter().copied() {
let expected = AttributionRule::from_coordinates(cell).is_some();
assert_eq!(
cell.is_realizable(),
expected,
"cell {cell:?}: is_realizable must equal from_coordinates(_).is_some()",
);
}
}
#[test]
fn attribution_coordinates_realizable_partitions_into_5_realizable_and_7_unrealizable() {
let realizable = AttributionCoordinates::ALL
.iter()
.filter(|c| c.is_realizable())
.count();
let unrealizable = AttributionCoordinates::ALL
.iter()
.filter(|c| !c.is_realizable())
.count();
assert_eq!(
realizable,
AttributionRule::ALL.len(),
"realizable cells must equal AttributionRule::ALL cardinality",
);
assert_eq!(
unrealizable,
AttributionCoordinates::ALL.len() - AttributionRule::ALL.len(),
"unrealizable cells must equal cube cardinality minus rule cardinality",
);
assert_eq!(
realizable + unrealizable,
AttributionCoordinates::ALL.len(),
"realizable + unrealizable must cover ALL exactly once",
);
assert_eq!(realizable, 5);
assert_eq!(unrealizable, 7);
}
#[test]
fn attribution_coordinates_is_realizable_image_equals_rule_image() {
use std::collections::HashSet;
let observed: HashSet<AttributionCoordinates> = AttributionRule::ALL
.iter()
.copied()
.map(AttributionRule::coordinates)
.collect();
let realizable: HashSet<AttributionCoordinates> = AttributionCoordinates::ALL
.iter()
.copied()
.filter(|c| c.is_realizable())
.collect();
assert_eq!(
observed, realizable,
"observed image over AttributionRule::ALL must equal the realizable cells",
);
}
#[test]
fn attribution_rule_coordinates_always_lies_on_realizable_cell() {
for rule in AttributionRule::ALL.iter().copied() {
assert!(
rule.coordinates().is_realizable(),
"rule {rule:?}: coordinates() must produce a realizable cell",
);
}
}
#[test]
fn attribution_coordinates_unrealizable_cells_have_no_inverse() {
for cell in AttributionCoordinates::ALL.iter().copied() {
if !cell.is_realizable() {
assert!(
AttributionRule::from_coordinates(cell).is_none(),
"unrealizable cell {cell:?}: from_coordinates must be None",
);
}
}
}
#[test]
fn failing_source_attribution_coordinates_mirrors_rule_coordinates() {
for (rule, expected) in rule_coordinate_table() {
let src = ConfigSource::Defaults;
let attr = FailingSourceAttribution::new(&src, rule);
assert_eq!(attr.coordinates(), expected);
assert_eq!(attr.coordinates(), rule.coordinates());
}
}
#[test]
fn failing_source_attribution_coordinates_field_agreement() {
for (rule, _) in rule_coordinate_table() {
let src = ConfigSource::Defaults;
let attr = FailingSourceAttribution::new(&src, rule);
let c = attr.coordinates();
assert_eq!(c.axis, attr.metadata_axis());
assert_eq!(c.layer_kind, attr.layer_kind());
assert_eq!(c.confidence, attr.confidence());
}
}
#[test]
fn failing_attribution_coordinates_for_yaml_extract() {
let (_dir, err) = extract_error_with_file_path_failure();
let attr = err.failing_attribution().expect("attribution");
assert_eq!(
attr.coordinates(),
AttributionCoordinates {
axis: AttributionAxis::MetadataSource,
layer_kind: ConfigSourceKind::File,
confidence: AttributionConfidence::Exact,
},
);
assert_eq!(
AttributionRule::from_coordinates(attr.coordinates()),
Some(AttributionRule::FileBySource),
);
}
#[test]
fn failing_attribution_metadata_axis_metadata_source_for_defaults_only_extract() {
use crate::provider::ProviderChain;
use serde::Serialize;
#[derive(Serialize)]
struct Bad {
count: String,
}
#[derive(serde::Deserialize, Debug)]
struct Cfg {
#[allow(dead_code)]
count: u32,
}
let err = ProviderChain::new()
.with_defaults(&Bad {
count: "not_a_number".into(),
})
.extract::<Cfg>()
.unwrap_err();
let attr = err.failing_attribution().expect("attribution");
assert_eq!(attr.rule, AttributionRule::DefaultsByCodeUniqueness);
assert_eq!(attr.metadata_axis(), AttributionAxis::MetadataSource);
assert_eq!(attr.confidence(), AttributionConfidence::Fallback);
}
#[test]
fn attribution_rule_all_has_no_duplicates() {
use std::collections::HashSet;
let unique: HashSet<AttributionRule> = AttributionRule::ALL.iter().copied().collect();
assert_eq!(
unique.len(),
AttributionRule::ALL.len(),
"AttributionRule::ALL must contain no duplicates",
);
}
#[test]
fn attribution_rule_all_covers_every_recognized_variant() {
use std::collections::HashSet;
let produced: HashSet<AttributionRule> = rule_coordinate_table()
.into_iter()
.map(|(r, _)| r)
.collect();
let listed: HashSet<AttributionRule> = AttributionRule::ALL.iter().copied().collect();
assert_eq!(
produced, listed,
"AttributionRule::ALL must equal the rule set produced by rule_coordinate_table",
);
}
#[test]
fn attribution_rule_all_cardinality_matches_coordinate_table() {
assert_eq!(
AttributionRule::ALL.len(),
rule_coordinate_table().len(),
"ALL.len() must equal rule_coordinate_table().len()",
);
}
#[test]
fn attribution_rule_all_iterates_in_declaration_order() {
assert_eq!(
AttributionRule::ALL,
&[
AttributionRule::FileBySource,
AttributionRule::FileByMetadataName,
AttributionRule::EnvByPrefix,
AttributionRule::EnvByUniqueness,
AttributionRule::DefaultsByCodeUniqueness,
],
"ALL must list variants in declaration order",
);
}
#[test]
fn attribution_rule_all_partitions_confidence_axis() {
let exact = AttributionRule::ALL.iter().filter(|r| r.is_exact()).count();
let fallback = AttributionRule::ALL
.iter()
.filter(|r| r.is_fallback())
.count();
assert_eq!(exact, 3, "three ALL rules are exact");
assert_eq!(fallback, 2, "two ALL rules are fallback");
assert_eq!(
exact + fallback,
AttributionRule::ALL.len(),
"the confidence partition must cover ALL exactly once",
);
}
#[test]
fn attribution_rule_all_partitions_layer_kind_axis() {
let file = AttributionRule::ALL
.iter()
.filter(|r| r.layer_kind() == ConfigSourceKind::File)
.count();
let env = AttributionRule::ALL
.iter()
.filter(|r| r.layer_kind() == ConfigSourceKind::Env)
.count();
let defaults = AttributionRule::ALL
.iter()
.filter(|r| r.layer_kind() == ConfigSourceKind::Defaults)
.count();
assert_eq!(file, 2, "two ALL rules attribute to File");
assert_eq!(env, 2, "two ALL rules attribute to Env");
assert_eq!(defaults, 1, "one ALL rule attributes to Defaults");
assert_eq!(
file + env + defaults,
AttributionRule::ALL.len(),
"the layer_kind partition must cover ALL exactly once",
);
}
#[test]
fn attribution_rule_all_partitions_metadata_axis() {
let source = AttributionRule::ALL
.iter()
.filter(|r| r.metadata_axis() == AttributionAxis::MetadataSource)
.count();
let name = AttributionRule::ALL
.iter()
.filter(|r| r.metadata_axis() == AttributionAxis::MetadataName)
.count();
assert_eq!(source, 2, "two ALL rules dispatch off metadata.source");
assert_eq!(name, 3, "three ALL rules dispatch off metadata.name");
assert_eq!(
source + name,
AttributionRule::ALL.len(),
"the metadata_axis partition must cover ALL exactly once",
);
}
#[test]
fn attribution_rule_all_layer_kind_agrees_with_attribution_source_kind() {
for rule in AttributionRule::ALL.iter().copied() {
let src = match rule.layer_kind() {
ConfigSourceKind::File => ConfigSource::File(PathBuf::from("/etc/app.yaml")),
ConfigSourceKind::Env => ConfigSource::Env("APP_".to_owned()),
ConfigSourceKind::Defaults => ConfigSource::Defaults,
};
let attr = FailingSourceAttribution::new(&src, rule);
assert_eq!(
attr.layer_kind(),
attr.source.kind(),
"rule {rule:?}: layer_kind / source.kind() must agree",
);
}
}
#[test]
fn error_localization_coordinates_all_has_no_duplicates() {
use std::collections::HashSet;
let unique: HashSet<ErrorLocalizationCoordinates> =
ErrorLocalizationCoordinates::ALL.iter().copied().collect();
assert_eq!(
unique.len(),
ErrorLocalizationCoordinates::ALL.len(),
"ErrorLocalizationCoordinates::ALL must contain no duplicates",
);
}
#[test]
fn error_localization_coordinates_all_cardinality_matches_product_of_axes() {
assert_eq!(
ErrorLocalizationCoordinates::ALL.len(),
ShikumiErrorKind::ALL.len() * FieldPathLocalization::ALL.len(),
"ALL must equal the cartesian product cardinality",
);
assert_eq!(
ErrorLocalizationCoordinates::ALL.len(),
18,
"ALL must have 6 * 3 = 18 cells today",
);
}
#[test]
fn error_localization_coordinates_all_equals_axes_cartesian_product() {
use std::collections::HashSet;
let mut expected: HashSet<ErrorLocalizationCoordinates> = HashSet::new();
for kind in ShikumiErrorKind::ALL.iter().copied() {
for localization in FieldPathLocalization::ALL.iter().copied() {
expected.insert(ErrorLocalizationCoordinates { kind, localization });
}
}
let listed: HashSet<ErrorLocalizationCoordinates> =
ErrorLocalizationCoordinates::ALL.iter().copied().collect();
assert_eq!(
listed, expected,
"ALL must be the exact cartesian product of the sibling ALL slices",
);
}
#[test]
fn error_localization_coordinates_all_iterates_in_lexicographic_order() {
let mut expected: Vec<ErrorLocalizationCoordinates> = Vec::new();
for kind in ShikumiErrorKind::ALL.iter().copied() {
for localization in FieldPathLocalization::ALL.iter().copied() {
expected.push(ErrorLocalizationCoordinates { kind, localization });
}
}
let listed: Vec<ErrorLocalizationCoordinates> = ErrorLocalizationCoordinates::ALL.to_vec();
assert_eq!(
listed, expected,
"ALL must iterate in kind-outer / localization-inner lexicographic order",
);
}
#[test]
fn error_localization_coordinates_is_realizable_agrees_with_figment_bearing_law() {
for cell in ErrorLocalizationCoordinates::ALL.iter().copied() {
let expected = cell.kind.is_figment_bearing()
== !matches!(cell.localization, FieldPathLocalization::NotApplicable);
assert_eq!(
cell.is_realizable(),
expected,
"cell {cell:?}: is_realizable must equal the figment-bearing law",
);
}
}
#[test]
fn error_localization_coordinates_realizable_partitions_into_8_realizable_and_10_unrealizable()
{
let realizable = ErrorLocalizationCoordinates::ALL
.iter()
.filter(|c| c.is_realizable())
.count();
let unrealizable = ErrorLocalizationCoordinates::ALL
.iter()
.filter(|c| !c.is_realizable())
.count();
assert_eq!(realizable, 8, "realizable cells must be 8");
assert_eq!(unrealizable, 10, "unrealizable cells must be 10");
assert_eq!(
realizable + unrealizable,
ErrorLocalizationCoordinates::ALL.len(),
"realizable + unrealizable must cover ALL exactly once",
);
}
#[test]
fn error_localization_coordinates_realizable_image_equals_observed_pairs() {
use std::collections::HashSet;
let mut observed: HashSet<ErrorLocalizationCoordinates> = HashSet::new();
for (_, err) in one_per_kind() {
observed.insert(err.error_localization_coordinates());
}
for (err, _) in one_per_localization() {
observed.insert(err.error_localization_coordinates());
}
let figment_localized = ShikumiError::Figment(Box::new(
figment::Error::from("t".to_owned()).with_path("k"),
));
observed.insert(figment_localized.error_localization_coordinates());
let realizable: HashSet<ErrorLocalizationCoordinates> = ErrorLocalizationCoordinates::ALL
.iter()
.copied()
.filter(|c| c.is_realizable())
.collect();
assert_eq!(
observed, realizable,
"observed pairs from the construction-table surface must equal the realizable cells",
);
}
#[test]
fn shikumi_error_error_localization_coordinates_returns_realizable_cell() {
for (_, err) in one_per_kind() {
let cell = err.error_localization_coordinates();
assert!(
cell.is_realizable(),
"every constructible error must map to a realizable cell (got {cell:?} from {err:?})",
);
}
for (err, _) in one_per_localization() {
let cell = err.error_localization_coordinates();
assert!(
cell.is_realizable(),
"every constructible error must map to a realizable cell (got {cell:?} from {err:?})",
);
}
}
#[test]
fn shikumi_error_error_localization_coordinates_mirrors_sibling_accessors() {
for (_, err) in one_per_kind() {
let cell = err.error_localization_coordinates();
assert_eq!(
cell.kind,
err.kind(),
"coordinate.kind must agree with err.kind() for {err:?}",
);
assert_eq!(
cell.localization,
err.field_path_localization(),
"coordinate.localization must agree with err.field_path_localization() for {err:?}",
);
}
}
#[test]
fn error_localization_coordinates_is_copy_and_hashable() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(ErrorLocalizationCoordinates {
kind: ShikumiErrorKind::Extract,
localization: FieldPathLocalization::Localized,
});
set.insert(ErrorLocalizationCoordinates {
kind: ShikumiErrorKind::Parse,
localization: FieldPathLocalization::NotApplicable,
});
set.insert(ErrorLocalizationCoordinates {
kind: ShikumiErrorKind::Extract,
localization: FieldPathLocalization::Localized,
});
assert_eq!(set.len(), 2, "every coordinate must hash distinctly");
let c = ErrorLocalizationCoordinates {
kind: ShikumiErrorKind::Figment,
localization: FieldPathLocalization::FigmentUnlocalized,
};
let c2 = c;
let c3 = c;
assert_eq!(c, c2);
assert_eq!(c2, c3);
}
#[test]
fn attribution_source_kind_coordinates_all_has_no_duplicates() {
use std::collections::HashSet;
let unique: HashSet<AttributionSourceKindCoordinates> =
AttributionSourceKindCoordinates::ALL
.iter()
.copied()
.collect();
assert_eq!(
unique.len(),
AttributionSourceKindCoordinates::ALL.len(),
"AttributionSourceKindCoordinates::ALL must contain no duplicates; got: {:?}",
AttributionSourceKindCoordinates::ALL,
);
}
#[test]
fn attribution_source_kind_coordinates_all_cardinality_matches_product_of_axes() {
assert_eq!(
AttributionSourceKindCoordinates::ALL.len(),
FigmentSourceKind::ALL.len() * ConfigSourceKind::ALL.len(),
"ALL must equal the cartesian product cardinality",
);
assert_eq!(
AttributionSourceKindCoordinates::ALL.len(),
9,
"ALL must have 3 * 3 = 9 cells today",
);
}
#[test]
fn attribution_source_kind_coordinates_all_equals_axes_cartesian_product() {
use std::collections::HashSet;
let mut expected: HashSet<AttributionSourceKindCoordinates> = HashSet::new();
for figment_source_kind in FigmentSourceKind::ALL.iter().copied() {
for layer_kind in ConfigSourceKind::ALL.iter().copied() {
expected.insert(AttributionSourceKindCoordinates {
figment_source_kind,
layer_kind,
});
}
}
let listed: HashSet<AttributionSourceKindCoordinates> =
AttributionSourceKindCoordinates::ALL
.iter()
.copied()
.collect();
assert_eq!(
listed, expected,
"ALL must be the exact cartesian product of the sibling ALL slices",
);
}
#[test]
fn attribution_source_kind_coordinates_all_iterates_in_lexicographic_order() {
let mut expected: Vec<AttributionSourceKindCoordinates> = Vec::new();
for figment_source_kind in FigmentSourceKind::ALL.iter().copied() {
for layer_kind in ConfigSourceKind::ALL.iter().copied() {
expected.push(AttributionSourceKindCoordinates {
figment_source_kind,
layer_kind,
});
}
}
let listed: Vec<AttributionSourceKindCoordinates> =
AttributionSourceKindCoordinates::ALL.to_vec();
assert_eq!(
listed, expected,
"ALL must iterate in figment_source_kind-outer / layer_kind-inner lexicographic order",
);
}
#[test]
fn attribution_source_kind_coordinates_is_realizable_matches_diagonal() {
for cell in AttributionSourceKindCoordinates::ALL.iter().copied() {
let expected = matches!(
(cell.figment_source_kind, cell.layer_kind),
(FigmentSourceKind::File, ConfigSourceKind::File)
| (FigmentSourceKind::Code, ConfigSourceKind::Defaults)
);
assert_eq!(
cell.is_realizable(),
expected,
"cell {cell:?}: is_realizable must equal the source-axis diagonal law",
);
}
}
#[test]
fn attribution_source_kind_coordinates_realizable_partitions_into_2_realizable_and_7_unrealizable()
{
let realizable = AttributionSourceKindCoordinates::ALL
.iter()
.filter(|c| c.is_realizable())
.count();
let unrealizable = AttributionSourceKindCoordinates::ALL
.iter()
.filter(|c| !c.is_realizable())
.count();
assert_eq!(realizable, 2, "realizable cells must be 2");
assert_eq!(unrealizable, 7, "unrealizable cells must be 7");
assert_eq!(
realizable + unrealizable,
AttributionSourceKindCoordinates::ALL.len(),
"realizable + unrealizable must cover ALL exactly once",
);
}
#[test]
fn attribution_source_kind_coordinates_realizable_image_equals_rule_image() {
use std::collections::HashSet;
let observed: HashSet<AttributionSourceKindCoordinates> = AttributionRule::ALL
.iter()
.copied()
.filter_map(AttributionRule::attribution_source_kind_coordinates)
.collect();
let realizable: HashSet<AttributionSourceKindCoordinates> =
AttributionSourceKindCoordinates::ALL
.iter()
.copied()
.filter(|c| c.is_realizable())
.collect();
assert_eq!(
observed, realizable,
"observed image over AttributionRule::ALL must equal the realizable cells",
);
}
#[test]
fn attribution_rule_attribution_source_kind_coordinates_returns_realizable_cell_when_some() {
for rule in AttributionRule::ALL.iter().copied() {
if let Some(cell) = rule.attribution_source_kind_coordinates() {
assert!(
cell.is_realizable(),
"rule {rule:?} mapped to non-realizable cell {cell:?}",
);
}
}
}
#[test]
fn attribution_rule_attribution_source_kind_coordinates_some_iff_metadata_axis_source() {
for rule in AttributionRule::ALL.iter().copied() {
assert_eq!(
rule.attribution_source_kind_coordinates().is_some(),
rule.metadata_axis() == AttributionAxis::MetadataSource,
"rule {rule:?}: attribution_source_kind_coordinates.is_some() must equal \
(metadata_axis == MetadataSource)",
);
}
}
#[test]
fn attribution_rule_attribution_source_kind_coordinates_mirrors_paired_projections() {
for rule in AttributionRule::ALL.iter().copied() {
let joint = rule.attribution_source_kind_coordinates();
let paired = rule.figment_source_kind().map(|figment_source_kind| {
AttributionSourceKindCoordinates {
figment_source_kind,
layer_kind: rule.layer_kind(),
}
});
assert_eq!(
joint, paired,
"rule {rule:?}: joint accessor must equal the paired projections",
);
}
}
#[test]
fn attribution_rule_attribution_source_kind_coordinates_pins_known_rules() {
let cases: [(AttributionRule, Option<AttributionSourceKindCoordinates>); 5] = [
(
AttributionRule::FileBySource,
Some(AttributionSourceKindCoordinates {
figment_source_kind: FigmentSourceKind::File,
layer_kind: ConfigSourceKind::File,
}),
),
(
AttributionRule::DefaultsByCodeUniqueness,
Some(AttributionSourceKindCoordinates {
figment_source_kind: FigmentSourceKind::Code,
layer_kind: ConfigSourceKind::Defaults,
}),
),
(AttributionRule::FileByMetadataName, None),
(AttributionRule::EnvByPrefix, None),
(AttributionRule::EnvByUniqueness, None),
];
for (rule, expected) in cases {
assert_eq!(
rule.attribution_source_kind_coordinates(),
expected,
"rule {rule:?}: attribution_source_kind_coordinates pin",
);
}
}
#[test]
fn failing_source_attribution_attribution_source_kind_coordinates_mirrors_rule() {
for rule in AttributionRule::ALL.iter().copied() {
let src = ConfigSource::Defaults;
let attr = FailingSourceAttribution::new(&src, rule);
assert_eq!(
attr.attribution_source_kind_coordinates(),
rule.attribution_source_kind_coordinates(),
"envelope for rule {rule:?}",
);
}
}
#[test]
fn attribution_source_kind_coordinates_is_copy_and_hashable() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(AttributionSourceKindCoordinates {
figment_source_kind: FigmentSourceKind::File,
layer_kind: ConfigSourceKind::File,
});
set.insert(AttributionSourceKindCoordinates {
figment_source_kind: FigmentSourceKind::Code,
layer_kind: ConfigSourceKind::Defaults,
});
set.insert(AttributionSourceKindCoordinates {
figment_source_kind: FigmentSourceKind::File,
layer_kind: ConfigSourceKind::File,
});
assert_eq!(set.len(), 2, "every coordinate must hash distinctly");
let c = AttributionSourceKindCoordinates {
figment_source_kind: FigmentSourceKind::Custom,
layer_kind: ConfigSourceKind::Env,
};
let c2 = c;
let c3 = c;
assert_eq!(c, c2);
assert_eq!(c2, c3);
}
#[test]
fn attribution_source_kind_coordinates_realizable_image_lies_in_attribution_source_kind_coordinates_all()
{
use std::collections::HashSet;
let observed: HashSet<AttributionSourceKindCoordinates> = AttributionRule::ALL
.iter()
.copied()
.filter_map(AttributionRule::attribution_source_kind_coordinates)
.collect();
let declared: HashSet<AttributionSourceKindCoordinates> =
AttributionSourceKindCoordinates::ALL
.iter()
.copied()
.collect();
assert!(
observed.is_subset(&declared),
"image of attribution_source_kind_coordinates must lie in \
AttributionSourceKindCoordinates::ALL; observed: {observed:?}, \
declared: {declared:?}",
);
}
#[test]
fn attribution_name_kind_coordinates_all_has_no_duplicates() {
use std::collections::HashSet;
let unique: HashSet<AttributionNameKindCoordinates> = AttributionNameKindCoordinates::ALL
.iter()
.copied()
.collect();
assert_eq!(
unique.len(),
AttributionNameKindCoordinates::ALL.len(),
"AttributionNameKindCoordinates::ALL must contain no duplicates; got: {:?}",
AttributionNameKindCoordinates::ALL,
);
}
#[test]
fn attribution_name_kind_coordinates_all_cardinality_matches_product_of_axes() {
assert_eq!(
AttributionNameKindCoordinates::ALL.len(),
FigmentNameTagKind::ALL.len() * ConfigSourceKind::ALL.len(),
"ALL must equal the cartesian product cardinality",
);
assert_eq!(
AttributionNameKindCoordinates::ALL.len(),
6,
"ALL must have 2 * 3 = 6 cells today",
);
}
#[test]
fn attribution_name_kind_coordinates_all_equals_axes_cartesian_product() {
use std::collections::HashSet;
let mut expected: HashSet<AttributionNameKindCoordinates> = HashSet::new();
for figment_name_tag_kind in FigmentNameTagKind::ALL.iter().copied() {
for layer_kind in ConfigSourceKind::ALL.iter().copied() {
expected.insert(AttributionNameKindCoordinates {
figment_name_tag_kind,
layer_kind,
});
}
}
let listed: HashSet<AttributionNameKindCoordinates> = AttributionNameKindCoordinates::ALL
.iter()
.copied()
.collect();
assert_eq!(
listed, expected,
"ALL must be the exact cartesian product of the sibling ALL slices",
);
}
#[test]
fn attribution_name_kind_coordinates_all_iterates_in_lexicographic_order() {
let mut expected: Vec<AttributionNameKindCoordinates> = Vec::new();
for figment_name_tag_kind in FigmentNameTagKind::ALL.iter().copied() {
for layer_kind in ConfigSourceKind::ALL.iter().copied() {
expected.push(AttributionNameKindCoordinates {
figment_name_tag_kind,
layer_kind,
});
}
}
let listed: Vec<AttributionNameKindCoordinates> =
AttributionNameKindCoordinates::ALL.to_vec();
assert_eq!(
listed, expected,
"ALL must iterate in figment_name_tag_kind-outer / layer_kind-inner lexicographic order",
);
}
#[test]
fn attribution_name_kind_coordinates_is_realizable_matches_diagonal() {
for cell in AttributionNameKindCoordinates::ALL.iter().copied() {
let expected = matches!(
(cell.figment_name_tag_kind, cell.layer_kind),
(FigmentNameTagKind::Format, ConfigSourceKind::File)
| (FigmentNameTagKind::Env, ConfigSourceKind::Env)
);
assert_eq!(
cell.is_realizable(),
expected,
"cell {cell:?}: is_realizable must equal the name-axis diagonal law",
);
}
}
#[test]
fn attribution_name_kind_coordinates_realizable_partitions_into_2_realizable_and_4_unrealizable()
{
let realizable = AttributionNameKindCoordinates::ALL
.iter()
.filter(|c| c.is_realizable())
.count();
let unrealizable = AttributionNameKindCoordinates::ALL
.iter()
.filter(|c| !c.is_realizable())
.count();
assert_eq!(realizable, 2, "realizable cells must be 2");
assert_eq!(unrealizable, 4, "unrealizable cells must be 4");
assert_eq!(
realizable + unrealizable,
AttributionNameKindCoordinates::ALL.len(),
"realizable + unrealizable must cover ALL exactly once",
);
}
#[test]
fn attribution_name_kind_coordinates_realizable_image_equals_rule_image() {
use std::collections::HashSet;
let observed: HashSet<AttributionNameKindCoordinates> = AttributionRule::ALL
.iter()
.copied()
.filter_map(AttributionRule::attribution_name_kind_coordinates)
.collect();
let realizable: HashSet<AttributionNameKindCoordinates> =
AttributionNameKindCoordinates::ALL
.iter()
.copied()
.filter(|c| c.is_realizable())
.collect();
assert_eq!(
observed, realizable,
"observed image over AttributionRule::ALL must equal the realizable cells",
);
}
#[test]
fn attribution_rule_attribution_name_kind_coordinates_returns_realizable_cell_when_some() {
for rule in AttributionRule::ALL.iter().copied() {
if let Some(cell) = rule.attribution_name_kind_coordinates() {
assert!(
cell.is_realizable(),
"rule {rule:?} mapped to non-realizable cell {cell:?}",
);
}
}
}
#[test]
fn attribution_rule_attribution_name_kind_coordinates_some_iff_metadata_axis_name() {
for rule in AttributionRule::ALL.iter().copied() {
assert_eq!(
rule.attribution_name_kind_coordinates().is_some(),
rule.metadata_axis() == AttributionAxis::MetadataName,
"rule {rule:?}: attribution_name_kind_coordinates.is_some() must equal \
(metadata_axis == MetadataName)",
);
}
}
#[test]
fn attribution_rule_attribution_name_kind_coordinates_mirrors_paired_projections() {
for rule in AttributionRule::ALL.iter().copied() {
let joint = rule.attribution_name_kind_coordinates();
let paired = rule.figment_name_tag_kind().map(|figment_name_tag_kind| {
AttributionNameKindCoordinates {
figment_name_tag_kind,
layer_kind: rule.layer_kind(),
}
});
assert_eq!(
joint, paired,
"rule {rule:?}: joint accessor must equal the paired projections",
);
}
}
#[test]
fn attribution_rule_attribution_name_kind_coordinates_pins_known_rules() {
let cases: [(AttributionRule, Option<AttributionNameKindCoordinates>); 5] = [
(AttributionRule::FileBySource, None),
(
AttributionRule::FileByMetadataName,
Some(AttributionNameKindCoordinates {
figment_name_tag_kind: FigmentNameTagKind::Format,
layer_kind: ConfigSourceKind::File,
}),
),
(
AttributionRule::EnvByPrefix,
Some(AttributionNameKindCoordinates {
figment_name_tag_kind: FigmentNameTagKind::Env,
layer_kind: ConfigSourceKind::Env,
}),
),
(
AttributionRule::EnvByUniqueness,
Some(AttributionNameKindCoordinates {
figment_name_tag_kind: FigmentNameTagKind::Env,
layer_kind: ConfigSourceKind::Env,
}),
),
(AttributionRule::DefaultsByCodeUniqueness, None),
];
for (rule, expected) in cases {
assert_eq!(
rule.attribution_name_kind_coordinates(),
expected,
"rule {rule:?}: attribution_name_kind_coordinates pin",
);
}
}
#[test]
fn attribution_rule_attribution_name_kind_coordinates_xor_attribution_source_kind_coordinates()
{
for rule in AttributionRule::ALL.iter().copied() {
let source = rule.attribution_source_kind_coordinates().is_some();
let name = rule.attribution_name_kind_coordinates().is_some();
assert_ne!(
source, name,
"rule {rule:?}: exactly one of the two joint cells must be Some",
);
}
}
#[test]
fn failing_source_attribution_attribution_name_kind_coordinates_mirrors_rule() {
for rule in AttributionRule::ALL.iter().copied() {
let src = ConfigSource::Defaults;
let attr = FailingSourceAttribution::new(&src, rule);
assert_eq!(
attr.attribution_name_kind_coordinates(),
rule.attribution_name_kind_coordinates(),
"envelope for rule {rule:?}",
);
}
}
#[test]
fn attribution_name_kind_coordinates_is_copy_and_hashable() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(AttributionNameKindCoordinates {
figment_name_tag_kind: FigmentNameTagKind::Format,
layer_kind: ConfigSourceKind::File,
});
set.insert(AttributionNameKindCoordinates {
figment_name_tag_kind: FigmentNameTagKind::Env,
layer_kind: ConfigSourceKind::Env,
});
set.insert(AttributionNameKindCoordinates {
figment_name_tag_kind: FigmentNameTagKind::Format,
layer_kind: ConfigSourceKind::File,
});
assert_eq!(set.len(), 2, "every coordinate must hash distinctly");
let c = AttributionNameKindCoordinates {
figment_name_tag_kind: FigmentNameTagKind::Env,
layer_kind: ConfigSourceKind::Defaults,
};
let c2 = c;
let c3 = c;
assert_eq!(c, c2);
assert_eq!(c2, c3);
}
#[test]
fn attribution_name_kind_coordinates_realizable_image_lies_in_attribution_name_kind_coordinates_all()
{
use std::collections::HashSet;
let observed: HashSet<AttributionNameKindCoordinates> = AttributionRule::ALL
.iter()
.copied()
.filter_map(AttributionRule::attribution_name_kind_coordinates)
.collect();
let declared: HashSet<AttributionNameKindCoordinates> = AttributionNameKindCoordinates::ALL
.iter()
.copied()
.collect();
assert!(
observed.is_subset(&declared),
"image of attribution_name_kind_coordinates must lie in \
AttributionNameKindCoordinates::ALL; observed: {observed:?}, \
declared: {declared:?}",
);
}
}