1#![forbid(unsafe_code)]
124#![warn(missing_docs)]
125
126pub mod ast;
127pub mod builder;
128pub mod canonical;
129pub mod determinism;
130pub mod error;
131pub mod guarantees;
132pub mod generator;
133pub mod presets;
134pub mod streaming;
135pub mod diff;
136pub mod messages;
137pub mod linker;
138pub mod id_generator;
139pub mod preflight;
140pub mod schema;
141pub mod versions;
142pub mod optimized_strings;
143pub mod memory_optimization;
144pub mod parallel_processing;
145pub mod caching;
146pub mod security;
147pub mod api_security;
148pub mod namespace_minimizer;
149pub mod fidelity;
150pub mod verification;
151pub mod round_trip;
152
153pub use builder::{DDEXBuilder, BuildOptions, BuildRequest, BuildResult};
155pub use canonical::DB_C14N;
156pub use determinism::DeterminismConfig;
157pub use error::{BuildError, BuildWarning};
158pub use guarantees::{DeterminismGuarantee, DeterminismGuaranteeValidator, GuaranteeReport};
159pub use presets::PartnerPreset;
160pub use linker::{ReferenceLinker, LinkerConfig, EntityType, LinkerError};
161pub use id_generator::{StableHashGenerator, StableHashConfig, HashAlgorithm};
162pub use preflight::{PreflightValidator, ValidationConfig, ValidationResult, PreflightLevel};
163pub use diff::{DiffEngine, DiffConfig, VersionCompatibility};
164pub use diff::types::{ChangeSet, SemanticChange, DiffPath, ChangeType, ImpactLevel};
165pub use diff::formatter::DiffFormatter;
166pub use messages::{UpdateReleaseMessage, UpdateGenerator, UpdateAction, UpdateConfig, ValidationStatus};
167pub use schema::{SchemaGenerator, JsonSchema, SchemaConfig, SchemaDraft, SchemaCommand};
168pub use versions::{VersionManager, VersionConverter, ConverterResult as ConversionResult, ConversionOptions};
169pub use presets::DdexVersion;
170
171pub use security::{InputValidator, SecurityConfig, RateLimiter, SecureTempFile, OutputSanitizer};
173pub use api_security::{ApiSecurityManager, ApiSecurityConfig, FfiDataType, BatchStats};
174
175pub use fidelity::{FidelityConfig, PreservationLevel, FidelityStatistics};
177pub use verification::{BuildVerifier, VerificationStatistics};
178pub use round_trip::{RoundTripTester, FidelityAnalysis};
179
180use indexmap::IndexMap;
181use serde::{Deserialize, Serialize};
182use std::time::Duration;
183use std::collections::HashMap;
184
185pub const DB_C14N_VERSION: &str = "1.0";
187
188#[derive(Debug, Clone, Serialize, Deserialize)]
190pub struct FidelityOptions {
191 pub enable_perfect_fidelity: bool,
193 pub preserve_comments: bool,
195 pub preserve_processing_instructions: bool,
197 pub preserve_extensions: bool,
199 pub preserve_attribute_order: bool,
201 pub preserve_namespace_prefixes: bool,
203 pub canonicalization: CanonicalizationAlgorithm,
205 pub custom_canonicalization_rules: Option<CustomCanonicalizationRules>,
207 pub enable_deterministic_ordering: bool,
209 pub collect_statistics: bool,
211 pub enable_verification: bool,
213 pub verification_config: VerificationConfig,
215}
216
217impl Default for FidelityOptions {
218 fn default() -> Self {
219 Self {
220 enable_perfect_fidelity: false,
221 preserve_comments: false,
222 preserve_processing_instructions: false,
223 preserve_extensions: true,
224 preserve_attribute_order: false,
225 preserve_namespace_prefixes: false,
226 canonicalization: CanonicalizationAlgorithm::DbC14N,
227 custom_canonicalization_rules: None,
228 enable_deterministic_ordering: true,
229 collect_statistics: false,
230 enable_verification: false,
231 verification_config: VerificationConfig::default(),
232 }
233 }
234}
235
236#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
238pub enum CanonicalizationAlgorithm {
239 None,
241 C14N,
243 C14N11,
245 DbC14N,
247 Custom(CustomCanonicalizationRules),
249}
250
251#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
253pub struct CustomCanonicalizationRules {
254 pub preserve_whitespace: bool,
256 pub sort_attributes: bool,
258 pub normalize_line_endings: bool,
260 pub minimize_namespaces: bool,
262 pub attribute_ordering: Vec<String>,
264 pub element_ordering: HashMap<String, Vec<String>>,
266}
267
268impl Default for CustomCanonicalizationRules {
269 fn default() -> Self {
270 Self {
271 preserve_whitespace: false,
272 sort_attributes: true,
273 normalize_line_endings: true,
274 minimize_namespaces: true,
275 attribute_ordering: Vec::new(),
276 element_ordering: HashMap::new(),
277 }
278 }
279}
280
281#[derive(Debug, Clone, Serialize, Deserialize)]
283pub struct VerificationConfig {
284 pub enable_round_trip_verification: bool,
286 pub enable_canonicalization_verification: bool,
288 pub enable_schema_validation: bool,
290 pub enable_determinism_verification: bool,
292 pub determinism_test_iterations: usize,
294 pub verification_timeout: Duration,
296}
297
298impl Default for VerificationConfig {
299 fn default() -> Self {
300 Self {
301 enable_round_trip_verification: true,
302 enable_canonicalization_verification: true,
303 enable_schema_validation: false,
304 enable_determinism_verification: true,
305 determinism_test_iterations: 3,
306 verification_timeout: Duration::from_secs(30),
307 }
308 }
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize)]
313pub struct VerificationResult {
314 pub success: bool,
316 pub round_trip_success: bool,
318 pub canonicalization_success: bool,
320 pub schema_validation_success: bool,
322 pub determinism_success: bool,
324 pub issues: Vec<VerificationIssue>,
326 pub verification_time: Duration,
328}
329
330#[derive(Debug, Clone, Serialize, Deserialize)]
332pub struct VerificationIssue {
333 pub severity: VerificationSeverity,
335 pub category: String,
337 pub message: String,
339 pub path: Option<String>,
341 pub suggestion: Option<String>,
343}
344
345#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
347pub enum VerificationSeverity {
348 Error,
350 Warning,
352 Info,
354}
355
356#[derive(Debug, Clone, Serialize, Deserialize)]
358pub struct BuildStatistics {
359 pub build_time: Duration,
361 pub canonicalization_time: Duration,
363 pub validation_time: Duration,
365 pub verification_time: Duration,
367 pub peak_memory_bytes: usize,
369 pub element_count: usize,
371 pub attribute_count: usize,
373 pub input_size_bytes: usize,
375 pub output_size_bytes: usize,
377 pub namespace_count: usize,
379 pub comment_count: usize,
381 pub processing_instruction_count: usize,
383}
384
385impl Default for BuildStatistics {
386 fn default() -> Self {
387 Self {
388 build_time: Duration::ZERO,
389 canonicalization_time: Duration::ZERO,
390 validation_time: Duration::ZERO,
391 verification_time: Duration::ZERO,
392 peak_memory_bytes: 0,
393 element_count: 0,
394 attribute_count: 0,
395 input_size_bytes: 0,
396 output_size_bytes: 0,
397 namespace_count: 0,
398 comment_count: 0,
399 processing_instruction_count: 0,
400 }
401 }
402}
403
404#[derive(Debug, Clone)]
495pub struct Builder {
496 config: DeterminismConfig,
497 presets: IndexMap<String, PartnerPreset>,
498 locked_preset: Option<String>,
499 version_manager: versions::VersionManager,
500 target_version: Option<DdexVersion>,
501 fidelity_options: FidelityOptions,
502 verification_config: VerificationConfig,
503}
504
505impl Default for Builder {
506 fn default() -> Self {
507 Self::new()
508 }
509}
510
511impl Builder {
512 pub fn new() -> Self {
535 Self {
536 config: DeterminismConfig::default(),
537 presets: Self::load_default_presets(),
538 locked_preset: None,
539 version_manager: versions::VersionManager::new(),
540 target_version: None,
541 fidelity_options: FidelityOptions::default(),
542 verification_config: VerificationConfig::default(),
543 }
544 }
545
546 pub fn with_config(config: DeterminismConfig) -> Self {
548 Self {
549 config,
550 presets: Self::load_default_presets(),
551 locked_preset: None,
552 version_manager: versions::VersionManager::new(),
553 target_version: None,
554 fidelity_options: FidelityOptions::default(),
555 verification_config: VerificationConfig::default(),
556 }
557 }
558
559 pub fn with_perfect_fidelity() -> Self {
561 let mut fidelity_options = FidelityOptions::default();
562 fidelity_options.enable_perfect_fidelity = true;
563 fidelity_options.preserve_comments = true;
564 fidelity_options.preserve_processing_instructions = true;
565 fidelity_options.preserve_extensions = true;
566 fidelity_options.preserve_attribute_order = true;
567 fidelity_options.preserve_namespace_prefixes = true;
568 fidelity_options.enable_verification = true;
569
570 Self {
571 config: DeterminismConfig::default(),
572 presets: Self::load_default_presets(),
573 locked_preset: None,
574 version_manager: versions::VersionManager::new(),
575 target_version: None,
576 fidelity_options,
577 verification_config: VerificationConfig::default(),
578 }
579 }
580
581 pub fn with_fidelity_options(fidelity_options: FidelityOptions) -> Self {
583 Self {
584 config: DeterminismConfig::default(),
585 presets: Self::load_default_presets(),
586 locked_preset: None,
587 version_manager: versions::VersionManager::new(),
588 target_version: None,
589 fidelity_options,
590 verification_config: VerificationConfig::default(),
591 }
592 }
593
594 pub fn for_round_trip() -> Self {
596 let mut fidelity_options = FidelityOptions::default();
597 fidelity_options.enable_perfect_fidelity = true;
598 fidelity_options.preserve_comments = true;
599 fidelity_options.preserve_processing_instructions = true;
600 fidelity_options.preserve_extensions = true;
601 fidelity_options.preserve_attribute_order = true;
602 fidelity_options.preserve_namespace_prefixes = true;
603 fidelity_options.canonicalization = CanonicalizationAlgorithm::DbC14N;
604 fidelity_options.enable_verification = true;
605 fidelity_options.collect_statistics = true;
606
607 let mut verification_config = VerificationConfig::default();
608 verification_config.enable_round_trip_verification = true;
609 verification_config.enable_canonicalization_verification = true;
610 verification_config.enable_determinism_verification = true;
611
612 Self {
613 config: DeterminismConfig::default(),
614 presets: Self::load_default_presets(),
615 locked_preset: None,
616 version_manager: versions::VersionManager::new(),
617 target_version: None,
618 fidelity_options,
619 verification_config,
620 }
621 }
622
623 pub fn apply_preset(&mut self, preset_name: &str, lock: bool) -> Result<(), error::BuildError> {
669 let preset = self.presets.get(preset_name)
670 .ok_or_else(|| error::BuildError::InvalidFormat {
671 field: "preset".to_string(),
672 message: format!("Unknown preset: {}", preset_name),
673 })?
674 .clone();
675
676 self.config = preset.determinism;
678
679 if lock {
681 self.locked_preset = Some(preset_name.to_string());
682 }
683
684 Ok(())
685 }
686
687 pub fn preset(&mut self, preset_name: &str) -> Result<&mut Self, error::BuildError> {
689 self.apply_preset(preset_name, false)?;
690 Ok(self)
691 }
692
693 pub fn available_presets(&self) -> Vec<String> {
695 self.presets.keys().cloned().collect()
696 }
697
698 pub fn get_preset(&self, preset_name: &str) -> Option<&PartnerPreset> {
700 self.presets.get(preset_name)
701 }
702
703 pub fn is_preset_locked(&self) -> bool {
705 self.locked_preset.is_some()
706 }
707
708 pub fn config(&self) -> &DeterminismConfig {
710 &self.config
711 }
712
713 pub fn fidelity_options(&self) -> &FidelityOptions {
715 &self.fidelity_options
716 }
717
718 pub fn set_fidelity_options(&mut self, options: FidelityOptions) -> &mut Self {
720 self.fidelity_options = options;
721 self
722 }
723
724 pub fn enable_perfect_fidelity(&mut self) -> &mut Self {
726 self.fidelity_options.enable_perfect_fidelity = true;
727 self.fidelity_options.preserve_comments = true;
728 self.fidelity_options.preserve_processing_instructions = true;
729 self.fidelity_options.preserve_extensions = true;
730 self.fidelity_options.preserve_attribute_order = true;
731 self.fidelity_options.preserve_namespace_prefixes = true;
732 self.fidelity_options.enable_verification = true;
733 self
734 }
735
736 pub fn disable_perfect_fidelity(&mut self) -> &mut Self {
738 self.fidelity_options.enable_perfect_fidelity = false;
739 self.fidelity_options.preserve_comments = false;
740 self.fidelity_options.preserve_processing_instructions = false;
741 self.fidelity_options.preserve_attribute_order = false;
742 self.fidelity_options.preserve_namespace_prefixes = false;
743 self.fidelity_options.enable_verification = false;
744 self
745 }
746
747 pub fn with_canonicalization(&mut self, algorithm: CanonicalizationAlgorithm) -> &mut Self {
749 self.fidelity_options.canonicalization = algorithm;
750 self
751 }
752
753 pub fn with_db_c14n(&mut self) -> &mut Self {
755 self.fidelity_options.canonicalization = CanonicalizationAlgorithm::DbC14N;
756 self
757 }
758
759 pub fn with_c14n(&mut self) -> &mut Self {
761 self.fidelity_options.canonicalization = CanonicalizationAlgorithm::C14N;
762 self
763 }
764
765 pub fn with_c14n11(&mut self) -> &mut Self {
767 self.fidelity_options.canonicalization = CanonicalizationAlgorithm::C14N11;
768 self
769 }
770
771 pub fn with_custom_canonicalization(&mut self, rules: CustomCanonicalizationRules) -> &mut Self {
773 self.fidelity_options.canonicalization = CanonicalizationAlgorithm::Custom(rules.clone());
774 self.fidelity_options.custom_canonicalization_rules = Some(rules);
775 self
776 }
777
778 pub fn with_verification(&mut self, config: VerificationConfig) -> &mut Self {
780 self.fidelity_options.enable_verification = true;
781 self.verification_config = config;
782 self
783 }
784
785 pub fn with_statistics(&mut self) -> &mut Self {
787 self.fidelity_options.collect_statistics = true;
788 self
789 }
790
791 pub fn is_perfect_fidelity_enabled(&self) -> bool {
793 self.fidelity_options.enable_perfect_fidelity
794 }
795
796 pub fn canonicalization_algorithm(&self) -> &CanonicalizationAlgorithm {
798 &self.fidelity_options.canonicalization
799 }
800
801 pub fn with_version(&mut self, version: DdexVersion) -> &mut Self {
803 self.target_version = Some(version);
804 self
805 }
806
807 pub fn target_version(&self) -> Option<DdexVersion> {
809 self.target_version
810 }
811
812 pub fn detect_version(&self, xml_content: &str) -> Result<DdexVersion, error::BuildError> {
814 self.version_manager.detect_version(xml_content)
815 .map(|detection| detection.detected_version)
816 .map_err(|e| error::BuildError::InvalidFormat {
817 field: "version".to_string(),
818 message: format!("Version detection failed: {}", e),
819 })
820 }
821
822 pub fn convert_version(&self, xml_content: &str, from_version: DdexVersion, to_version: DdexVersion, options: Option<ConversionOptions>) -> Result<versions::ConverterResult, error::BuildError> {
824 let converter = versions::VersionConverter::new();
825 Ok(converter.convert(xml_content, from_version, to_version, options))
826 }
827
828 pub fn is_version_compatible(&self, from: DdexVersion, to: DdexVersion) -> bool {
830 self.version_manager.is_conversion_supported(from, to)
831 }
832
833 pub fn supported_versions(&self) -> Vec<DdexVersion> {
835 versions::utils::supported_versions()
836 }
837
838 fn load_default_presets() -> IndexMap<String, PartnerPreset> {
839 presets::all_presets()
840 }
841
842 pub fn build_with_fidelity(&self, request: &builder::BuildRequest) -> Result<FidelityBuildResult, error::BuildError> {
844 let start_time = std::time::Instant::now();
845 let mut statistics = BuildStatistics::default();
846
847 let build_options = builder::BuildOptions::default();
849
850 let ddex_builder = builder::DDEXBuilder::new();
852 let build_result = ddex_builder.build(request.clone(), build_options)?;
853
854 statistics.build_time = start_time.elapsed();
855 statistics.output_size_bytes = build_result.xml.len();
856
857 let verification_result = if self.fidelity_options.enable_verification {
859 let verification_config = verification::VerificationConfig {
861 enable_round_trip_verification: self.verification_config.enable_round_trip_verification,
862 enable_canonicalization_verification: self.verification_config.enable_canonicalization_verification,
863 enable_schema_validation: self.verification_config.enable_schema_validation,
864 enable_determinism_verification: self.verification_config.enable_determinism_verification,
865 determinism_test_iterations: self.verification_config.determinism_test_iterations,
866 verification_timeout: self.verification_config.verification_timeout,
867 };
868 let verifier = verification::BuildVerifier::new(verification_config);
869 let result = verifier.verify(&build_result.xml, &self.fidelity_options)?;
870
871 Some(VerificationResult {
873 success: result.success,
874 round_trip_success: result.round_trip_success,
875 canonicalization_success: result.canonicalization_success,
876 determinism_success: result.determinism_success,
877 schema_validation_success: result.schema_validation_success,
878 issues: result.issues.into_iter().map(|issue| VerificationIssue {
879 category: issue.category,
880 severity: match issue.severity {
881 verification::VerificationSeverity::Error => VerificationSeverity::Error,
882 verification::VerificationSeverity::Warning => VerificationSeverity::Warning,
883 verification::VerificationSeverity::Info => VerificationSeverity::Info,
884 },
885 message: issue.message,
886 path: issue.path,
887 suggestion: issue.suggestion,
888 }).collect(),
889 verification_time: result.verification_time,
890 })
891 } else {
892 None
893 };
894
895 Ok(FidelityBuildResult {
896 xml: build_result.xml,
897 statistics: if self.fidelity_options.collect_statistics {
898 Some(statistics)
899 } else {
900 None
901 },
902 verification_result,
903 canonicalization_applied: self.fidelity_options.canonicalization != CanonicalizationAlgorithm::None,
904 db_c14n_version: if self.fidelity_options.canonicalization == CanonicalizationAlgorithm::DbC14N {
905 Some(DB_C14N_VERSION.to_string())
906 } else {
907 None
908 },
909 })
910 }
911
912 pub fn verify_build(&self, xml_output: &str) -> Result<VerificationResult, error::BuildError> {
914 let verification_config = verification::VerificationConfig {
916 enable_round_trip_verification: self.verification_config.enable_round_trip_verification,
917 enable_canonicalization_verification: self.verification_config.enable_canonicalization_verification,
918 enable_schema_validation: self.verification_config.enable_schema_validation,
919 enable_determinism_verification: self.verification_config.enable_determinism_verification,
920 determinism_test_iterations: self.verification_config.determinism_test_iterations,
921 verification_timeout: self.verification_config.verification_timeout,
922 };
923
924 let verifier = verification::BuildVerifier::new(verification_config);
925 let result = verifier.verify(xml_output, &self.fidelity_options)?;
926
927 Ok(VerificationResult {
929 success: result.success,
930 round_trip_success: result.round_trip_success,
931 canonicalization_success: result.canonicalization_success,
932 determinism_success: result.determinism_success,
933 schema_validation_success: result.schema_validation_success,
934 issues: result.issues.into_iter().map(|issue| VerificationIssue {
935 category: issue.category,
936 severity: match issue.severity {
937 verification::VerificationSeverity::Error => VerificationSeverity::Error,
938 verification::VerificationSeverity::Warning => VerificationSeverity::Warning,
939 verification::VerificationSeverity::Info => VerificationSeverity::Info,
940 },
941 message: issue.message,
942 path: issue.path,
943 suggestion: issue.suggestion,
944 }).collect(),
945 verification_time: result.verification_time,
946 })
947 }
948
949 pub fn test_round_trip_fidelity(&self, original_xml: &str) -> Result<RoundTripResult, error::BuildError> {
951 let round_trip = round_trip::RoundTripTester::new(self.fidelity_options.clone());
952 let result = round_trip.test_round_trip(original_xml)?;
953
954 Ok(RoundTripResult {
956 success: result.success,
957 original_xml: result.original_xml,
958 rebuilt_xml: result.rebuilt_xml,
959 byte_identical: result.byte_identical,
960 differences: result.differences,
961 test_time: result.test_time,
962 })
963 }
964
965 pub fn canonicalize(&self, xml_content: &str) -> Result<String, error::BuildError> {
967 match &self.fidelity_options.canonicalization {
968 CanonicalizationAlgorithm::None => Ok(xml_content.to_string()),
969 CanonicalizationAlgorithm::C14N => {
970 Ok(xml_content.to_string())
972 },
973 CanonicalizationAlgorithm::C14N11 => {
974 Ok(xml_content.to_string())
976 },
977 CanonicalizationAlgorithm::DbC14N => {
978 Ok(xml_content.to_string())
980 },
981 CanonicalizationAlgorithm::Custom(rules) => {
982 let _ = rules; Ok(xml_content.to_string())
985 },
986 }
987 }
988
989 pub fn db_c14n_config(&self) -> DbC14NConfig {
991 DbC14NConfig {
992 version: DB_C14N_VERSION.to_string(),
993 algorithm: self.fidelity_options.canonicalization.clone(),
994 deterministic_ordering: self.fidelity_options.enable_deterministic_ordering,
995 preserve_comments: self.fidelity_options.preserve_comments,
996 preserve_processing_instructions: self.fidelity_options.preserve_processing_instructions,
997 namespace_handling: if self.fidelity_options.preserve_namespace_prefixes {
998 NamespaceHandling::Preserve
999 } else {
1000 NamespaceHandling::Minimize
1001 },
1002 }
1003 }
1004
1005 pub(crate) fn build_internal(&self, request: &builder::BuildRequest) -> Result<builder::BuildResult, error::BuildError> {
1007 let ddex_builder = builder::DDEXBuilder::new();
1008 let build_options = builder::BuildOptions::default();
1009
1010 ddex_builder.build(request.clone(), build_options)
1011 }
1012}
1013
1014#[derive(Debug, Clone, Serialize, Deserialize)]
1016pub struct FidelityBuildResult {
1017 pub xml: String,
1019 pub statistics: Option<BuildStatistics>,
1021 pub verification_result: Option<VerificationResult>,
1023 pub canonicalization_applied: bool,
1025 pub db_c14n_version: Option<String>,
1027}
1028
1029#[derive(Debug, Clone, Serialize, Deserialize)]
1031pub struct RoundTripResult {
1032 pub success: bool,
1034 pub original_xml: String,
1036 pub rebuilt_xml: String,
1038 pub byte_identical: bool,
1040 pub differences: Vec<String>,
1042 pub test_time: Duration,
1044}
1045
1046#[derive(Debug, Clone, Serialize, Deserialize)]
1048pub struct DbC14NConfig {
1049 pub version: String,
1051 pub algorithm: CanonicalizationAlgorithm,
1053 pub deterministic_ordering: bool,
1055 pub preserve_comments: bool,
1057 pub preserve_processing_instructions: bool,
1059 pub namespace_handling: NamespaceHandling,
1061}
1062
1063#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1065pub enum NamespaceHandling {
1066 Preserve,
1068 Minimize,
1070 Normalize,
1072}
1073
1074pub fn version_info() -> String {
1076 format!(
1077 "DDEX Builder v{} âĒ DB-C14N/{} âĒ Perfect Fidelity Engine âĒ Rust {}",
1078 env!("CARGO_PKG_VERSION"),
1079 DB_C14N_VERSION,
1080 env!("CARGO_PKG_RUST_VERSION", "unknown")
1081 )
1082}
1083
1084pub fn fidelity_engine_info() -> String {
1086 format!(
1087 "Perfect Fidelity Engine v{} âĒ Round-trip: â âĒ DB-C14N/{} âĒ Extensions: â",
1088 env!("CARGO_PKG_VERSION"),
1089 DB_C14N_VERSION
1090 )
1091}
1092
1093#[cfg(test)]
1094mod tests {
1095 use super::*;
1096
1097 #[test]
1098 fn test_builder_creation() {
1099 let builder = Builder::new();
1100 assert!(!builder.is_preset_locked());
1101 }
1102
1103 #[test]
1104 fn test_preset_application() {
1105 let mut builder = Builder::new();
1106 assert!(builder.apply_preset("audio_album", false).is_ok());
1107 assert!(!builder.is_preset_locked());
1108
1109 assert!(builder.apply_preset("audio_album", true).is_ok());
1110 assert!(builder.is_preset_locked());
1111 }
1112
1113 #[test]
1114 fn test_unknown_preset() {
1115 let mut builder = Builder::new();
1116 assert!(builder.apply_preset("unknown_preset", false).is_err());
1117 }
1118
1119 #[test]
1120 fn test_version_info() {
1121 let info = version_info();
1122 assert!(info.contains("DDEX Builder"));
1123 assert!(info.contains("DB-C14N/1.0"));
1124 }
1125}