From 52e6556e5350bb53b2c205612e6716c5a4cc5aa9 Mon Sep 17 00:00:00 2001 From: thephez Date: Mon, 27 Oct 2025 13:08:06 -0400 Subject: [PATCH 1/5] refactor(wasm-sdk): make data contract test matrix extensible Refactor platform version compatibility test configuration to be data-driven and auto-generate test suites. Makes adding new contract format versions easier by centralizing configuration. --- .../tests/unit/data-contract.spec.mjs | 71 +++++++++++++++---- 1 file changed, 57 insertions(+), 14 deletions(-) diff --git a/packages/wasm-sdk/tests/unit/data-contract.spec.mjs b/packages/wasm-sdk/tests/unit/data-contract.spec.mjs index 9f6df0fa5d..c08bed7c31 100644 --- a/packages/wasm-sdk/tests/unit/data-contract.spec.mjs +++ b/packages/wasm-sdk/tests/unit/data-contract.spec.mjs @@ -2,15 +2,54 @@ import init, * as sdk from '../../dist/sdk.compressed.js'; import contractFixtureV0 from './fixtures/data-contract-v0-crypto-card-game.mjs'; import contractFixtureV1 from './fixtures/data-contract-v1-with-docs-tokens-groups.mjs'; -// Platform version constants -const PLATFORM_VERSION_CONTRACT_V0 = 1; -const PLATFORM_VERSION_CONTRACT_V1 = 9; // V1 contracts introduced in Platform v9 +// Platform version configuration +const PLATFORM_VERSIONS = { + MIN: 1, + MAX: 10, // Update as new platform versions are released +}; + +// Contract format version configuration +// To add a new format version, add an entry here with: +// - The Platform version when introduced +// - A fixture for the new format +// Example: V2: { introduced: 12, fixture: contractFixtureV2 } +const CONTRACT_FORMAT_VERSIONS = { + V0: { introduced: 1, fixture: contractFixtureV0 }, + V1: { introduced: 9, fixture: contractFixtureV1 }, +}; + +// Auto-generate compatibility data for all formats +const FORMATS = Object.entries(CONTRACT_FORMAT_VERSIONS).reduce((acc, [formatKey, config]) => { + const compatibleVersions = Array.from( + { length: PLATFORM_VERSIONS.MAX - config.introduced + 1 }, + (_, i) => i + config.introduced + ); + + const allVersions = Array.from( + { length: PLATFORM_VERSIONS.MAX - PLATFORM_VERSIONS.MIN + 1 }, + (_, i) => i + PLATFORM_VERSIONS.MIN + ); + + const incompatibleVersions = allVersions.filter(v => v < config.introduced); -// Platform version compatibility ranges -const V0_COMPATIBLE_VERSIONS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // V0 works across all versions -const V1_COMPATIBLE_VERSIONS = [9, 10]; // V1 only works from version 9+ -const V0_ONLY_VERSIONS = [1, 2, 3, 4, 5, 6, 7, 8]; // Versions that only support V0 -const LATEST_KNOWN_VERSION = Math.max(...V0_COMPATIBLE_VERSIONS); + acc[formatKey] = { + ...config, + compatibleVersions, + incompatibleVersions, + platformVersion: config.introduced, + }; + + return acc; +}, {}); + +// Backward compatibility: Keep existing constant names as aliases +const PLATFORM_VERSION_CONTRACT_V0 = FORMATS.V0.platformVersion; +const PLATFORM_VERSION_CONTRACT_V1 = FORMATS.V1.platformVersion; +const V0_COMPATIBLE_VERSIONS = FORMATS.V0.compatibleVersions; +const V1_COMPATIBLE_VERSIONS = FORMATS.V1.compatibleVersions; +const V0_ONLY_VERSIONS = FORMATS.V0.incompatibleVersions; + +const LATEST_KNOWN_VERSION = PLATFORM_VERSIONS.MAX; // Helper function for testing contract compatibility across versions const testContractAcrossVersions = ( @@ -242,12 +281,16 @@ describe('DataContract', () => { }); describe('Platform Version Compatibility Matrix', () => { - describe('V0 Contract Compatibility', () => { - testContractAcrossVersions(contractFixtureV0, 'V0', V0_COMPATIBLE_VERSIONS); - }); - - describe('V1 Contract Compatibility', () => { - testContractAcrossVersions(contractFixtureV1, 'V1', V1_COMPATIBLE_VERSIONS, V0_ONLY_VERSIONS); + // Dynamically test all defined contract formats + Object.entries(FORMATS).forEach(([formatKey, formatData]) => { + describe(`${formatKey} Contract Compatibility`, () => { + testContractAcrossVersions( + formatData.fixture, + formatKey, + formatData.compatibleVersions, + formatData.incompatibleVersions + ); + }); }); describe('Edge Cases', () => { From 4bf18f77b4e2e1c0b4ce5876d1409580bdd3a5ac Mon Sep 17 00:00:00 2001 From: thephez Date: Mon, 27 Oct 2025 13:10:24 -0400 Subject: [PATCH 2/5] refactor(wasm-sdk): make contract format version compatibility fully dynamic Replace hardcoded version arrays with auto-generated compatibility data from CONTRACT_FORMAT_VERSIONS config. Tests now dynamically iterate over all defined formats, eliminating need for manual updates when adding new contract versions. --- .../tests/unit/data-contract.spec.mjs | 43 ++++++++----------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/packages/wasm-sdk/tests/unit/data-contract.spec.mjs b/packages/wasm-sdk/tests/unit/data-contract.spec.mjs index c08bed7c31..31a69f1949 100644 --- a/packages/wasm-sdk/tests/unit/data-contract.spec.mjs +++ b/packages/wasm-sdk/tests/unit/data-contract.spec.mjs @@ -42,13 +42,6 @@ const FORMATS = Object.entries(CONTRACT_FORMAT_VERSIONS).reduce((acc, [formatKey return acc; }, {}); -// Backward compatibility: Keep existing constant names as aliases -const PLATFORM_VERSION_CONTRACT_V0 = FORMATS.V0.platformVersion; -const PLATFORM_VERSION_CONTRACT_V1 = FORMATS.V1.platformVersion; -const V0_COMPATIBLE_VERSIONS = FORMATS.V0.compatibleVersions; -const V1_COMPATIBLE_VERSIONS = FORMATS.V1.compatibleVersions; -const V0_ONLY_VERSIONS = FORMATS.V0.incompatibleVersions; - const LATEST_KNOWN_VERSION = PLATFORM_VERSIONS.MAX; // Helper function for testing contract compatibility across versions @@ -87,7 +80,7 @@ describe('DataContract', () => { describe('Contract Creation', () => { it('should create a V0 contract from JSON and expose all properties', async () => { - const contract = sdk.DataContract.fromJSON(contractFixtureV0, PLATFORM_VERSION_CONTRACT_V0); + const contract = sdk.DataContract.fromJSON(contractFixtureV0, FORMATS.V0.platformVersion); expect(contract).to.be.ok(); expect(contract.id()).to.equal(contractFixtureV0.id); @@ -113,7 +106,7 @@ describe('DataContract', () => { // TODO: enable test once an SDK fix to support this is merged it.skip('should create a V1 contract from JSON and expose all properties including tokens and groups', async () => { - const contract = sdk.DataContract.fromJSON(contractFixtureV1, PLATFORM_VERSION_CONTRACT_V1); + const contract = sdk.DataContract.fromJSON(contractFixtureV1, FORMATS.V1.platformVersion); expect(contract).to.be.ok(); expect(contract.id()).to.equal(contractFixtureV1.id); @@ -144,7 +137,7 @@ describe('DataContract', () => { it('should create a contract with only document schemas (no tokens)', () => { // V0 fixture already has only documents, no tokens - verify it works - const contract = sdk.DataContract.fromJSON(contractFixtureV0, PLATFORM_VERSION_CONTRACT_V0); + const contract = sdk.DataContract.fromJSON(contractFixtureV0, FORMATS.V0.platformVersion); const roundTripped = contract.toJSON(); expect(roundTripped.documentSchemas.card).to.exist(); @@ -162,7 +155,7 @@ describe('DataContract', () => { const contract = sdk.DataContract.fromJSON( contractWithOnlyTokens, - PLATFORM_VERSION_CONTRACT_V1, + FORMATS.V1.platformVersion, ); const roundTripped = contract.toJSON(); @@ -175,7 +168,7 @@ describe('DataContract', () => { describe('Version Compatibility', () => { it('should fail to create a V1 contract with V0 platform version', async () => { expect(() => { - sdk.DataContract.fromJSON(contractFixtureV1, PLATFORM_VERSION_CONTRACT_V0); + sdk.DataContract.fromJSON(contractFixtureV1, FORMATS.V0.platformVersion); }).to.throw(/dpp unknown version.*known versions.*\[0\].*received.*1/); }); }); @@ -183,15 +176,15 @@ describe('DataContract', () => { describe('Validation', () => { it('should handle invalid JSON input gracefully', () => { expect(() => { - sdk.DataContract.fromJSON(null, PLATFORM_VERSION_CONTRACT_V0); + sdk.DataContract.fromJSON(null, FORMATS.V0.platformVersion); }).to.throw(); expect(() => { - sdk.DataContract.fromJSON({}, PLATFORM_VERSION_CONTRACT_V0); + sdk.DataContract.fromJSON({}, FORMATS.V0.platformVersion); }).to.throw(); expect(() => { - sdk.DataContract.fromJSON({ id: 'invalid' }, PLATFORM_VERSION_CONTRACT_V0); + sdk.DataContract.fromJSON({ id: 'invalid' }, FORMATS.V0.platformVersion); }).to.throw(); }); @@ -201,7 +194,7 @@ describe('DataContract', () => { sdk.DataContract.fromJSON({ ...contractFixtureV0, id: 'invalid-not-base58!', - }, PLATFORM_VERSION_CONTRACT_V0); + }, FORMATS.V0.platformVersion); }).to.throw(); // Test negative version number @@ -209,7 +202,7 @@ describe('DataContract', () => { sdk.DataContract.fromJSON({ ...contractFixtureV0, version: -1, - }, PLATFORM_VERSION_CONTRACT_V0); + }, FORMATS.V0.platformVersion); }).to.throw(); // Test invalid ownerId @@ -217,7 +210,7 @@ describe('DataContract', () => { sdk.DataContract.fromJSON({ ...contractFixtureV0, ownerId: 'not-a-valid-id', - }, PLATFORM_VERSION_CONTRACT_V0); + }, FORMATS.V0.platformVersion); }).to.throw(); }); @@ -232,18 +225,18 @@ describe('DataContract', () => { }; expect(() => { - sdk.DataContract.fromJSON(contractWithEmptySchemas, PLATFORM_VERSION_CONTRACT_V0); + sdk.DataContract.fromJSON(contractWithEmptySchemas, FORMATS.V0.platformVersion); }).to.throw(/must have at least one document type or token defined/); }); }); describe('Data Preservation', () => { it('should preserve all data through JSON round-trip for V0 contract', async () => { - const contract = sdk.DataContract.fromJSON(contractFixtureV0, PLATFORM_VERSION_CONTRACT_V0); + const contract = sdk.DataContract.fromJSON(contractFixtureV0, FORMATS.V0.platformVersion); const roundTripped = contract.toJSON(); // Create a new contract from the round-tripped JSON - const contract2 = sdk.DataContract.fromJSON(roundTripped, PLATFORM_VERSION_CONTRACT_V0); + const contract2 = sdk.DataContract.fromJSON(roundTripped, FORMATS.V0.platformVersion); const roundTripped2 = contract2.toJSON(); expect(roundTripped2).to.deep.equal(roundTripped); @@ -253,11 +246,11 @@ describe('DataContract', () => { }); it('should preserve all data through JSON round-trip for V1 contract', async () => { - const contract = sdk.DataContract.fromJSON(contractFixtureV1, PLATFORM_VERSION_CONTRACT_V1); + const contract = sdk.DataContract.fromJSON(contractFixtureV1, FORMATS.V1.platformVersion); const roundTripped = contract.toJSON(); // Create a new contract from the round-tripped JSON - const contract2 = sdk.DataContract.fromJSON(roundTripped, PLATFORM_VERSION_CONTRACT_V1); + const contract2 = sdk.DataContract.fromJSON(roundTripped, FORMATS.V1.platformVersion); const roundTripped2 = contract2.toJSON(); expect(roundTripped2).to.deep.equal(roundTripped); @@ -269,8 +262,8 @@ describe('DataContract', () => { describe('Memory Management', () => { it('should handle memory management properly with multiple contracts', async () => { - const contract1 = sdk.DataContract.fromJSON(contractFixtureV0, PLATFORM_VERSION_CONTRACT_V0); - const contract2 = sdk.DataContract.fromJSON(contractFixtureV1, PLATFORM_VERSION_CONTRACT_V1); + const contract1 = sdk.DataContract.fromJSON(contractFixtureV0, FORMATS.V0.platformVersion); + const contract2 = sdk.DataContract.fromJSON(contractFixtureV1, FORMATS.V1.platformVersion); expect(contract1.id()).to.equal(contractFixtureV0.id); expect(contract2.id()).to.equal(contractFixtureV1.id); From a3c6cd3c962b1c3a851289ba7e4db3d952d43383 Mon Sep 17 00:00:00 2001 From: thephez Date: Mon, 27 Oct 2025 13:40:14 -0400 Subject: [PATCH 3/5] chore: do basic config validation --- .../wasm-sdk/tests/unit/data-contract.spec.mjs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/wasm-sdk/tests/unit/data-contract.spec.mjs b/packages/wasm-sdk/tests/unit/data-contract.spec.mjs index 31a69f1949..4efff5d71d 100644 --- a/packages/wasm-sdk/tests/unit/data-contract.spec.mjs +++ b/packages/wasm-sdk/tests/unit/data-contract.spec.mjs @@ -18,6 +18,17 @@ const CONTRACT_FORMAT_VERSIONS = { V1: { introduced: 9, fixture: contractFixtureV1 }, }; +// Validate configuration +if (PLATFORM_VERSIONS.MIN >= PLATFORM_VERSIONS.MAX) { + throw new Error(`Invalid PLATFORM_VERSIONS: MIN (${PLATFORM_VERSIONS.MIN}) must be less than MAX (${PLATFORM_VERSIONS.MAX})`); +} + +Object.entries(CONTRACT_FORMAT_VERSIONS).forEach(([key, config]) => { + if (config.introduced < PLATFORM_VERSIONS.MIN || config.introduced > PLATFORM_VERSIONS.MAX) { + throw new Error(`Invalid ${key}.introduced (${config.introduced}): must be between ${PLATFORM_VERSIONS.MIN} and ${PLATFORM_VERSIONS.MAX}`); + } +}); + // Auto-generate compatibility data for all formats const FORMATS = Object.entries(CONTRACT_FORMAT_VERSIONS).reduce((acc, [formatKey, config]) => { const compatibleVersions = Array.from( @@ -25,13 +36,11 @@ const FORMATS = Object.entries(CONTRACT_FORMAT_VERSIONS).reduce((acc, [formatKey (_, i) => i + config.introduced ); - const allVersions = Array.from( - { length: PLATFORM_VERSIONS.MAX - PLATFORM_VERSIONS.MIN + 1 }, + const incompatibleVersions = Array.from( + { length: Math.max(0, config.introduced - PLATFORM_VERSIONS.MIN) }, (_, i) => i + PLATFORM_VERSIONS.MIN ); - const incompatibleVersions = allVersions.filter(v => v < config.introduced); - acc[formatKey] = { ...config, compatibleVersions, From aa5b75482c5e282bbbd21960b74646aa9111b5df Mon Sep 17 00:00:00 2001 From: thephez Date: Mon, 27 Oct 2025 13:40:32 -0400 Subject: [PATCH 4/5] chore: lint fixes --- packages/wasm-sdk/tests/unit/data-contract.spec.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/wasm-sdk/tests/unit/data-contract.spec.mjs b/packages/wasm-sdk/tests/unit/data-contract.spec.mjs index 4efff5d71d..15675dcbb0 100644 --- a/packages/wasm-sdk/tests/unit/data-contract.spec.mjs +++ b/packages/wasm-sdk/tests/unit/data-contract.spec.mjs @@ -33,12 +33,12 @@ Object.entries(CONTRACT_FORMAT_VERSIONS).forEach(([key, config]) => { const FORMATS = Object.entries(CONTRACT_FORMAT_VERSIONS).reduce((acc, [formatKey, config]) => { const compatibleVersions = Array.from( { length: PLATFORM_VERSIONS.MAX - config.introduced + 1 }, - (_, i) => i + config.introduced + (_, i) => i + config.introduced, ); const incompatibleVersions = Array.from( { length: Math.max(0, config.introduced - PLATFORM_VERSIONS.MIN) }, - (_, i) => i + PLATFORM_VERSIONS.MIN + (_, i) => i + PLATFORM_VERSIONS.MIN, ); acc[formatKey] = { @@ -290,7 +290,7 @@ describe('DataContract', () => { formatData.fixture, formatKey, formatData.compatibleVersions, - formatData.incompatibleVersions + formatData.incompatibleVersions, ); }); }); From dea43c1d7171bf46221780285908a378fc0867ac Mon Sep 17 00:00:00 2001 From: thephez Date: Mon, 27 Oct 2025 15:16:05 -0400 Subject: [PATCH 5/5] test(wasm-sdk): remove duplicate contract version boundary test The "should handle version boundary correctly at V9 transition" test duplicated coverage already provided by the dynamic compatibility matrix. The matrix already tests V0/V1 contracts at version 9 and V1 failure at version 8 through the auto-generated compatible/incompatible version loops. --- .../wasm-sdk/tests/unit/data-contract.spec.mjs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/packages/wasm-sdk/tests/unit/data-contract.spec.mjs b/packages/wasm-sdk/tests/unit/data-contract.spec.mjs index 15675dcbb0..e67798aaa3 100644 --- a/packages/wasm-sdk/tests/unit/data-contract.spec.mjs +++ b/packages/wasm-sdk/tests/unit/data-contract.spec.mjs @@ -310,23 +310,6 @@ describe('DataContract', () => { }).to.throw(/unknown version/); }); }); - - it('should handle version boundary correctly at V9 transition', () => { - // V0 contract should work in V9 (backward compatibility) - const contract = sdk.DataContract.fromJSON(contractFixtureV0, 9); - expect(contract.id()).to.equal(contractFixtureV0.id); - contract.free(); - - // V1 contract should work in V9 (first supported version) - const contractV1 = sdk.DataContract.fromJSON(contractFixtureV1, 9); - expect(contractV1.id()).to.equal(contractFixtureV1.id); - contractV1.free(); - - // V1 contract should fail in V8 (last unsupported version) - expect(() => { - sdk.DataContract.fromJSON(contractFixtureV1, 8); - }).to.throw(/dpp unknown version/); - }); }); }); });