use super::*;
#[test]
fn test_struct_with_default_derive() {
let source = r#"
/// A configuration with sensible defaults.
#[derive(Default, Clone)]
pub struct Config {
pub name: String,
pub count: u32,
pub enabled: bool,
}
"#;
let surface = extract_from_source(source);
assert_eq!(surface.types.len(), 1);
let config = &surface.types[0];
assert_eq!(config.name, "Config");
assert!(
config.has_default,
"Config with #[derive(Default)] should have has_default=true"
);
for field in &config.fields {
assert_eq!(
field.typed_default,
Some(crate::core::ir::DefaultValue::Empty),
"`{}` is a derived type-zero and must stay `Empty`, never `Unresolved`",
field.name
);
}
}
#[test]
fn test_struct_without_default() {
let source = r#"
/// A configuration without defaults.
pub struct Custom {
pub value: String,
}
"#;
let surface = extract_from_source(source);
assert_eq!(surface.types.len(), 1);
let custom = &surface.types[0];
assert_eq!(custom.name, "Custom");
assert!(
!custom.has_default,
"Struct without Default should have has_default=false"
);
}
#[test]
fn test_serde_function_default_preserves_runtime_provider() {
let source = r#"
pub struct RetryPolicy {
#[serde(default = "defaults::retry_limit")]
pub limit: u32,
}
"#;
let surface = extract_from_source(source);
let limit = &surface.types[0].fields[0];
assert_eq!(
limit.typed_default,
Some(crate::core::ir::DefaultValue::FunctionCall(
"defaults::retry_limit".to_string()
))
);
}
#[test]
fn public_associated_serde_default_is_resolved_as_callable() {
let source = r#"
pub struct NetworkPolicy;
impl NetworkPolicy {
pub fn from_environment() -> Self {
Self
}
}
pub struct ClientConfig {
#[serde(default = "NetworkPolicy::from_environment")]
pub policy: NetworkPolicy,
pub nested: RequiredSettings,
}
pub struct RequiredSettings {
pub label: String,
}
"#;
let surface = extract_from_source(source);
let config = surface.types.iter().find(|typ| typ.name == "ClientConfig").unwrap();
let policy = config.fields.iter().find(|field| field.name == "policy").unwrap();
assert_eq!(
policy.typed_default,
Some(crate::core::ir::DefaultValue::PublicFunctionCall(
"test_crate::NetworkPolicy::from_environment".to_string()
))
);
}
#[test]
fn test_impl_default_without_fn_default() {
let source = r#"
pub struct Incomplete {
pub value: u32,
}
impl Default for Incomplete {
// Missing fn default() - no matching method
}
"#;
let surface = extract_from_source(source);
let incomplete = &surface.types[0];
let value_field = &incomplete.fields[0];
assert_eq!(
value_field.typed_default,
Some(crate::core::ir::DefaultValue::Unresolved(
"impl Default block without a `fn default()` item".to_string()
)),
"an unreadable impl Default must be Unresolved, not Empty"
);
assert_ne!(
value_field.typed_default,
Some(crate::core::ir::DefaultValue::Empty),
"Empty licenses every backend to substitute its own zero for a default it never read"
);
}
#[test]
fn test_manual_default_impl_exposes_static_constructor() {
let source = r#"
pub struct Settings {
pub enabled: bool,
}
impl Default for Settings {
fn default() -> Self {
Self { enabled: true }
}
}
"#;
let surface = extract_from_source(source);
let settings = surface.types.iter().find(|typ| typ.name == "Settings").unwrap();
let default = settings.methods.iter().find(|method| method.name == "default").unwrap();
assert!(default.is_static);
assert!(default.params.is_empty());
assert!(matches!(&default.return_type, crate::core::ir::TypeRef::Named(name) if name == "Settings"));
}
#[test]
fn test_enum_with_default_derive_and_default_variant() {
let source = r#"
#[derive(Default, Clone)]
pub enum Priority {
#[default]
Normal,
High,
Low,
}
"#;
let surface = extract_from_source(source);
assert_eq!(surface.enums.len(), 1);
let priority = &surface.enums[0];
assert_eq!(priority.name, "Priority");
assert_eq!(priority.variants.len(), 3);
let normal = &priority.variants[0];
assert_eq!(normal.name, "Normal");
assert!(
normal.is_default,
"Normal variant with #[default] should have is_default=true"
);
let high = &priority.variants[1];
assert_eq!(high.name, "High");
assert!(!high.is_default, "Non-default variant should have is_default=false");
let low = &priority.variants[2];
assert_eq!(low.name, "Low");
assert!(!low.is_default);
}
#[test]
fn test_enum_without_default() {
let source = r#"
pub enum Format {
Json,
Xml,
Yaml,
}
"#;
let surface = extract_from_source(source);
let format = &surface.enums[0];
for variant in &format.variants {
assert!(
!variant.is_default,
"Variants without #[default] should be is_default=false"
);
}
}
#[test]
fn test_enum_with_manual_default_impl() {
let source = r#"
pub enum ClassificationMode {
Known,
Custom(String),
}
impl Default for ClassificationMode {
fn default() -> Self {
Self::Custom(String::new())
}
}
"#;
let surface = extract_from_source(source);
let mode = &surface.enums[0];
assert!(mode.has_default, "manual Default impl should set has_default=true");
assert!(
mode.variants.iter().all(|variant| !variant.is_default),
"manual enum Default impls should not synthesize a default variant"
);
}
#[test]
fn container_level_serde_default_is_recorded_on_the_type() {
let source = r#"
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(default)]
pub struct RetryPolicy {
pub limit: u32,
}
impl Default for RetryPolicy {
fn default() -> Self {
Self { limit: 3 }
}
}
"#;
let surface = extract_from_source(source);
let policy = &surface.types[0];
assert!(
policy.serde_container_default,
"a container-level #[serde(default)] must set serde_container_default=true"
);
assert_eq!(
policy.fields[0].default, None,
"the container attribute must not be mistaken for a per-field #[serde(default)]"
);
assert_eq!(
policy.fields[0].typed_default,
Some(crate::core::ir::DefaultValue::IntLiteral(3))
);
}
#[test]
fn deriving_default_alone_does_not_set_the_container_serde_flag() {
let source = r#"
/// Every key stays required on the wire even though the type has a Default impl.
#[derive(Default, serde::Serialize, serde::Deserialize)]
pub struct RetryPolicy {
pub limit: u32,
}
"#;
let surface = extract_from_source(source);
let policy = &surface.types[0];
assert!(policy.has_default, "derive(Default) still sets has_default");
assert!(
!policy.serde_container_default,
"derive(Default) is not #[serde(default)]: every field is still a required wire key"
);
}