1use super::EMPTY_KEYWORDS;
2use crate::data_contract::accessors::v0::DataContractV0Getters;
3use crate::data_contract::associated_token::token_configuration::TokenConfiguration;
4use crate::data_contract::config::moderation::document_schema_grants_moderator_abilities;
5use crate::data_contract::config::DataContractConfig;
6use crate::data_contract::group::Group;
7use crate::data_contract::serialized_version::v0::DataContractInSerializationFormatV0;
8use crate::data_contract::serialized_version::v1::DataContractInSerializationFormatV1;
9use crate::data_contract::serialized_version::v2::DataContractInSerializationFormatV2;
10use crate::data_contract::v0::DataContractV0;
11use crate::data_contract::v1::DataContractV1;
12use crate::data_contract::{
13 DataContract, DefinitionName, DocumentName, GroupContractPosition, TokenContractPosition,
14 EMPTY_GROUPS, EMPTY_TOKENS,
15};
16#[cfg(feature = "json-conversion")]
17use crate::serialization::JsonConvertible;
18#[cfg(feature = "value-conversion")]
19use crate::serialization::ValueConvertible;
20use crate::validation::operations::ProtocolValidationOperation;
21use crate::version::PlatformVersion;
22use crate::ProtocolError;
23use bincode::{Decode, DecodeUntrusted, Encode};
24use derive_more::From;
25use platform_value::{Identifier, Value};
26use platform_version::{IntoPlatformVersioned, TryFromPlatformVersioned};
27use platform_versioning::PlatformVersioned;
28#[cfg(feature = "serde-conversion")]
29use serde::{Deserialize, Serialize};
30use std::collections::BTreeMap;
31use std::fmt;
32
33pub(in crate::data_contract) mod v0;
34pub(in crate::data_contract) mod v1;
35pub(in crate::data_contract) mod v2;
36
37pub mod property_names {
38 pub const ID: &str = "id";
39 pub const OWNER_ID: &str = "ownerId";
40 pub const VERSION: &str = "version";
41 pub const DEFINITIONS: &str = "$defs";
42}
43
44pub const CONTRACT_DESERIALIZATION_LIMIT: usize = 15000;
45
46#[derive(Debug, PartialEq, Eq, Clone, Copy)]
52pub enum DataContractMismatch {
53 Id,
55 Config,
57 Version,
59 OwnerId,
61 SchemaDefs,
63 DocumentSchemas,
65 Groups,
67 Tokens,
69 Keywords,
71 Description,
73 FormatVersionMismatch,
75 V0Mismatch,
77}
78
79impl fmt::Display for DataContractMismatch {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 let description = match self {
83 DataContractMismatch::Id => "ID fields differ",
84 DataContractMismatch::Config => "Config fields differ",
85 DataContractMismatch::Version => "Version fields differ",
86 DataContractMismatch::OwnerId => "Owner ID fields differ",
87 DataContractMismatch::SchemaDefs => "Schema definitions differ",
88 DataContractMismatch::DocumentSchemas => "Document schemas differ",
89 DataContractMismatch::Groups => "Groups differ",
90 DataContractMismatch::Tokens => "Tokens differ",
91 DataContractMismatch::Keywords => "Keywords differ",
92 DataContractMismatch::Description => "Description fields differ",
93 DataContractMismatch::FormatVersionMismatch => {
94 "Serialization format versions differ (e.g., V0 vs V1)"
95 }
96 DataContractMismatch::V0Mismatch => "V0 versions differ",
97 };
98 write!(f, "{}", description)
99 }
100}
101
102#[cfg_attr(
103 all(feature = "json-conversion", feature = "serde-conversion"),
104 derive(JsonConvertible)
105)]
106#[cfg_attr(
107 all(feature = "value-conversion", feature = "serde-conversion"),
108 derive(ValueConvertible)
109)]
110#[derive(Debug, Clone, Encode, Decode, PartialEq, PlatformVersioned, From, DecodeUntrusted)]
111#[cfg_attr(
112 feature = "serde-conversion",
113 derive(Serialize, Deserialize),
114 serde(tag = "$formatVersion")
115)]
116pub enum DataContractInSerializationFormat {
117 #[cfg_attr(feature = "serde-conversion", serde(rename = "0"))]
118 V0(DataContractInSerializationFormatV0),
119 #[cfg_attr(feature = "serde-conversion", serde(rename = "1"))]
120 V1(DataContractInSerializationFormatV1),
121 #[cfg_attr(feature = "serde-conversion", serde(rename = "2"))]
123 V2(DataContractInSerializationFormatV2),
124}
125
126#[derive(Decode, DecodeUntrusted)]
135#[cfg_attr(
136 feature = "serde-conversion",
137 derive(Deserialize),
138 serde(tag = "$formatVersion")
139)]
140pub(crate) enum TransitionContractFormat {
141 #[cfg_attr(feature = "serde-conversion", serde(rename = "0"))]
142 V0(DataContractInSerializationFormatV0),
143 #[cfg_attr(feature = "serde-conversion", serde(rename = "1"))]
144 V1(DataContractInSerializationFormatV1),
145}
146
147impl From<TransitionContractFormat> for DataContractInSerializationFormat {
148 fn from(format: TransitionContractFormat) -> Self {
149 match format {
150 TransitionContractFormat::V0(v0) => DataContractInSerializationFormat::V0(v0),
151 TransitionContractFormat::V1(v1) => DataContractInSerializationFormat::V1(v1),
152 }
153 }
154}
155
156impl DataContractInSerializationFormat {
157 pub fn into_schema_form(self) -> Self {
162 match self {
163 DataContractInSerializationFormat::V2(stored) => {
164 DataContractInSerializationFormat::V1(stored.into())
165 }
166 format => format,
167 }
168 }
169
170 pub fn id(&self) -> Identifier {
172 match self {
173 DataContractInSerializationFormat::V0(v0) => v0.id,
174 DataContractInSerializationFormat::V1(v1) => v1.id,
175 DataContractInSerializationFormat::V2(v2) => v2.base.id,
176 }
177 }
178
179 pub fn owner_id(&self) -> Identifier {
181 match self {
182 DataContractInSerializationFormat::V0(v0) => v0.owner_id,
183 DataContractInSerializationFormat::V1(v1) => v1.owner_id,
184 DataContractInSerializationFormat::V2(v2) => v2.base.owner_id,
185 }
186 }
187
188 pub fn document_schemas(&self) -> &BTreeMap<DocumentName, Value> {
189 match self {
190 DataContractInSerializationFormat::V0(v0) => &v0.document_schemas,
191 DataContractInSerializationFormat::V1(v1) => &v1.document_schemas,
192 DataContractInSerializationFormat::V2(v2) => &v2.base.document_schemas,
193 }
194 }
195
196 pub fn has_document_type_with_moderator_abilities(&self) -> bool {
202 self.document_schemas()
203 .values()
204 .any(document_schema_grants_moderator_abilities)
205 }
206
207 pub fn document_schemas_mut(
214 &mut self,
215 ) -> Result<&mut BTreeMap<DocumentName, Value>, ProtocolError> {
216 match self {
217 DataContractInSerializationFormat::V0(v0) => Ok(&mut v0.document_schemas),
218 DataContractInSerializationFormat::V1(v1) => Ok(&mut v1.document_schemas),
219 DataContractInSerializationFormat::V2(_) => Err(ProtocolError::NotSupported(
220 "the schemas of a contract in the stored format are not changed in place: its \
221 compiled document types would no longer be theirs"
222 .to_string(),
223 )),
224 }
225 }
226
227 pub fn schema_defs(&self) -> Option<&BTreeMap<DefinitionName, Value>> {
228 match self {
229 DataContractInSerializationFormat::V0(v0) => v0.schema_defs.as_ref(),
230 DataContractInSerializationFormat::V1(v1) => v1.schema_defs.as_ref(),
231 DataContractInSerializationFormat::V2(v2) => v2.base.schema_defs.as_ref(),
232 }
233 }
234
235 pub fn version(&self) -> u32 {
236 match self {
237 DataContractInSerializationFormat::V0(v0) => v0.version,
238 DataContractInSerializationFormat::V1(v1) => v1.version,
239 DataContractInSerializationFormat::V2(v2) => v2.base.version,
240 }
241 }
242
243 pub fn config(&self) -> &DataContractConfig {
245 match self {
246 DataContractInSerializationFormat::V0(v0) => &v0.config,
247 DataContractInSerializationFormat::V1(v1) => &v1.config,
248 DataContractInSerializationFormat::V2(v2) => &v2.base.config,
249 }
250 }
251
252 pub fn groups(&self) -> &BTreeMap<GroupContractPosition, Group> {
253 match self {
254 DataContractInSerializationFormat::V0(_) => &EMPTY_GROUPS,
255 DataContractInSerializationFormat::V1(v1) => &v1.groups,
256 DataContractInSerializationFormat::V2(v2) => &v2.base.groups,
257 }
258 }
259 pub fn tokens(&self) -> &BTreeMap<TokenContractPosition, TokenConfiguration> {
260 match self {
261 DataContractInSerializationFormat::V0(_) => &EMPTY_TOKENS,
262 DataContractInSerializationFormat::V1(v1) => &v1.tokens,
263 DataContractInSerializationFormat::V2(v2) => &v2.base.tokens,
264 }
265 }
266
267 pub fn keywords(&self) -> &Vec<String> {
268 match self {
269 DataContractInSerializationFormat::V0(_) => &EMPTY_KEYWORDS,
270 DataContractInSerializationFormat::V1(v1) => &v1.keywords,
271 DataContractInSerializationFormat::V2(v2) => &v2.base.keywords,
272 }
273 }
274
275 pub fn description(&self) -> &Option<String> {
276 match self {
277 DataContractInSerializationFormat::V0(_) => &None,
278 DataContractInSerializationFormat::V1(v1) => &v1.description,
279 DataContractInSerializationFormat::V2(v2) => &v2.base.description,
280 }
281 }
282
283 pub fn first_mismatch(&self, other: &Self) -> Option<DataContractMismatch> {
296 match (self, other) {
297 (
298 DataContractInSerializationFormat::V0(v0_self),
299 DataContractInSerializationFormat::V0(v0_other),
300 ) => {
301 if v0_self != v0_other {
302 Some(DataContractMismatch::V0Mismatch)
303 } else {
304 None
305 }
306 }
307 (
308 DataContractInSerializationFormat::V1(v1_self),
309 DataContractInSerializationFormat::V1(v1_other),
310 ) => {
311 if v1_self.id != v1_other.id {
312 Some(DataContractMismatch::Id)
313 } else if v1_self.config != v1_other.config {
314 Some(DataContractMismatch::Config)
315 } else if v1_self.version != v1_other.version {
316 Some(DataContractMismatch::Version)
317 } else if v1_self.owner_id != v1_other.owner_id {
318 Some(DataContractMismatch::OwnerId)
319 } else if v1_self.schema_defs != v1_other.schema_defs {
320 Some(DataContractMismatch::SchemaDefs)
321 } else if v1_self.document_schemas != v1_other.document_schemas {
322 Some(DataContractMismatch::DocumentSchemas)
323 } else if v1_self.groups != v1_other.groups {
324 Some(DataContractMismatch::Groups)
325 } else if v1_self.tokens != v1_other.tokens {
326 Some(DataContractMismatch::Tokens)
327 } else if v1_self.keywords.len() != v1_other.keywords.len()
328 || v1_self
329 .keywords
330 .iter()
331 .zip(v1_other.keywords.iter())
332 .any(|(a, b)| a.to_lowercase() != b.to_lowercase())
333 {
334 Some(DataContractMismatch::Keywords)
335 } else if v1_self.description != v1_other.description {
336 Some(DataContractMismatch::Description)
337 } else {
338 None
339 }
340 }
341 _ => Some(DataContractMismatch::FormatVersionMismatch),
342 }
343 }
344
345 pub fn try_from_data_contract_for_storage(
350 contract: &DataContract,
351 platform_version: &PlatformVersion,
352 ) -> Result<Self, ProtocolError> {
353 contract
354 .config()
355 .ensure_admitted_by_platform_version(platform_version)?;
356 match platform_version
357 .dpp
358 .contract_versions
359 .contract_storage_serialization_version
360 {
361 0 => {
362 let v0_format: DataContractInSerializationFormatV0 =
363 contract.clone().into_platform_versioned(platform_version);
364 Ok(v0_format.into())
365 }
366 1 => {
367 let v1_format: DataContractInSerializationFormatV1 =
368 contract.clone().into_platform_versioned(platform_version);
369 Ok(v1_format.into())
370 }
371 2 => Ok(DataContractInSerializationFormatV2::try_from_data_contract(
372 contract,
373 platform_version,
374 )?
375 .into()),
376 version => Err(ProtocolError::UnknownVersionMismatch {
377 method: "DataContractInSerializationFormat::try_from_data_contract_for_storage"
378 .to_string(),
379 known_versions: vec![0, 1, 2],
380 received: version,
381 }),
382 }
383 }
384}
385
386impl TryFromPlatformVersioned<DataContractV0> for DataContractInSerializationFormat {
387 type Error = ProtocolError;
388
389 fn try_from_platform_versioned(
390 value: DataContractV0,
391 platform_version: &PlatformVersion,
392 ) -> Result<Self, Self::Error> {
393 value
394 .config
395 .ensure_admitted_by_platform_version(platform_version)?;
396 match platform_version
397 .dpp
398 .contract_versions
399 .contract_serialization_version
400 .default_current_version
401 {
402 0 => {
403 let v0_format: DataContractInSerializationFormatV0 =
404 DataContract::V0(value).into_platform_versioned(platform_version);
405 Ok(v0_format.into())
406 }
407 1 => {
408 let v1_format: DataContractInSerializationFormatV1 =
409 DataContract::V0(value).into_platform_versioned(platform_version);
410 Ok(v1_format.into())
411 }
412 version => Err(ProtocolError::UnknownVersionMismatch {
413 method: "DataContract::serialize_to_default_current_version".to_string(),
414 known_versions: vec![0, 1],
415 received: version,
416 }),
417 }
418 }
419}
420
421impl TryFromPlatformVersioned<&DataContractV0> for DataContractInSerializationFormat {
422 type Error = ProtocolError;
423
424 fn try_from_platform_versioned(
425 value: &DataContractV0,
426 platform_version: &PlatformVersion,
427 ) -> Result<Self, Self::Error> {
428 value
429 .config
430 .ensure_admitted_by_platform_version(platform_version)?;
431 match platform_version
432 .dpp
433 .contract_versions
434 .contract_serialization_version
435 .default_current_version
436 {
437 0 => {
438 let v0_format: DataContractInSerializationFormatV0 =
439 DataContract::V0(value.to_owned()).into_platform_versioned(platform_version);
440 Ok(v0_format.into())
441 }
442 1 => {
443 let v1_format: DataContractInSerializationFormatV1 =
444 DataContract::V0(value.to_owned()).into_platform_versioned(platform_version);
445 Ok(v1_format.into())
446 }
447 version => Err(ProtocolError::UnknownVersionMismatch {
448 method: "DataContract::serialize_to_default_current_version".to_string(),
449 known_versions: vec![0, 1],
450 received: version,
451 }),
452 }
453 }
454}
455
456impl TryFromPlatformVersioned<DataContractV1> for DataContractInSerializationFormat {
457 type Error = ProtocolError;
458
459 fn try_from_platform_versioned(
460 value: DataContractV1,
461 platform_version: &PlatformVersion,
462 ) -> Result<Self, Self::Error> {
463 value
464 .config
465 .ensure_admitted_by_platform_version(platform_version)?;
466 match platform_version
467 .dpp
468 .contract_versions
469 .contract_serialization_version
470 .default_current_version
471 {
472 0 => {
473 let v0_format: DataContractInSerializationFormatV0 =
474 DataContract::V1(value).into_platform_versioned(platform_version);
475 Ok(v0_format.into())
476 }
477 1 => {
478 let v1_format: DataContractInSerializationFormatV1 =
479 DataContract::V1(value).into_platform_versioned(platform_version);
480 Ok(v1_format.into())
481 }
482 version => Err(ProtocolError::UnknownVersionMismatch {
483 method: "DataContract::serialize_to_default_current_version".to_string(),
484 known_versions: vec![0, 1],
485 received: version,
486 }),
487 }
488 }
489}
490
491impl TryFromPlatformVersioned<&DataContractV1> for DataContractInSerializationFormat {
492 type Error = ProtocolError;
493
494 fn try_from_platform_versioned(
495 value: &DataContractV1,
496 platform_version: &PlatformVersion,
497 ) -> Result<Self, Self::Error> {
498 value
499 .config
500 .ensure_admitted_by_platform_version(platform_version)?;
501 match platform_version
502 .dpp
503 .contract_versions
504 .contract_serialization_version
505 .default_current_version
506 {
507 0 => {
508 let v0_format: DataContractInSerializationFormatV0 =
509 DataContract::V1(value.to_owned()).into_platform_versioned(platform_version);
510 Ok(v0_format.into())
511 }
512 1 => {
513 let v1_format: DataContractInSerializationFormatV1 =
514 DataContract::V1(value.to_owned()).into_platform_versioned(platform_version);
515 Ok(v1_format.into())
516 }
517 version => Err(ProtocolError::UnknownVersionMismatch {
518 method: "DataContract::serialize_to_default_current_version".to_string(),
519 known_versions: vec![0, 1],
520 received: version,
521 }),
522 }
523 }
524}
525
526impl TryFromPlatformVersioned<&DataContract> for DataContractInSerializationFormat {
527 type Error = ProtocolError;
528
529 fn try_from_platform_versioned(
530 value: &DataContract,
531 platform_version: &PlatformVersion,
532 ) -> Result<Self, Self::Error> {
533 value
534 .config()
535 .ensure_admitted_by_platform_version(platform_version)?;
536 match platform_version
537 .dpp
538 .contract_versions
539 .contract_serialization_version
540 .default_current_version
541 {
542 0 => {
543 let v0_format: DataContractInSerializationFormatV0 =
544 value.clone().into_platform_versioned(platform_version);
545 Ok(v0_format.into())
546 }
547 1 => {
548 let v1_format: DataContractInSerializationFormatV1 =
549 value.clone().into_platform_versioned(platform_version);
550 Ok(v1_format.into())
551 }
552 version => Err(ProtocolError::UnknownVersionMismatch {
553 method: "DataContract::serialize_to_default_current_version".to_string(),
554 known_versions: vec![0, 1],
555 received: version,
556 }),
557 }
558 }
559}
560
561impl TryFromPlatformVersioned<DataContract> for DataContractInSerializationFormat {
562 type Error = ProtocolError;
563
564 fn try_from_platform_versioned(
565 value: DataContract,
566 platform_version: &PlatformVersion,
567 ) -> Result<Self, Self::Error> {
568 value
569 .config()
570 .ensure_admitted_by_platform_version(platform_version)?;
571 match platform_version
572 .dpp
573 .contract_versions
574 .contract_serialization_version
575 .default_current_version
576 {
577 0 => {
578 let v0_format: DataContractInSerializationFormatV0 =
579 value.into_platform_versioned(platform_version);
580 Ok(v0_format.into())
581 }
582 1 => {
583 let v1_format: DataContractInSerializationFormatV1 =
584 value.into_platform_versioned(platform_version);
585 Ok(v1_format.into())
586 }
587 version => Err(ProtocolError::UnknownVersionMismatch {
588 method: "DataContract::serialize_consume_to_default_current_version".to_string(),
589 known_versions: vec![0, 1],
590 received: version,
591 }),
592 }
593 }
594}
595
596impl DataContract {
597 pub fn try_from_platform_versioned(
598 value: DataContractInSerializationFormat,
599 full_validation: bool,
600 validation_operations: &mut Vec<ProtocolValidationOperation>,
601 platform_version: &PlatformVersion,
602 ) -> Result<Self, ProtocolError> {
603 match platform_version
604 .dpp
605 .contract_versions
606 .contract_structure_version
607 {
608 0 => DataContractV0::try_from_platform_versioned(
609 value,
610 full_validation,
611 validation_operations,
612 platform_version,
613 )
614 .map(|contract| contract.into()),
615 1 => DataContractV1::try_from_platform_versioned(
616 value,
617 full_validation,
618 validation_operations,
619 platform_version,
620 )
621 .map(|contract| contract.into()),
622 version => Err(ProtocolError::UnknownVersionMismatch {
623 method: "DataContract::try_from_platform_versioned".to_string(),
624 known_versions: vec![0, 1],
625 received: version,
626 }),
627 }
628 }
629}
630
631#[cfg(test)]
632mod tests {
633 use super::*;
634 use crate::data_contract::accessors::v0::DataContractV0Setters;
635 use crate::data_contract::config::moderation::ContractModerationConfig;
636 use crate::data_contract::config::v0::DataContractConfigV0;
637 use crate::data_contract::config::v1::DataContractConfigV1;
638 use crate::data_contract::group::v0::GroupV0;
639 use crate::data_contract::serialized_version::v0::DataContractInSerializationFormatV0;
640 use crate::data_contract::serialized_version::v1::DataContractInSerializationFormatV1;
641 use crate::serialization::PlatformSerializableWithPlatformVersion;
642 use crate::state_transition::data_contract_create_transition::DataContractCreateTransition;
643 use crate::tests::fixtures::get_data_contract_fixture;
644 use platform_value::Identifier;
645 use std::collections::BTreeMap;
646
647 fn moderated_contract(protocol_version: u32) -> DataContract {
649 let mut contract =
650 get_data_contract_fixture(None, 0, protocol_version).data_contract_owned();
651 let config = contract
652 .config()
653 .clone()
654 .with_moderation(Some(ContractModerationConfig {
655 banlist: true,
656 suspensions: false,
657 moderators: Default::default(),
658 warnings: false,
659 }));
660 contract.set_config(config);
661 contract
662 }
663
664 fn every_conversion(
666 contract: &DataContract,
667 platform_version: &PlatformVersion,
668 ) -> Vec<Result<DataContractInSerializationFormat, ProtocolError>> {
669 let mut conversions = vec![
670 DataContractInSerializationFormat::try_from_platform_versioned(
671 contract.clone(),
672 platform_version,
673 ),
674 DataContractInSerializationFormat::try_from_platform_versioned(
675 contract,
676 platform_version,
677 ),
678 ];
679 match contract {
680 DataContract::V0(v0) => {
681 conversions.push(
682 DataContractInSerializationFormat::try_from_platform_versioned(
683 v0.clone(),
684 platform_version,
685 ),
686 );
687 conversions.push(
688 DataContractInSerializationFormat::try_from_platform_versioned(
689 v0,
690 platform_version,
691 ),
692 );
693 }
694 DataContract::V1(v1) => {
695 conversions.push(
696 DataContractInSerializationFormat::try_from_platform_versioned(
697 v1.clone(),
698 platform_version,
699 ),
700 );
701 conversions.push(
702 DataContractInSerializationFormat::try_from_platform_versioned(
703 v1,
704 platform_version,
705 ),
706 );
707 }
708 }
709 conversions
710 }
711
712 #[test]
713 fn should_refuse_to_serialize_a_moderated_contract_below_protocol_version_14() {
714 let latest = PlatformVersion::latest();
715 let v13 = PlatformVersion::get(13).expect("protocol version 13");
716
717 assert!(matches!(moderated_contract(1), DataContract::V0(_)));
719 assert!(matches!(moderated_contract(14), DataContract::V1(_)));
720 for contract in [moderated_contract(1), moderated_contract(14)] {
721 for conversion in every_conversion(&contract, v13) {
724 assert!(matches!(conversion, Err(ProtocolError::NotSupported(_))));
725 }
726 assert!(matches!(
727 contract.serialize_to_bytes_with_platform_version(v13),
728 Err(ProtocolError::NotSupported(_))
729 ));
730 assert!(matches!(
731 DataContractCreateTransition::try_from_platform_versioned(contract.clone(), v13),
732 Err(ProtocolError::NotSupported(_))
733 ));
734
735 for conversion in every_conversion(&contract, latest) {
736 assert!(conversion.is_ok());
737 }
738 assert!(contract
739 .serialize_to_bytes_with_platform_version(latest)
740 .is_ok());
741 }
742 }
743
744 fn make_v0() -> DataContractInSerializationFormatV0 {
746 DataContractInSerializationFormatV0 {
747 id: Identifier::default(),
748 config: DataContractConfig::V0(DataContractConfigV0::default()),
749 version: 1,
750 owner_id: Identifier::default(),
751 schema_defs: None,
752 document_schemas: BTreeMap::new(),
753 }
754 }
755
756 fn make_v1() -> DataContractInSerializationFormatV1 {
758 DataContractInSerializationFormatV1 {
759 id: Identifier::default(),
760 config: DataContractConfig::V1(DataContractConfigV1::default()),
761 version: 1,
762 owner_id: Identifier::default(),
763 schema_defs: None,
764 document_schemas: BTreeMap::new(),
765 created_at: None,
766 updated_at: None,
767 created_at_block_height: None,
768 updated_at_block_height: None,
769 created_at_epoch: None,
770 updated_at_epoch: None,
771 groups: BTreeMap::new(),
772 tokens: BTreeMap::new(),
773 keywords: vec![],
774 description: None,
775 }
776 }
777
778 #[test]
783 fn first_mismatch_v0_v0_identical_returns_none() {
784 let a = DataContractInSerializationFormat::V0(make_v0());
785 let b = DataContractInSerializationFormat::V0(make_v0());
786 assert_eq!(a.first_mismatch(&b), None);
787 }
788
789 #[test]
790 fn first_mismatch_v0_v0_different_id() {
791 let mut v0_b = make_v0();
792 v0_b.id = Identifier::from([1u8; 32]);
793 let a = DataContractInSerializationFormat::V0(make_v0());
794 let b = DataContractInSerializationFormat::V0(v0_b);
795 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::V0Mismatch));
796 }
797
798 #[test]
799 fn first_mismatch_v0_v0_different_config() {
800 let mut v0_b = make_v0();
801 let mut cfg = DataContractConfigV0::default();
802 cfg.readonly = !cfg.readonly;
803 v0_b.config = DataContractConfig::V0(cfg);
804 let a = DataContractInSerializationFormat::V0(make_v0());
805 let b = DataContractInSerializationFormat::V0(v0_b);
806 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::V0Mismatch));
807 }
808
809 #[test]
810 fn first_mismatch_v0_v0_different_version() {
811 let mut v0_b = make_v0();
812 v0_b.version = 99;
813 let a = DataContractInSerializationFormat::V0(make_v0());
814 let b = DataContractInSerializationFormat::V0(v0_b);
815 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::V0Mismatch));
816 }
817
818 #[test]
819 fn first_mismatch_v0_v0_different_owner_id() {
820 let mut v0_b = make_v0();
821 v0_b.owner_id = Identifier::from([2u8; 32]);
822 let a = DataContractInSerializationFormat::V0(make_v0());
823 let b = DataContractInSerializationFormat::V0(v0_b);
824 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::V0Mismatch));
825 }
826
827 #[test]
828 fn first_mismatch_v0_v0_different_document_schemas() {
829 let mut v0_b = make_v0();
830 v0_b.document_schemas
831 .insert("doc".to_string(), Value::Bool(true));
832 let a = DataContractInSerializationFormat::V0(make_v0());
833 let b = DataContractInSerializationFormat::V0(v0_b);
834 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::V0Mismatch));
835 }
836
837 #[test]
842 fn first_mismatch_v0_v1_returns_format_version_mismatch() {
843 let a = DataContractInSerializationFormat::V0(make_v0());
844 let b = DataContractInSerializationFormat::V1(make_v1());
845 assert_eq!(
846 a.first_mismatch(&b),
847 Some(DataContractMismatch::FormatVersionMismatch)
848 );
849 }
850
851 #[test]
852 fn first_mismatch_v1_v0_returns_format_version_mismatch() {
853 let a = DataContractInSerializationFormat::V1(make_v1());
854 let b = DataContractInSerializationFormat::V0(make_v0());
855 assert_eq!(
856 a.first_mismatch(&b),
857 Some(DataContractMismatch::FormatVersionMismatch)
858 );
859 }
860
861 #[test]
866 fn first_mismatch_v1_v1_identical_returns_none() {
867 let a = DataContractInSerializationFormat::V1(make_v1());
868 let b = DataContractInSerializationFormat::V1(make_v1());
869 assert_eq!(a.first_mismatch(&b), None);
870 }
871
872 #[test]
877 fn first_mismatch_v1_v1_different_id() {
878 let mut v1_b = make_v1();
879 v1_b.id = Identifier::from([1u8; 32]);
880 let a = DataContractInSerializationFormat::V1(make_v1());
881 let b = DataContractInSerializationFormat::V1(v1_b);
882 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::Id));
883 }
884
885 #[test]
886 fn first_mismatch_v1_v1_different_config() {
887 let mut v1_b = make_v1();
888 let mut cfg = DataContractConfigV1::default();
889 cfg.readonly = !cfg.readonly;
890 v1_b.config = DataContractConfig::V1(cfg);
891 let a = DataContractInSerializationFormat::V1(make_v1());
892 let b = DataContractInSerializationFormat::V1(v1_b);
893 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::Config));
894 }
895
896 #[test]
897 fn first_mismatch_v1_v1_different_version() {
898 let mut v1_b = make_v1();
899 v1_b.version = 42;
900 let a = DataContractInSerializationFormat::V1(make_v1());
901 let b = DataContractInSerializationFormat::V1(v1_b);
902 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::Version));
903 }
904
905 #[test]
906 fn first_mismatch_v1_v1_different_owner_id() {
907 let mut v1_b = make_v1();
908 v1_b.owner_id = Identifier::from([3u8; 32]);
909 let a = DataContractInSerializationFormat::V1(make_v1());
910 let b = DataContractInSerializationFormat::V1(v1_b);
911 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::OwnerId));
912 }
913
914 #[test]
915 fn first_mismatch_v1_v1_different_schema_defs() {
916 let mut v1_b = make_v1();
917 let mut defs = BTreeMap::new();
918 defs.insert("someDef".to_string(), Value::Bool(true));
919 v1_b.schema_defs = Some(defs);
920 let a = DataContractInSerializationFormat::V1(make_v1());
921 let b = DataContractInSerializationFormat::V1(v1_b);
922 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::SchemaDefs));
923 }
924
925 #[test]
926 fn first_mismatch_v1_v1_different_document_schemas() {
927 let mut v1_b = make_v1();
928 v1_b.document_schemas
929 .insert("doc".to_string(), Value::U64(1));
930 let a = DataContractInSerializationFormat::V1(make_v1());
931 let b = DataContractInSerializationFormat::V1(v1_b);
932 assert_eq!(
933 a.first_mismatch(&b),
934 Some(DataContractMismatch::DocumentSchemas)
935 );
936 }
937
938 #[test]
939 fn first_mismatch_v1_v1_different_groups() {
940 let mut v1_b = make_v1();
941 v1_b.groups.insert(
942 0,
943 Group::V0(GroupV0 {
944 members: Default::default(),
945 required_power: 1,
946 }),
947 );
948 let a = DataContractInSerializationFormat::V1(make_v1());
949 let b = DataContractInSerializationFormat::V1(v1_b);
950 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::Groups));
951 }
952
953 #[test]
954 fn first_mismatch_v1_v1_different_tokens() {
955 let mut v1_b = make_v1();
956 v1_b.tokens.insert(
957 0,
958 TokenConfiguration::V0(
959 crate::data_contract::associated_token::token_configuration::v0::TokenConfigurationV0::default_most_restrictive(),
960 ),
961 );
962 let a = DataContractInSerializationFormat::V1(make_v1());
963 let b = DataContractInSerializationFormat::V1(v1_b);
964 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::Tokens));
965 }
966
967 #[test]
968 fn first_mismatch_v1_v1_different_keywords() {
969 let mut v1_b = make_v1();
970 v1_b.keywords = vec!["test".to_string()];
971 let a = DataContractInSerializationFormat::V1(make_v1());
972 let b = DataContractInSerializationFormat::V1(v1_b);
973 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::Keywords));
974 }
975
976 #[test]
977 fn first_mismatch_v1_v1_keywords_case_insensitive_match() {
978 let mut v1_a = make_v1();
979 v1_a.keywords = vec!["Test".to_string()];
980 let mut v1_b = make_v1();
981 v1_b.keywords = vec!["test".to_string()];
982 let a = DataContractInSerializationFormat::V1(v1_a);
983 let b = DataContractInSerializationFormat::V1(v1_b);
984 assert_eq!(a.first_mismatch(&b), None);
986 }
987
988 #[test]
989 fn first_mismatch_v1_v1_keywords_different_length() {
990 let mut v1_a = make_v1();
991 v1_a.keywords = vec!["a".to_string()];
992 let mut v1_b = make_v1();
993 v1_b.keywords = vec!["a".to_string(), "b".to_string()];
994 let a = DataContractInSerializationFormat::V1(v1_a);
995 let b = DataContractInSerializationFormat::V1(v1_b);
996 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::Keywords));
997 }
998
999 #[test]
1000 fn first_mismatch_v1_v1_different_description() {
1001 let mut v1_b = make_v1();
1002 v1_b.description = Some("a description".to_string());
1003 let a = DataContractInSerializationFormat::V1(make_v1());
1004 let b = DataContractInSerializationFormat::V1(v1_b);
1005 assert_eq!(
1006 a.first_mismatch(&b),
1007 Some(DataContractMismatch::Description)
1008 );
1009 }
1010
1011 #[test]
1016 fn first_mismatch_v1_v1_id_takes_priority_over_config() {
1017 let mut v1_b = make_v1();
1018 v1_b.id = Identifier::from([5u8; 32]);
1019 let mut cfg = DataContractConfigV1::default();
1020 cfg.readonly = !cfg.readonly;
1021 v1_b.config = DataContractConfig::V1(cfg);
1022 let a = DataContractInSerializationFormat::V1(make_v1());
1023 let b = DataContractInSerializationFormat::V1(v1_b);
1024 assert_eq!(a.first_mismatch(&b), Some(DataContractMismatch::Id));
1026 }
1027
1028 #[test]
1033 fn data_contract_mismatch_display() {
1034 assert_eq!(format!("{}", DataContractMismatch::Id), "ID fields differ");
1035 assert_eq!(
1036 format!("{}", DataContractMismatch::FormatVersionMismatch),
1037 "Serialization format versions differ (e.g., V0 vs V1)"
1038 );
1039 assert_eq!(
1040 format!("{}", DataContractMismatch::V0Mismatch),
1041 "V0 versions differ"
1042 );
1043 assert_eq!(format!("{}", DataContractMismatch::Tokens), "Tokens differ");
1044 assert_eq!(
1045 format!("{}", DataContractMismatch::Keywords),
1046 "Keywords differ"
1047 );
1048 assert_eq!(
1049 format!("{}", DataContractMismatch::Description),
1050 "Description fields differ"
1051 );
1052 }
1053
1054 #[test]
1059 fn accessor_id_v0() {
1060 let v0 = make_v0();
1061 let expected_id = v0.id;
1062 let format = DataContractInSerializationFormat::V0(v0);
1063 assert_eq!(format.id(), expected_id);
1064 }
1065
1066 #[test]
1067 fn accessor_id_v1() {
1068 let v1 = make_v1();
1069 let expected_id = v1.id;
1070 let format = DataContractInSerializationFormat::V1(v1);
1071 assert_eq!(format.id(), expected_id);
1072 }
1073
1074 #[test]
1075 fn accessor_owner_id_v0() {
1076 let mut v0 = make_v0();
1077 v0.owner_id = Identifier::from([7u8; 32]);
1078 let expected = v0.owner_id;
1079 let format = DataContractInSerializationFormat::V0(v0);
1080 assert_eq!(format.owner_id(), expected);
1081 }
1082
1083 #[test]
1084 fn accessor_version_v0() {
1085 let mut v0 = make_v0();
1086 v0.version = 10;
1087 let format = DataContractInSerializationFormat::V0(v0);
1088 assert_eq!(format.version(), 10);
1089 }
1090
1091 #[test]
1092 fn accessor_version_v1() {
1093 let mut v1 = make_v1();
1094 v1.version = 20;
1095 let format = DataContractInSerializationFormat::V1(v1);
1096 assert_eq!(format.version(), 20);
1097 }
1098
1099 #[test]
1100 fn accessor_groups_v0_returns_empty() {
1101 let format = DataContractInSerializationFormat::V0(make_v0());
1102 assert!(format.groups().is_empty());
1103 }
1104
1105 #[test]
1106 fn accessor_tokens_v0_returns_empty() {
1107 let format = DataContractInSerializationFormat::V0(make_v0());
1108 assert!(format.tokens().is_empty());
1109 }
1110
1111 #[test]
1112 fn accessor_keywords_v0_returns_empty() {
1113 let format = DataContractInSerializationFormat::V0(make_v0());
1114 assert!(format.keywords().is_empty());
1115 }
1116
1117 #[test]
1118 fn accessor_description_v0_returns_none() {
1119 let format = DataContractInSerializationFormat::V0(make_v0());
1120 assert_eq!(format.description(), &None);
1121 }
1122
1123 #[test]
1124 fn accessor_keywords_v1() {
1125 let mut v1 = make_v1();
1126 v1.keywords = vec!["hello".to_string()];
1127 let format = DataContractInSerializationFormat::V1(v1);
1128 assert_eq!(format.keywords(), &vec!["hello".to_string()]);
1129 }
1130
1131 #[test]
1132 fn accessor_description_v1_some() {
1133 let mut v1 = make_v1();
1134 v1.description = Some("desc".to_string());
1135 let format = DataContractInSerializationFormat::V1(v1);
1136 assert_eq!(format.description(), &Some("desc".to_string()));
1137 }
1138
1139 #[test]
1140 fn accessor_document_schemas_v0() {
1141 let mut v0 = make_v0();
1142 v0.document_schemas
1143 .insert("note".to_string(), Value::Bool(true));
1144 let format = DataContractInSerializationFormat::V0(v0);
1145 assert_eq!(format.document_schemas().len(), 1);
1146 assert!(format.document_schemas().contains_key("note"));
1147 }
1148
1149 #[test]
1150 fn accessor_schema_defs_v0_none() {
1151 let format = DataContractInSerializationFormat::V0(make_v0());
1152 assert!(format.schema_defs().is_none());
1153 }
1154
1155 #[test]
1156 fn accessor_schema_defs_v1_some() {
1157 let mut v1 = make_v1();
1158 let mut defs = BTreeMap::new();
1159 defs.insert("def1".to_string(), Value::Null);
1160 v1.schema_defs = Some(defs);
1161 let format = DataContractInSerializationFormat::V1(v1);
1162 assert!(format.schema_defs().is_some());
1163 assert!(format.schema_defs().unwrap().contains_key("def1"));
1164 }
1165
1166 #[test]
1171 fn try_from_platform_versioned_data_contract_v0_version_0() {
1172 let platform_version = PlatformVersion::first();
1173 let v0 = DataContractV0 {
1175 id: Identifier::from([10u8; 32]),
1176 config: DataContractConfig::V0(DataContractConfigV0::default()),
1177 version: 1,
1178 owner_id: Identifier::from([20u8; 32]),
1179 schema_defs: None,
1180 document_types: BTreeMap::new(),
1181 metadata: None,
1182 };
1183 let result = DataContractInSerializationFormat::try_from_platform_versioned(
1184 v0.clone(),
1185 platform_version,
1186 );
1187 assert!(result.is_ok());
1188 let format = result.unwrap();
1189 assert!(matches!(format, DataContractInSerializationFormat::V0(_)));
1190 assert_eq!(format.id(), Identifier::from([10u8; 32]));
1191 assert_eq!(format.owner_id(), Identifier::from([20u8; 32]));
1192 }
1193
1194 #[test]
1195 fn try_from_platform_versioned_data_contract_v0_ref_version_0() {
1196 let platform_version = PlatformVersion::first();
1197 let v0 = DataContractV0 {
1198 id: Identifier::from([11u8; 32]),
1199 config: DataContractConfig::V0(DataContractConfigV0::default()),
1200 version: 2,
1201 owner_id: Identifier::from([22u8; 32]),
1202 schema_defs: None,
1203 document_types: BTreeMap::new(),
1204 metadata: None,
1205 };
1206 let result =
1207 DataContractInSerializationFormat::try_from_platform_versioned(&v0, platform_version);
1208 assert!(result.is_ok());
1209 let format = result.unwrap();
1210 assert!(matches!(format, DataContractInSerializationFormat::V0(_)));
1211 assert_eq!(format.version(), 2);
1212 }
1213
1214 #[test]
1215 fn try_from_platform_versioned_data_contract_v0_version_1() {
1216 let platform_version = PlatformVersion::latest();
1217 let v0 = DataContractV0 {
1219 id: Identifier::from([10u8; 32]),
1220 config: DataContractConfig::V0(DataContractConfigV0::default()),
1221 version: 1,
1222 owner_id: Identifier::from([20u8; 32]),
1223 schema_defs: None,
1224 document_types: BTreeMap::new(),
1225 metadata: None,
1226 };
1227 let result = DataContractInSerializationFormat::try_from_platform_versioned(
1228 v0.clone(),
1229 platform_version,
1230 );
1231 assert!(result.is_ok());
1232 let format = result.unwrap();
1233 assert!(matches!(format, DataContractInSerializationFormat::V1(_)));
1234 }
1235
1236 #[test]
1241 fn try_from_platform_versioned_data_contract_v1_version_0() {
1242 let platform_version = PlatformVersion::first();
1243 let v1 = DataContractV1 {
1244 id: Identifier::from([10u8; 32]),
1245 config: DataContractConfig::V0(DataContractConfigV0::default()),
1246 version: 1,
1247 owner_id: Identifier::from([20u8; 32]),
1248 schema_defs: None,
1249 document_types: BTreeMap::new(),
1250 created_at: None,
1251 updated_at: None,
1252 created_at_block_height: None,
1253 updated_at_block_height: None,
1254 created_at_epoch: None,
1255 updated_at_epoch: None,
1256 groups: BTreeMap::new(),
1257 tokens: BTreeMap::new(),
1258 keywords: vec![],
1259 description: None,
1260 };
1261 let result = DataContractInSerializationFormat::try_from_platform_versioned(
1262 v1.clone(),
1263 platform_version,
1264 );
1265 assert!(result.is_ok());
1266 let format = result.unwrap();
1267 assert!(matches!(format, DataContractInSerializationFormat::V0(_)));
1268 }
1269
1270 #[test]
1271 fn try_from_platform_versioned_data_contract_v1_version_1() {
1272 let platform_version = PlatformVersion::latest();
1273 let v1 = DataContractV1 {
1274 id: Identifier::from([10u8; 32]),
1275 config: DataContractConfig::V1(DataContractConfigV1::default()),
1276 version: 1,
1277 owner_id: Identifier::from([20u8; 32]),
1278 schema_defs: None,
1279 document_types: BTreeMap::new(),
1280 created_at: None,
1281 updated_at: None,
1282 created_at_block_height: None,
1283 updated_at_block_height: None,
1284 created_at_epoch: None,
1285 updated_at_epoch: None,
1286 groups: BTreeMap::new(),
1287 tokens: BTreeMap::new(),
1288 keywords: vec![],
1289 description: None,
1290 };
1291 let result = DataContractInSerializationFormat::try_from_platform_versioned(
1292 v1.clone(),
1293 platform_version,
1294 );
1295 assert!(result.is_ok());
1296 let format = result.unwrap();
1297 assert!(matches!(format, DataContractInSerializationFormat::V1(_)));
1298 }
1299
1300 #[test]
1301 fn try_from_platform_versioned_data_contract_v1_ref_version_1() {
1302 let platform_version = PlatformVersion::latest();
1303 let v1 = DataContractV1 {
1304 id: Identifier::from([10u8; 32]),
1305 config: DataContractConfig::V1(DataContractConfigV1::default()),
1306 version: 3,
1307 owner_id: Identifier::from([20u8; 32]),
1308 schema_defs: None,
1309 document_types: BTreeMap::new(),
1310 created_at: None,
1311 updated_at: None,
1312 created_at_block_height: None,
1313 updated_at_block_height: None,
1314 created_at_epoch: None,
1315 updated_at_epoch: None,
1316 groups: BTreeMap::new(),
1317 tokens: BTreeMap::new(),
1318 keywords: vec![],
1319 description: None,
1320 };
1321 let result =
1322 DataContractInSerializationFormat::try_from_platform_versioned(&v1, platform_version);
1323 assert!(result.is_ok());
1324 let format = result.unwrap();
1325 assert!(matches!(format, DataContractInSerializationFormat::V1(_)));
1326 assert_eq!(format.version(), 3);
1327 }
1328
1329 #[test]
1334 fn try_from_platform_versioned_data_contract_ref_version_0() {
1335 let platform_version = PlatformVersion::first();
1336 let contract = DataContract::V0(DataContractV0 {
1337 id: Identifier::from([10u8; 32]),
1338 config: DataContractConfig::V0(DataContractConfigV0::default()),
1339 version: 1,
1340 owner_id: Identifier::from([20u8; 32]),
1341 schema_defs: None,
1342 document_types: BTreeMap::new(),
1343 metadata: None,
1344 });
1345 let result = DataContractInSerializationFormat::try_from_platform_versioned(
1346 &contract,
1347 platform_version,
1348 );
1349 assert!(result.is_ok());
1350 assert!(matches!(
1351 result.unwrap(),
1352 DataContractInSerializationFormat::V0(_)
1353 ));
1354 }
1355
1356 #[test]
1357 fn try_from_platform_versioned_data_contract_owned_version_1() {
1358 let platform_version = PlatformVersion::latest();
1359 let contract = DataContract::V0(DataContractV0 {
1360 id: Identifier::from([10u8; 32]),
1361 config: DataContractConfig::V0(DataContractConfigV0::default()),
1362 version: 1,
1363 owner_id: Identifier::from([20u8; 32]),
1364 schema_defs: None,
1365 document_types: BTreeMap::new(),
1366 metadata: None,
1367 });
1368 let result = DataContractInSerializationFormat::try_from_platform_versioned(
1369 contract,
1370 platform_version,
1371 );
1372 assert!(result.is_ok());
1373 assert!(matches!(
1374 result.unwrap(),
1375 DataContractInSerializationFormat::V1(_)
1376 ));
1377 }
1378
1379 #[test]
1384 fn first_platform_version_uses_serialization_version_0() {
1385 let pv = PlatformVersion::first();
1386 assert_eq!(
1387 pv.dpp
1388 .contract_versions
1389 .contract_serialization_version
1390 .default_current_version,
1391 0
1392 );
1393 }
1394
1395 #[test]
1396 fn latest_platform_version_uses_serialization_version_1() {
1397 let pv = PlatformVersion::latest();
1398 assert_eq!(
1399 pv.dpp
1400 .contract_versions
1401 .contract_serialization_version
1402 .default_current_version,
1403 1
1404 );
1405 }
1406
1407 #[test]
1408 fn first_platform_version_uses_contract_structure_0() {
1409 let pv = PlatformVersion::first();
1410 assert_eq!(pv.dpp.contract_versions.contract_structure_version, 0);
1411 }
1412
1413 #[test]
1414 fn latest_platform_version_uses_contract_structure_1() {
1415 let pv = PlatformVersion::latest();
1416 assert_eq!(pv.dpp.contract_versions.contract_structure_version, 1);
1417 }
1418}
1419
1420#[cfg(all(
1421 test,
1422 feature = "json-conversion",
1423 feature = "value-conversion",
1424 feature = "serde-conversion"
1425))]
1426mod json_convertible_tests {
1427 use super::*;
1428 use crate::data_contract::config::v0::DataContractConfigV0;
1429 use crate::data_contract::config::DataContractConfig;
1430 use crate::data_contract::serialized_version::v0::DataContractInSerializationFormatV0;
1431 use platform_value::Identifier;
1432 use std::collections::BTreeMap;
1433
1434 fn fixture() -> DataContractInSerializationFormat {
1435 DataContractInSerializationFormat::V0(DataContractInSerializationFormatV0 {
1436 id: Identifier::new([0xa1; 32]),
1437 config: DataContractConfig::V0(DataContractConfigV0::default()),
1438 version: 1,
1439 owner_id: Identifier::new([0xb2; 32]),
1440 schema_defs: None,
1441 document_schemas: BTreeMap::new(),
1442 })
1443 }
1444
1445 #[test]
1446 fn json_round_trip_with_full_wire_shape() {
1447 use crate::serialization::JsonConvertible;
1448 use serde_json::json;
1449 let original = fixture();
1450 let json = original.to_json().expect("to_json");
1451 assert_eq!(json["$formatVersion"], "0");
1458 assert_eq!(
1459 json["id"],
1460 json!("Bswb3UyeD1pUTaGiE6WvqwFpJZsQSEY1xhJePCDTHdvp")
1461 );
1462 assert_eq!(
1463 json["ownerId"],
1464 json!("D2ZcUbtpG5sKq7XLeB4YnpNnTGSptKCxTddoNeydzJQq")
1465 );
1466 assert_eq!(json["version"], json!(1));
1467 assert_eq!(json["schemaDefs"], json!(null));
1468 assert_eq!(json["documentSchemas"], json!({}));
1469 assert!(json.get("config").is_some(), "config envelope present");
1470 assert_eq!(json["config"]["$formatVersion"], "0");
1471 let recovered = DataContractInSerializationFormat::from_json(json).expect("from_json");
1472 assert_eq!(original, recovered);
1473 }
1474
1475 #[test]
1476 fn value_round_trip_with_full_wire_shape() {
1477 use crate::serialization::ValueConvertible;
1478 use platform_value::Value;
1479 let original = fixture();
1480 let value = original.to_object().expect("to_object");
1481 let map = match &value {
1484 Value::Map(m) => m,
1485 other => panic!("expected Value::Map, got {:?}", other),
1486 };
1487 let get = |k: &str| -> &Value {
1488 map.iter()
1489 .find(|(key, _)| matches!(key, Value::Text(t) if t == k))
1490 .map(|(_, v)| v)
1491 .unwrap_or_else(|| panic!("missing key {k}"))
1492 };
1493 assert_eq!(get("$formatVersion"), &Value::Text("0".to_string()));
1494 assert_eq!(get("id"), &Value::Identifier([0xa1; 32]));
1495 assert_eq!(get("ownerId"), &Value::Identifier([0xb2; 32]));
1496 assert_eq!(get("version"), &Value::U32(1));
1497 assert_eq!(get("schemaDefs"), &Value::Null);
1498 assert!(matches!(get("documentSchemas"), Value::Map(m) if m.is_empty()));
1500 assert!(matches!(get("config"), Value::Map(_)));
1502 let recovered = DataContractInSerializationFormat::from_object(value).expect("from_object");
1503 assert_eq!(original, recovered);
1504 }
1505
1506 #[test]
1507 fn json_preserves_format_version_tag() {
1508 use crate::serialization::JsonConvertible;
1509 let json = fixture().to_json().expect("to_json");
1510 assert_eq!(json["$formatVersion"], "0");
1511 }
1512}