#![allow(clippy::unwrap_used)]
#![allow(clippy::expect_used)]
use sublime_standard_tools::config::Configurable;
use crate::config::{
AuditConfig, AuditSectionsConfig, BackupConfig, BreakingChangesAuditConfig, ChangelogConfig,
ChangelogFormat, ChangesetConfig, ConventionalConfig, DependencyAuditConfig, DependencyConfig,
ExecuteConfig, GitConfig, MonorepoMode, PackageToolsConfig, RegistryConfig, UpgradeAuditConfig,
UpgradeConfig, VersionConfig, VersionConsistencyAuditConfig, VersioningStrategy,
};
mod package_tools_config {
use super::*;
#[test]
fn test_default_config_is_valid() {
let config = PackageToolsConfig::default();
assert!(config.validate().is_ok());
}
#[test]
fn test_config_serialization() {
let config = PackageToolsConfig::default();
let serialized = serde_json::to_string(&config);
assert!(serialized.is_ok());
}
#[test]
fn test_config_deserialization() {
let config = PackageToolsConfig::default();
let serialized = serde_json::to_string(&config).expect("Should serialize");
let deserialized: PackageToolsConfig =
serde_json::from_str(&serialized).expect("Should deserialize");
assert_eq!(deserialized.changeset.path, config.changeset.path);
assert_eq!(deserialized.version.strategy, config.version.strategy);
assert_eq!(deserialized.dependency.propagation_bump, config.dependency.propagation_bump);
assert_eq!(deserialized.upgrade.auto_changeset, config.upgrade.auto_changeset);
assert_eq!(deserialized.changelog.enabled, config.changelog.enabled);
assert_eq!(deserialized.git.include_breaking_warning, config.git.include_breaking_warning);
assert_eq!(deserialized.audit.enabled, config.audit.enabled);
}
#[test]
fn test_config_merge() {
let mut base = PackageToolsConfig::default();
let other = PackageToolsConfig::default();
assert!(base.merge_with(other).is_ok());
}
#[test]
fn test_nested_config_access() {
let config = PackageToolsConfig::default();
assert_eq!(config.changeset.path, ".changesets");
assert_eq!(config.version.default_bump, "patch");
assert!(config.dependency.propagate_dependencies);
assert!(config.upgrade.auto_changeset);
assert!(config.changelog.enabled);
assert!(config.git.include_breaking_warning);
assert!(config.audit.enabled);
}
}
mod changeset_config {
use super::*;
#[test]
fn test_default_config_is_valid() {
let config = ChangesetConfig::default();
assert!(config.validate().is_ok());
}
#[test]
fn test_default_values() {
let config = ChangesetConfig::default();
assert_eq!(config.path, ".changesets");
assert_eq!(config.history_path, ".changesets/history");
assert_eq!(config.available_environments, vec!["production"]);
assert_eq!(config.default_environments, vec!["production"]);
}
#[test]
fn test_empty_path_validation() {
let config = ChangesetConfig { path: String::new(), ..Default::default() };
assert!(config.validate().is_err());
}
#[test]
fn test_empty_history_path_validation() {
let config = ChangesetConfig { history_path: String::new(), ..Default::default() };
assert!(config.validate().is_err());
}
#[test]
fn test_empty_available_environments_validation() {
let config = ChangesetConfig { available_environments: vec![], ..Default::default() };
assert!(config.validate().is_err());
}
#[test]
fn test_invalid_default_environment() {
let config = ChangesetConfig {
available_environments: vec!["production".to_string()],
default_environments: vec!["staging".to_string()],
..Default::default()
};
assert!(config.validate().is_err());
}
#[test]
fn test_valid_multiple_environments() {
let config = ChangesetConfig {
available_environments: vec![
"development".to_string(),
"staging".to_string(),
"production".to_string(),
],
default_environments: vec!["staging".to_string(), "production".to_string()],
..Default::default()
};
assert!(config.validate().is_ok());
}
#[test]
fn test_serialization() {
let config = ChangesetConfig::default();
let serialized = serde_json::to_string(&config);
assert!(serialized.is_ok());
}
#[test]
fn test_deserialization() {
let json = r#"{
"path": ".changesets",
"history_path": ".changesets/history",
"available_environments": ["production"],
"default_environments": ["production"]
}"#;
let result: Result<ChangesetConfig, _> = serde_json::from_str(json);
assert!(result.is_ok());
assert_eq!(result.unwrap(), ChangesetConfig::default());
}
#[test]
fn test_merge() {
let mut base = ChangesetConfig::default();
let override_config = ChangesetConfig {
path: ".custom-changesets".to_string(),
history_path: ".custom-history".to_string(),
available_environments: vec!["dev".to_string(), "prod".to_string()],
default_environments: vec!["prod".to_string()],
};
assert!(base.merge_with(override_config.clone()).is_ok());
assert_eq!(base.path, ".custom-changesets");
assert_eq!(base.history_path, ".custom-history");
assert_eq!(base.available_environments, override_config.available_environments);
assert_eq!(base.default_environments, override_config.default_environments);
}
}
mod version_config {
use super::*;
#[test]
fn test_default_config_is_valid() {
let config = VersionConfig::default();
assert!(config.validate().is_ok());
}
#[test]
fn test_default_values() {
let config = VersionConfig::default();
assert_eq!(config.strategy, VersioningStrategy::Independent);
assert_eq!(config.default_bump, "patch");
assert_eq!(config.snapshot_format, "{version}-{branch}.{timestamp}");
}
#[test]
fn test_versioning_strategy_serialization() {
let independent = VersioningStrategy::Independent;
let serialized = serde_json::to_string(&independent).unwrap();
assert_eq!(serialized, r#""independent""#);
let unified = VersioningStrategy::Unified;
let serialized = serde_json::to_string(&unified).unwrap();
assert_eq!(serialized, r#""unified""#);
}
#[test]
fn test_versioning_strategy_deserialization() {
let json = r#""independent""#;
let result: Result<VersioningStrategy, _> = serde_json::from_str(json);
assert!(result.is_ok());
assert_eq!(result.unwrap(), VersioningStrategy::Independent);
let json = r#""unified""#;
let result: Result<VersioningStrategy, _> = serde_json::from_str(json);
assert!(result.is_ok());
assert_eq!(result.unwrap(), VersioningStrategy::Unified);
}
#[test]
fn test_invalid_default_bump() {
let config = VersionConfig { default_bump: "invalid".to_string(), ..Default::default() };
assert!(config.validate().is_err());
}
#[test]
fn test_valid_bump_types() {
for bump in &["major", "minor", "patch", "none"] {
let config = VersionConfig { default_bump: bump.to_string(), ..Default::default() };
assert!(config.validate().is_ok(), "Bump type '{}' should be valid", bump);
}
}
#[test]
fn test_empty_snapshot_format() {
let config = VersionConfig { snapshot_format: String::new(), ..Default::default() };
assert!(config.validate().is_err());
}
#[test]
fn test_serialization() {
let config = VersionConfig::default();
let serialized = serde_json::to_string(&config);
assert!(serialized.is_ok());
}
#[test]
fn test_deserialization() {
let json = r#"{
"strategy": "independent",
"default_bump": "patch",
"snapshot_format": "{version}-{branch}.{timestamp}"
}"#;
let result: Result<VersionConfig, _> = serde_json::from_str(json);
assert!(result.is_ok());
assert_eq!(result.unwrap(), VersionConfig::default());
}
#[test]
fn test_merge() {
let mut base = VersionConfig::default();
let override_config = VersionConfig {
strategy: VersioningStrategy::Unified,
default_bump: "minor".to_string(),
snapshot_format: "{version}-snapshot".to_string(),
};
assert!(base.merge_with(override_config.clone()).is_ok());
assert_eq!(base.strategy, VersioningStrategy::Unified);
assert_eq!(base.default_bump, "minor");
assert_eq!(base.snapshot_format, "{version}-snapshot");
}
#[test]
fn test_custom_snapshot_format() {
let config = VersionConfig {
snapshot_format: "{version}-{short_hash}".to_string(),
..Default::default()
};
assert!(config.validate().is_ok());
}
}
mod dependency_config {
use super::*;
#[test]
fn test_default_config_is_valid() {
let config = DependencyConfig::default();
assert!(config.validate().is_ok());
}
#[test]
fn test_default_values() {
let config = DependencyConfig::default();
assert_eq!(config.propagation_bump, "patch");
assert!(config.propagate_dependencies);
assert!(!config.propagate_dev_dependencies);
assert!(config.propagate_peer_dependencies);
assert_eq!(config.max_depth, 10);
assert!(config.fail_on_circular);
assert!(config.skip_workspace_protocol);
assert!(config.skip_file_protocol);
assert!(config.skip_link_protocol);
assert!(config.skip_portal_protocol);
}
#[test]
fn test_invalid_propagation_bump() {
let config =
DependencyConfig { propagation_bump: "invalid".to_string(), ..Default::default() };
assert!(config.validate().is_err());
}
#[test]
fn test_valid_bump_types() {
for bump in &["major", "minor", "patch", "none"] {
let config =
DependencyConfig { propagation_bump: bump.to_string(), ..Default::default() };
assert!(config.validate().is_ok(), "Bump type '{}' should be valid", bump);
}
}
#[test]
fn test_no_propagation_enabled() {
let config = DependencyConfig {
propagate_dependencies: false,
propagate_dev_dependencies: false,
propagate_peer_dependencies: false,
..Default::default()
};
assert!(config.validate().is_err());
}
#[test]
fn test_only_dev_dependencies_enabled() {
let config = DependencyConfig {
propagate_dependencies: false,
propagate_dev_dependencies: true,
propagate_peer_dependencies: false,
..Default::default()
};
assert!(config.validate().is_ok());
}
#[test]
fn test_serialization() {
let config = DependencyConfig::default();
let serialized = serde_json::to_string(&config);
assert!(serialized.is_ok());
}
#[test]
fn test_deserialization() {
let json = r#"{
"propagation_bump": "patch",
"propagate_dependencies": true,
"propagate_dev_dependencies": false,
"propagate_peer_dependencies": true,
"max_depth": 10,
"fail_on_circular": true,
"skip_workspace_protocol": true,
"skip_file_protocol": true,
"skip_link_protocol": true,
"skip_portal_protocol": true
}"#;
let result: Result<DependencyConfig, _> = serde_json::from_str(json);
assert!(result.is_ok());
assert_eq!(result.unwrap(), DependencyConfig::default());
}
#[test]
fn test_merge() {
let mut base = DependencyConfig::default();
let override_config = DependencyConfig {
propagation_bump: "minor".to_string(),
propagate_dependencies: false,
propagate_dev_dependencies: true,
propagate_peer_dependencies: false,
max_depth: 5,
fail_on_circular: false,
skip_workspace_protocol: false,
skip_file_protocol: false,
skip_link_protocol: false,
skip_portal_protocol: false,
};
assert!(base.merge_with(override_config.clone()).is_ok());
assert_eq!(base.propagation_bump, "minor");
assert!(!base.propagate_dependencies);
assert!(base.propagate_dev_dependencies);
assert!(!base.propagate_peer_dependencies);
assert_eq!(base.max_depth, 5);
assert!(!base.fail_on_circular);
assert!(!base.skip_workspace_protocol);
assert!(!base.skip_file_protocol);
assert!(!base.skip_link_protocol);
assert!(!base.skip_portal_protocol);
}
#[test]
fn test_max_depth_zero() {
let config = DependencyConfig { max_depth: 0, ..Default::default() };
assert!(config.validate().is_ok());
}
#[test]
fn test_all_protocols_disabled() {
let config = DependencyConfig {
skip_workspace_protocol: false,
skip_file_protocol: false,
skip_link_protocol: false,
skip_portal_protocol: false,
..Default::default()
};
assert!(config.validate().is_ok());
}
}
mod git_config {
use super::*;
#[test]
fn test_default_config_is_valid() {
let config = GitConfig::default();
assert!(config.validate().is_ok());
}
#[test]
fn test_default_values() {
let config = GitConfig::default();
assert_eq!(
config.merge_commit_template,
"chore(release): {version}\n\nRelease version {version}\n\n{changelog_summary}"
);
assert_eq!(
config.monorepo_merge_commit_template,
"chore(release): {package_name}@{version}\n\nRelease {package_name} version {version}\n\n{changelog_summary}"
);
assert!(config.include_breaking_warning);
assert_eq!(
config.breaking_warning_template,
"\n⚠️ BREAKING CHANGES: {breaking_changes_count}\n"
);
}
#[test]
fn test_empty_merge_commit_template() {
let config = GitConfig { merge_commit_template: String::new(), ..Default::default() };
assert!(config.validate().is_err());
}
#[test]
fn test_empty_monorepo_merge_commit_template() {
let config =
GitConfig { monorepo_merge_commit_template: String::new(), ..Default::default() };
assert!(config.validate().is_err());
}
#[test]
fn test_empty_breaking_warning_template_when_enabled() {
let config = GitConfig {
include_breaking_warning: true,
breaking_warning_template: String::new(),
..Default::default()
};
assert!(config.validate().is_err());
}
#[test]
fn test_empty_breaking_warning_template_when_disabled() {
let config = GitConfig {
include_breaking_warning: false,
breaking_warning_template: String::new(),
..Default::default()
};
assert!(config.validate().is_ok());
}
#[test]
fn test_custom_templates() {
let config = GitConfig {
merge_commit_template: "release: v{version}".to_string(),
monorepo_merge_commit_template: "release: {count} packages".to_string(),
include_breaking_warning: true,
breaking_warning_template: "BREAKING: {changes}".to_string(),
};
assert!(config.validate().is_ok());
}
#[test]
fn test_serialization() {
let config = GitConfig::default();
let serialized = serde_json::to_string(&config);
assert!(serialized.is_ok());
}
#[test]
fn test_deserialization() {
let json = r#"{
"merge_commit_template": "chore(release): {version}\n\nRelease version {version}\n\n{changelog_summary}",
"monorepo_merge_commit_template": "chore(release): {package_name}@{version}\n\nRelease {package_name} version {version}\n\n{changelog_summary}",
"include_breaking_warning": true,
"breaking_warning_template": "\n⚠️ BREAKING CHANGES: {breaking_changes_count}\n"
}"#;
let result: Result<GitConfig, _> = serde_json::from_str(json);
assert!(result.is_ok());
assert_eq!(result.unwrap(), GitConfig::default());
}
#[test]
fn test_template_placeholders() {
let config = GitConfig::default();
assert!(config.merge_commit_template.contains("{version}"));
assert!(config.merge_commit_template.contains("{changelog_summary}"));
assert!(config.monorepo_merge_commit_template.contains("{package_name}"));
assert!(config.monorepo_merge_commit_template.contains("{version}"));
assert!(config.breaking_warning_template.contains("{breaking_changes_count}"));
}
#[test]
fn test_merge() {
let mut base = GitConfig::default();
let override_config = GitConfig {
merge_commit_template: "custom: {version}".to_string(),
monorepo_merge_commit_template: "custom: {packages}".to_string(),
include_breaking_warning: false,
breaking_warning_template: "BREAKING: {changes}".to_string(),
};
assert!(base.merge_with(override_config.clone()).is_ok());
assert_eq!(base.merge_commit_template, "custom: {version}");
assert_eq!(base.monorepo_merge_commit_template, "custom: {packages}");
assert!(!base.include_breaking_warning);
assert_eq!(base.breaking_warning_template, "BREAKING: {changes}");
}
#[test]
fn test_breaking_warning_disabled() {
let config = GitConfig { include_breaking_warning: false, ..Default::default() };
assert!(config.validate().is_ok());
assert!(!config.include_breaking_warning);
}
}
mod changelog_config {
use super::*;
#[test]
fn test_default_config_is_valid() {
let config = ChangelogConfig::default();
assert!(config.validate().is_ok());
}
#[test]
fn test_default_values() {
let config = ChangelogConfig::default();
assert!(config.enabled);
assert_eq!(config.format, ChangelogFormat::KeepAChangelog);
assert_eq!(config.filename, "CHANGELOG.md");
assert!(config.include_commit_links);
assert!(config.include_issue_links);
assert!(!config.include_authors);
}
#[test]
fn test_empty_filename_validation() {
let config = ChangelogConfig { filename: String::new(), ..Default::default() };
assert!(config.validate().is_err());
}
#[test]
fn test_conventional_config_default() {
let config = ConventionalConfig::default();
assert!(config.enabled);
assert!(config.types.contains_key("feat"));
assert!(config.types.contains_key("fix"));
assert_eq!(config.breaking_section, "Breaking Changes");
}
#[test]
fn test_format_serialization() {
let format = ChangelogFormat::KeepAChangelog;
let serialized = serde_json::to_string(&format).unwrap();
assert_eq!(serialized, r#""keep-a-changelog""#);
}
#[test]
fn test_monorepo_mode_serialization() {
let mode = MonorepoMode::PerPackage;
let serialized = serde_json::to_string(&mode).unwrap();
assert_eq!(serialized, r#""per-package""#);
}
}
mod upgrade_config {
use super::*;
use std::collections::HashMap;
#[test]
fn test_default_upgrade_config_is_valid() {
let config = UpgradeConfig::default();
assert!(config.validate().is_ok());
}
#[test]
fn test_default_registry_config_is_valid() {
let config = RegistryConfig::default();
assert!(config.validate().is_ok());
}
#[test]
fn test_default_backup_config_is_valid() {
let config = BackupConfig::default();
assert!(config.validate().is_ok());
}
#[test]
fn test_default_values() {
let config = UpgradeConfig::default();
assert!(config.auto_changeset);
assert_eq!(config.changeset_bump, "patch");
assert_eq!(config.registry.default_registry, "https://registry.npmjs.org");
assert!(config.backup.enabled);
}
#[test]
fn test_invalid_changeset_bump() {
let config = UpgradeConfig { changeset_bump: "invalid".to_string(), ..Default::default() };
assert!(config.validate().is_err());
}
#[test]
fn test_empty_registry_url() {
let config = UpgradeConfig {
registry: RegistryConfig { default_registry: String::new(), ..Default::default() },
..Default::default()
};
assert!(config.validate().is_err());
}
#[test]
fn test_zero_timeout() {
let config = UpgradeConfig {
registry: RegistryConfig { timeout_secs: 0, ..Default::default() },
..Default::default()
};
assert!(config.validate().is_err());
}
#[test]
fn test_empty_backup_dir() {
let config = UpgradeConfig {
backup: BackupConfig { backup_dir: String::new(), ..Default::default() },
..Default::default()
};
assert!(config.validate().is_err());
}
#[test]
fn test_scoped_registries() {
let mut scoped = HashMap::new();
scoped.insert("myorg".to_string(), "https://npm.myorg.com".to_string());
let config = UpgradeConfig {
registry: RegistryConfig { scoped_registries: scoped.clone(), ..Default::default() },
..Default::default()
};
assert!(config.validate().is_ok());
assert_eq!(
config.registry.scoped_registries.get("myorg").unwrap(),
"https://npm.myorg.com"
);
}
#[test]
fn test_serialization() {
let config = UpgradeConfig::default();
let serialized = serde_json::to_string(&config);
assert!(serialized.is_ok());
}
#[test]
fn test_deserialization() {
let json = r#"{
"registry": {
"default_registry": "https://registry.npmjs.org",
"scoped_registries": {},
"auth_tokens": {},
"timeout_secs": 30,
"retry_attempts": 3,
"retry_delay_ms": 1000,
"read_npmrc": true
},
"auto_changeset": true,
"changeset_bump": "patch",
"backup": {
"enabled": true,
"backup_dir": ".workspace-backups",
"keep_after_success": false,
"max_backups": 5
}
}"#;
let result: Result<UpgradeConfig, _> = serde_json::from_str(json);
assert!(result.is_ok());
}
#[test]
fn test_merge() {
let mut base = UpgradeConfig::default();
let override_config = UpgradeConfig {
registry: RegistryConfig {
default_registry: "https://custom.registry.com".to_string(),
..Default::default()
},
auto_changeset: false,
changeset_bump: "minor".to_string(),
backup: BackupConfig { max_backups: 10, ..Default::default() },
};
assert!(base.merge_with(override_config).is_ok());
assert_eq!(base.registry.default_registry, "https://custom.registry.com");
assert!(!base.auto_changeset);
assert_eq!(base.changeset_bump, "minor");
assert_eq!(base.backup.max_backups, 10);
}
}
mod audit_config {
use super::*;
use crate::config::HealthScoreWeightsConfig;
#[test]
fn test_default_audit_config_is_valid() {
let config = AuditConfig::default();
assert!(config.validate().is_ok());
}
#[test]
fn test_default_values() {
let config = AuditConfig::default();
assert!(config.enabled);
assert_eq!(config.min_severity, "warning");
assert!(config.sections.upgrades);
assert!(config.sections.dependencies);
assert!(config.upgrades.include_major);
assert!(config.dependencies.check_circular);
assert!(config.breaking_changes.check_conventional_commits);
assert!(config.version_consistency.warn_on_inconsistency);
}
#[test]
fn test_invalid_severity() {
let config = AuditConfig { min_severity: "invalid".to_string(), ..Default::default() };
assert!(config.validate().is_err());
}
#[test]
fn test_valid_severities() {
for severity in &["critical", "warning", "info"] {
let config = AuditConfig { min_severity: severity.to_string(), ..Default::default() };
assert!(config.validate().is_ok(), "Severity '{}' should be valid", severity);
}
}
#[test]
fn test_serialization() {
let config = AuditConfig::default();
let serialized = serde_json::to_string(&config);
assert!(serialized.is_ok());
}
#[test]
fn test_deserialization() {
let json = r#"{
"enabled": true,
"min_severity": "warning",
"sections": {
"upgrades": true,
"dependencies": true,
"breaking_changes": true,
"categorization": true,
"version_consistency": true
},
"upgrades": {
"include_patch": true,
"include_minor": true,
"include_major": true,
"deprecated_as_critical": true
},
"dependencies": {
"check_circular": true,
"check_missing": false,
"check_unused": false,
"check_version_conflicts": true
},
"breaking_changes": {
"check_conventional_commits": true,
"check_changelog": true
},
"version_consistency": {
"fail_on_inconsistency": false,
"warn_on_inconsistency": true
},
"health_score_weights": {
"critical_weight": 15.0,
"warning_weight": 5.0,
"info_weight": 1.0,
"security_multiplier": 1.5,
"breaking_changes_multiplier": 1.3,
"dependencies_multiplier": 1.2,
"version_consistency_multiplier": 1.0,
"upgrades_multiplier": 0.8,
"other_multiplier": 1.0
}
}"#;
let result: Result<AuditConfig, _> = serde_json::from_str(json);
assert!(result.is_ok());
assert_eq!(result.unwrap(), AuditConfig::default());
}
#[test]
fn test_merge() {
let mut base = AuditConfig::default();
let override_config = AuditConfig {
enabled: false,
min_severity: "critical".to_string(),
sections: AuditSectionsConfig {
upgrades: false,
dependencies: false,
breaking_changes: false,
categorization: false,
version_consistency: false,
},
upgrades: UpgradeAuditConfig {
include_patch: false,
include_minor: false,
include_major: false,
deprecated_as_critical: false,
},
dependencies: DependencyAuditConfig {
check_circular: false,
check_missing: true,
check_unused: true,
check_version_conflicts: false,
},
breaking_changes: BreakingChangesAuditConfig {
check_conventional_commits: false,
check_changelog: false,
},
version_consistency: VersionConsistencyAuditConfig {
fail_on_inconsistency: true,
warn_on_inconsistency: false,
},
health_score_weights: HealthScoreWeightsConfig::default(),
};
assert!(base.merge_with(override_config.clone()).is_ok());
assert!(!base.enabled);
assert_eq!(base.min_severity, "critical");
assert!(!base.sections.upgrades);
assert!(!base.upgrades.include_major);
assert!(base.dependencies.check_missing);
assert!(!base.breaking_changes.check_conventional_commits);
assert!(base.version_consistency.fail_on_inconsistency);
}
#[test]
fn test_all_sections_disabled() {
let config = AuditConfig {
sections: AuditSectionsConfig {
upgrades: false,
dependencies: false,
breaking_changes: false,
categorization: false,
version_consistency: false,
},
..Default::default()
};
assert!(config.validate().is_ok());
}
#[test]
fn test_all_checks_disabled() {
let config = AuditConfig {
dependencies: DependencyAuditConfig {
check_circular: false,
check_missing: false,
check_unused: false,
check_version_conflicts: false,
},
..Default::default()
};
assert!(config.validate().is_ok());
}
#[test]
fn test_health_score_weights_default() {
let config = HealthScoreWeightsConfig::default();
assert_eq!(config.critical_weight, 15.0);
assert_eq!(config.warning_weight, 5.0);
assert_eq!(config.info_weight, 1.0);
assert_eq!(config.security_multiplier, 1.5);
assert_eq!(config.breaking_changes_multiplier, 1.3);
assert_eq!(config.dependencies_multiplier, 1.2);
assert_eq!(config.version_consistency_multiplier, 1.0);
assert_eq!(config.upgrades_multiplier, 0.8);
assert_eq!(config.other_multiplier, 1.0);
}
#[test]
fn test_health_score_weights_validation_positive() {
let config = HealthScoreWeightsConfig::default();
assert!(config.validate().is_ok());
}
#[test]
fn test_health_score_weights_validation_negative_critical() {
use sublime_standard_tools::config::Configurable;
let config = HealthScoreWeightsConfig { critical_weight: -1.0, ..Default::default() };
assert!(config.validate().is_err());
}
#[test]
fn test_health_score_weights_validation_negative_multiplier() {
use sublime_standard_tools::config::Configurable;
let config = HealthScoreWeightsConfig { security_multiplier: -0.5, ..Default::default() };
assert!(config.validate().is_err());
}
#[test]
fn test_health_score_weights_serialization() {
let config = HealthScoreWeightsConfig::default();
let serialized = serde_json::to_string(&config);
assert!(serialized.is_ok());
let json = serialized.unwrap();
assert!(json.contains("\"critical_weight\""));
assert!(json.contains("\"security_multiplier\""));
}
#[test]
fn test_health_score_weights_deserialization() {
let json = r#"{
"critical_weight": 20.0,
"warning_weight": 8.0,
"info_weight": 2.0,
"security_multiplier": 2.0,
"breaking_changes_multiplier": 1.5,
"dependencies_multiplier": 1.3,
"version_consistency_multiplier": 1.2,
"upgrades_multiplier": 1.0,
"other_multiplier": 1.1
}"#;
let result: Result<HealthScoreWeightsConfig, _> = serde_json::from_str(json);
assert!(result.is_ok());
let config = result.unwrap();
assert_eq!(config.critical_weight, 20.0);
assert_eq!(config.security_multiplier, 2.0);
}
#[test]
fn test_health_score_weights_merge() {
use sublime_standard_tools::config::Configurable;
let mut base = HealthScoreWeightsConfig::default();
let override_config = HealthScoreWeightsConfig {
critical_weight: 20.0,
warning_weight: 8.0,
info_weight: 2.0,
security_multiplier: 2.0,
breaking_changes_multiplier: 1.5,
dependencies_multiplier: 1.3,
version_consistency_multiplier: 1.2,
upgrades_multiplier: 1.0,
other_multiplier: 1.1,
};
assert!(base.merge_with(override_config.clone()).is_ok());
assert_eq!(base.critical_weight, 20.0);
assert_eq!(base.warning_weight, 8.0);
assert_eq!(base.security_multiplier, 2.0);
}
#[test]
fn test_health_score_weights_zero_values() {
use sublime_standard_tools::config::Configurable;
let config = HealthScoreWeightsConfig {
critical_weight: 0.0,
warning_weight: 0.0,
info_weight: 0.0,
security_multiplier: 0.0,
breaking_changes_multiplier: 0.0,
dependencies_multiplier: 0.0,
version_consistency_multiplier: 0.0,
upgrades_multiplier: 0.0,
other_multiplier: 0.0,
};
assert!(config.validate().is_ok());
}
}
#[cfg(test)]
mod parsing_tests {
use crate::config::{ConfigFormat, PackageToolsConfig};
#[test]
fn test_default_config_is_valid() {
let config = PackageToolsConfig::default();
assert_eq!(config.changeset.path, ".changesets");
}
#[test]
fn test_from_str_toml() {
let toml_content = r#"
[changeset]
path = ".custom-changesets"
history_path = ".custom-history"
available_environments = ["dev", "prod"]
default_environments = ["prod"]
[version]
strategy = "unified"
default_bump = "minor"
"#;
let result = PackageToolsConfig::from_str(toml_content, ConfigFormat::Toml);
assert!(result.is_ok());
let config = result.unwrap();
assert_eq!(config.changeset.path, ".custom-changesets");
assert_eq!(config.changeset.history_path, ".custom-history");
assert_eq!(config.version.default_bump, "minor");
}
#[test]
fn test_from_str_json() {
let json_content = r#"{
"changeset": {
"path": ".json-changesets",
"history_path": ".json-history",
"available_environments": ["staging"],
"default_environments": ["staging"]
}
}"#;
let result = PackageToolsConfig::from_str(json_content, ConfigFormat::Json);
assert!(result.is_ok());
let config = result.unwrap();
assert_eq!(config.changeset.path, ".json-changesets");
}
#[test]
fn test_from_str_yaml() {
let yaml_content = r#"
changeset:
path: ".yaml-changesets"
history_path: ".yaml-history"
available_environments:
- production
default_environments:
- production
"#;
let result = PackageToolsConfig::from_str(yaml_content, ConfigFormat::Yaml);
assert!(result.is_ok());
let config = result.unwrap();
assert_eq!(config.changeset.path, ".yaml-changesets");
}
#[test]
fn test_from_str_invalid_toml() {
let invalid_toml = "this is not valid toml [[[";
let result = PackageToolsConfig::from_str(invalid_toml, ConfigFormat::Toml);
assert!(result.is_err());
}
#[test]
fn test_from_str_invalid_json() {
let invalid_json = "{ invalid json }";
let result = PackageToolsConfig::from_str(invalid_json, ConfigFormat::Json);
assert!(result.is_err());
}
}
#[cfg(test)]
mod validation_tests {
use crate::config::{
PackageToolsConfig, validate_config, validate_path_format, validate_url_format,
};
#[test]
fn test_validate_default_config() {
let config = PackageToolsConfig::default();
assert!(validate_config(&config).is_ok());
}
#[test]
fn test_validate_changeset_parent_directory() {
let mut config = PackageToolsConfig::default();
config.changeset.path = "../changesets".to_string();
let result = validate_config(&config);
assert!(result.is_err());
}
#[test]
fn test_validate_same_changeset_paths() {
let mut config = PackageToolsConfig::default();
config.changeset.path = ".changesets".to_string();
config.changeset.history_path = ".changesets".to_string();
let result = validate_config(&config);
assert!(result.is_err());
}
#[test]
fn test_validate_duplicate_environments() {
let mut config = PackageToolsConfig::default();
config.changeset.available_environments = vec!["prod".to_string(), "prod".to_string()];
config.changeset.default_environments = vec!["prod".to_string()];
let result = validate_config(&config);
assert!(result.is_err());
}
#[test]
fn test_validate_environment_with_whitespace() {
let mut config = PackageToolsConfig::default();
config.changeset.available_environments = vec!["prod test".to_string()];
config.changeset.default_environments = vec!["prod test".to_string()];
let result = validate_config(&config);
assert!(result.is_err());
}
#[test]
fn test_validate_invalid_registry_url() {
let mut config = PackageToolsConfig::default();
config.upgrade.registry.default_registry = "not-a-url".to_string();
let result = validate_config(&config);
assert!(result.is_err());
}
#[test]
fn test_validate_high_max_depth() {
let mut config = PackageToolsConfig::default();
config.dependency.max_depth = 150;
let result = validate_config(&config);
assert!(result.is_err());
}
#[test]
fn test_validate_no_propagation() {
let mut config = PackageToolsConfig::default();
config.dependency.propagate_dependencies = false;
config.dependency.propagate_dev_dependencies = false;
config.dependency.propagate_peer_dependencies = false;
let result = validate_config(&config);
assert!(result.is_err());
}
#[test]
fn test_validate_path_format_empty() {
let result = validate_path_format("", "test_field");
assert!(result.is_err());
}
#[test]
fn test_validate_path_format_valid() {
let result = validate_path_format(".changesets", "test_field");
assert!(result.is_ok());
}
#[test]
fn test_validate_url_format_valid_https() {
let result = validate_url_format("https://registry.npmjs.org", "test_field");
assert!(result.is_ok());
}
#[test]
fn test_validate_url_format_valid_http() {
let result = validate_url_format("http://localhost:4873", "test_field");
assert!(result.is_ok());
}
#[test]
fn test_validate_url_format_invalid() {
let result = validate_url_format("not-a-url", "test_field");
assert!(result.is_err());
}
#[test]
fn test_validate_url_format_empty() {
let result = validate_url_format("", "test_field");
assert!(result.is_err());
}
#[test]
fn test_validate_snapshot_format_missing_placeholder() {
let mut config = PackageToolsConfig::default();
config.version.snapshot_format = "invalid-format".to_string();
let result = validate_config(&config);
assert!(result.is_err());
}
#[test]
fn test_validate_zero_timeout() {
let mut config = PackageToolsConfig::default();
config.upgrade.registry.timeout_secs = 0;
let result = validate_config(&config);
assert!(result.is_err());
}
#[test]
fn test_validate_high_timeout() {
let mut config = PackageToolsConfig::default();
config.upgrade.registry.timeout_secs = 500;
let result = validate_config(&config);
assert!(result.is_err());
}
#[test]
fn test_validate_too_many_retries() {
let mut config = PackageToolsConfig::default();
config.upgrade.registry.retry_attempts = 20;
let result = validate_config(&config);
assert!(result.is_err());
}
#[test]
fn test_validate_execute_zero_max_parallel() {
let mut config = PackageToolsConfig::default();
config.execute.max_parallel = 0;
let result = validate_config(&config);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("max_parallel"));
}
#[test]
fn test_validate_execute_valid_config() {
let mut config = PackageToolsConfig::default();
config.execute.timeout_secs = 600;
config.execute.per_package_timeout_secs = 120;
config.execute.max_parallel = 4;
let result = validate_config(&config);
assert!(result.is_ok());
}
#[test]
fn test_validate_execute_zero_timeouts_valid() {
let mut config = PackageToolsConfig::default();
config.execute.timeout_secs = 0;
config.execute.per_package_timeout_secs = 0;
config.execute.max_parallel = 8;
let result = validate_config(&config);
assert!(result.is_ok());
}
}
mod workspace_config {
use crate::config::WorkspaceConfig;
#[test]
fn test_workspace_config_default() {
let config = WorkspaceConfig::default();
assert!(config.patterns.is_empty());
assert!(config.is_empty());
assert_eq!(config.len(), 0);
}
#[test]
fn test_workspace_config_new() {
let patterns = vec!["packages/*".to_string(), "apps/*".to_string()];
let config = WorkspaceConfig::new(patterns.clone());
assert_eq!(config.patterns, patterns);
assert_eq!(config.len(), 2);
assert!(!config.is_empty());
}
#[test]
fn test_workspace_config_empty() {
let config = WorkspaceConfig::empty();
assert!(config.is_empty());
assert_eq!(config.len(), 0);
}
#[test]
fn test_workspace_config_validate_valid() {
let config = WorkspaceConfig::new(vec!["packages/*".to_string()]);
assert!(config.validate().is_ok());
let empty_config = WorkspaceConfig::empty();
assert!(empty_config.validate().is_ok());
let multi_pattern = WorkspaceConfig::new(vec![
"packages/*".to_string(),
"apps/*".to_string(),
"libs/**/*.js".to_string(),
]);
assert!(multi_pattern.validate().is_ok());
}
#[test]
fn test_workspace_config_validate_empty_pattern() {
let config = WorkspaceConfig::new(vec!["".to_string()]);
let result = config.validate();
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("cannot be empty"));
}
#[test]
fn test_workspace_config_validate_path_traversal() {
let config = WorkspaceConfig::new(vec!["../packages/*".to_string()]);
let result = config.validate();
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("path traversal"));
let config2 = WorkspaceConfig::new(vec!["packages/../apps/*".to_string()]);
let result2 = config2.validate();
assert!(result2.is_err());
}
#[test]
fn test_workspace_config_validate_absolute_unix_path() {
let config = WorkspaceConfig::new(vec!["/usr/local/packages/*".to_string()]);
let result = config.validate();
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("must be relative"));
}
#[test]
fn test_workspace_config_validate_absolute_windows_path() {
let config = WorkspaceConfig::new(vec!["C:\\packages\\*".to_string()]);
let result = config.validate();
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("must be relative"));
let config2 = WorkspaceConfig::new(vec!["D:/packages/*".to_string()]);
let result2 = config2.validate();
assert!(result2.is_err());
}
#[test]
fn test_workspace_config_merge() {
let mut config = WorkspaceConfig::new(vec!["packages/*".to_string()]);
let other = WorkspaceConfig::new(vec!["apps/*".to_string(), "libs/*".to_string()]);
let result = config.merge_with(other);
assert!(result.is_ok());
assert_eq!(config.patterns.len(), 2);
assert_eq!(config.patterns[0], "apps/*");
assert_eq!(config.patterns[1], "libs/*");
}
#[test]
fn test_workspace_config_merge_empty() {
let mut config = WorkspaceConfig::new(vec!["packages/*".to_string()]);
let other = WorkspaceConfig::empty();
let result = config.merge_with(other);
assert!(result.is_ok());
assert_eq!(config.patterns.len(), 1);
assert_eq!(config.patterns[0], "packages/*");
}
#[test]
fn test_workspace_config_equality() {
let config1 = WorkspaceConfig::new(vec!["packages/*".to_string(), "apps/*".to_string()]);
let config2 = WorkspaceConfig::new(vec!["packages/*".to_string(), "apps/*".to_string()]);
let config3 = WorkspaceConfig::new(vec!["libs/*".to_string()]);
assert_eq!(config1, config2);
assert_ne!(config1, config3);
}
#[test]
fn test_workspace_config_clone() {
let config1 = WorkspaceConfig::new(vec!["packages/*".to_string()]);
let config2 = config1.clone();
assert_eq!(config1, config2);
assert_eq!(config1.patterns, config2.patterns);
}
}
mod execute_config {
use super::*;
#[test]
fn test_default_config_is_valid() {
let config = ExecuteConfig::default();
assert!(config.validate().is_ok());
}
#[test]
fn test_default_values() {
let config = ExecuteConfig::default();
assert_eq!(config.timeout_secs, 300);
assert_eq!(config.per_package_timeout_secs, 60);
assert_eq!(config.max_parallel, 8);
}
#[test]
fn test_zero_timeout_is_valid() {
let config = ExecuteConfig { timeout_secs: 0, ..Default::default() };
assert!(config.validate().is_ok());
}
#[test]
fn test_zero_per_package_timeout_is_valid() {
let config = ExecuteConfig { per_package_timeout_secs: 0, ..Default::default() };
assert!(config.validate().is_ok());
}
#[test]
fn test_zero_max_parallel_is_invalid() {
let config = ExecuteConfig { max_parallel: 0, ..Default::default() };
let result = config.validate();
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("max_parallel"));
}
#[test]
fn test_max_parallel_one_is_valid() {
let config = ExecuteConfig { max_parallel: 1, ..Default::default() };
assert!(config.validate().is_ok());
}
#[test]
fn test_high_max_parallel_is_valid() {
let config = ExecuteConfig { max_parallel: 100, ..Default::default() };
assert!(config.validate().is_ok());
}
#[test]
fn test_serialization() {
let config = ExecuteConfig::default();
let serialized = serde_json::to_string(&config);
assert!(serialized.is_ok());
let json = serialized.unwrap();
assert!(json.contains("timeout_secs"));
assert!(json.contains("per_package_timeout_secs"));
assert!(json.contains("max_parallel"));
}
#[test]
fn test_deserialization() {
let json = r#"{
"timeout_secs": 600,
"per_package_timeout_secs": 120,
"max_parallel": 4
}"#;
let result: Result<ExecuteConfig, _> = serde_json::from_str(json);
assert!(result.is_ok());
let config = result.unwrap();
assert_eq!(config.timeout_secs, 600);
assert_eq!(config.per_package_timeout_secs, 120);
assert_eq!(config.max_parallel, 4);
}
#[test]
fn test_deserialization_with_defaults() {
let json = r#"{
"max_parallel": 16
}"#;
let result: Result<ExecuteConfig, _> = serde_json::from_str(json);
assert!(result.is_ok());
let config = result.unwrap();
assert_eq!(config.timeout_secs, 300); assert_eq!(config.per_package_timeout_secs, 60); assert_eq!(config.max_parallel, 16);
}
#[test]
fn test_deserialization_empty_object() {
let json = "{}";
let result: Result<ExecuteConfig, _> = serde_json::from_str(json);
assert!(result.is_ok());
let config = result.unwrap();
assert_eq!(config.timeout_secs, 300);
assert_eq!(config.per_package_timeout_secs, 60);
assert_eq!(config.max_parallel, 8);
}
#[test]
fn test_merge() {
let mut base = ExecuteConfig::default();
let override_config =
ExecuteConfig { timeout_secs: 600, per_package_timeout_secs: 120, max_parallel: 4 };
assert!(base.merge_with(override_config).is_ok());
assert_eq!(base.timeout_secs, 600);
assert_eq!(base.per_package_timeout_secs, 120);
assert_eq!(base.max_parallel, 4);
}
#[test]
fn test_merge_with_zero_timeout() {
let mut base = ExecuteConfig::default();
let override_config = ExecuteConfig { timeout_secs: 0, ..Default::default() };
assert!(base.merge_with(override_config).is_ok());
assert_eq!(base.timeout_secs, 0);
}
#[test]
fn test_equality() {
let config1 = ExecuteConfig::default();
let config2 = ExecuteConfig::default();
let config3 = ExecuteConfig { max_parallel: 4, ..Default::default() };
assert_eq!(config1, config2);
assert_ne!(config1, config3);
}
#[test]
fn test_clone() {
let config1 =
ExecuteConfig { timeout_secs: 500, per_package_timeout_secs: 100, max_parallel: 16 };
let config2 = config1.clone();
assert_eq!(config1, config2);
assert_eq!(config1.timeout_secs, config2.timeout_secs);
assert_eq!(config1.per_package_timeout_secs, config2.per_package_timeout_secs);
assert_eq!(config1.max_parallel, config2.max_parallel);
}
#[test]
fn test_debug_formatting() {
let config = ExecuteConfig::default();
let debug_str = format!("{:?}", config);
assert!(debug_str.contains("ExecuteConfig"));
assert!(debug_str.contains("timeout_secs"));
assert!(debug_str.contains("per_package_timeout_secs"));
assert!(debug_str.contains("max_parallel"));
}
#[test]
fn test_custom_values() {
let config = ExecuteConfig {
timeout_secs: 3600, per_package_timeout_secs: 300, max_parallel: 32,
};
assert!(config.validate().is_ok());
assert_eq!(config.timeout_secs, 3600);
assert_eq!(config.per_package_timeout_secs, 300);
assert_eq!(config.max_parallel, 32);
}
}