Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
274 changes: 264 additions & 10 deletions packages/wasm-sdk/tests/unit/data-contract.spec.mjs
Original file line number Diff line number Diff line change
@@ -1,23 +1,277 @@
import init, * as sdk from '../../dist/sdk.compressed.js';
import contractFixture from './fixtures/data-contract-crypto-card-game.mjs';
import contractFixtureV0 from './fixtures/data-contract-v0-crypto-card-game.mjs';
import contractFixtureV1 from './fixtures/data-contract-v1-with-docs-tokens-groups.mjs';

const PLATFORM_VERSION = 1;
// Platform version constants
const PLATFORM_VERSION_CONTRACT_V0 = 1;
const PLATFORM_VERSION_CONTRACT_V1 = 9; // V1 contracts introduced in Platform v9

// 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+
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would do a 9+ instead of a 9, 10, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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); // Dynamically determine latest version

Check failure on line 13 in packages/wasm-sdk/tests/unit/data-contract.spec.mjs

View workflow job for this annotation

GitHub Actions / JS packages (@dashevo/wasm-sdk) / Linting

This line has a length of 105. Maximum allowed is 100

// Helper function for testing contract compatibility across versions
const testContractAcrossVersions = (contractFixture, contractName, compatibleVersions, incompatibleVersions = []) => {

Check failure on line 16 in packages/wasm-sdk/tests/unit/data-contract.spec.mjs

View workflow job for this annotation

GitHub Actions / JS packages (@dashevo/wasm-sdk) / Linting

This line has a length of 118. Maximum allowed is 100
compatibleVersions.forEach(version => {

Check failure on line 17 in packages/wasm-sdk/tests/unit/data-contract.spec.mjs

View workflow job for this annotation

GitHub Actions / JS packages (@dashevo/wasm-sdk) / Linting

Expected parentheses around arrow function argument
it(`should work with platform version ${version}`, () => {
const contract = sdk.DataContract.fromJSON(contractFixture, version);
expect(contract).to.be.ok();
expect(contract.id()).to.equal(contractFixture.id);

const roundTripped = contract.toJSON();
expect(roundTripped.id).to.equal(contractFixture.id);

contract.free();
});
});

incompatibleVersions.forEach(version => {

Check failure on line 30 in packages/wasm-sdk/tests/unit/data-contract.spec.mjs

View workflow job for this annotation

GitHub Actions / JS packages (@dashevo/wasm-sdk) / Linting

Expected parentheses around arrow function argument
it(`should fail with platform version ${version}`, () => {
expect(() => {
sdk.DataContract.fromJSON(contractFixture, version);
}).to.throw(/unknown version|dpp unknown version/);
});
});
};

describe('DataContract', () => {
before(async () => {
await init();
});

it('should create a contract from JSON and expose identifiers', async () => {
const contract = sdk.DataContract.fromJSON(contractFixture, PLATFORM_VERSION);
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);

expect(contract).to.be.ok();
expect(contract.id()).to.equal(contractFixtureV0.id);

const roundTripped = contract.toJSON();
expect(roundTripped).to.be.an('object');
expect(roundTripped.id).to.equal(contractFixtureV0.id);
expect(roundTripped.ownerId).to.equal(contractFixtureV0.ownerId);
expect(roundTripped.version).to.equal(contractFixtureV0.version);
expect(roundTripped.$format_version).to.equal(contractFixtureV0.$format_version);
expect(roundTripped.config).to.deep.equal(contractFixtureV0.config);
expect(roundTripped.documentSchemas).to.deep.equal(contractFixtureV0.documentSchemas);

// Verify document schema structure
expect(roundTripped.documentSchemas.card).to.exist;

Check failure on line 61 in packages/wasm-sdk/tests/unit/data-contract.spec.mjs

View workflow job for this annotation

GitHub Actions / JS packages (@dashevo/wasm-sdk) / Linting

Expected an assignment or function call and instead saw an expression
expect(roundTripped.documentSchemas.card.properties.name).to.exist;

Check failure on line 62 in packages/wasm-sdk/tests/unit/data-contract.spec.mjs

View workflow job for this annotation

GitHub Actions / JS packages (@dashevo/wasm-sdk) / Linting

Expected an assignment or function call and instead saw an expression
expect(roundTripped.documentSchemas.card.properties.rarity.enum).to.deep.equal(['common', 'uncommon', 'rare', 'legendary']);
expect(roundTripped.documentSchemas.card.indices).to.have.length(2);

contract.free();
});

it('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);

expect(contract).to.be.ok();
expect(contract.id()).to.equal(contractFixtureV1.id);

const roundTripped = contract.toJSON();
expect(roundTripped).to.be.an('object');
expect(roundTripped.id).to.equal(contractFixtureV1.id);
expect(roundTripped.ownerId).to.equal(contractFixtureV1.ownerId);
expect(roundTripped.version).to.equal(contractFixtureV1.version);
expect(roundTripped.$format_version).to.equal(contractFixtureV1.$format_version);
expect(roundTripped.config.sizedIntegerTypes).to.be.true;

Check failure on line 81 in packages/wasm-sdk/tests/unit/data-contract.spec.mjs

View workflow job for this annotation

GitHub Actions / JS packages (@dashevo/wasm-sdk) / Linting

Expected an assignment or function call and instead saw an expression
expect(roundTripped.documentSchemas).to.deep.equal(contractFixtureV1.documentSchemas);

// Verify V1-specific features
expect(roundTripped.tokens).to.exist;

Check failure on line 85 in packages/wasm-sdk/tests/unit/data-contract.spec.mjs

View workflow job for this annotation

GitHub Actions / JS packages (@dashevo/wasm-sdk) / Linting

Expected an assignment or function call and instead saw an expression
expect(roundTripped.tokens['0']).to.exist;

Check failure on line 86 in packages/wasm-sdk/tests/unit/data-contract.spec.mjs

View workflow job for this annotation

GitHub Actions / JS packages (@dashevo/wasm-sdk) / Linting

Expected an assignment or function call and instead saw an expression
expect(roundTripped.tokens['0'].baseSupply).to.equal(100);
expect(roundTripped.tokens['0'].conventions.decimals).to.equal(0);

expect(roundTripped.groups).to.exist;

Check failure on line 90 in packages/wasm-sdk/tests/unit/data-contract.spec.mjs

View workflow job for this annotation

GitHub Actions / JS packages (@dashevo/wasm-sdk) / Linting

Expected an assignment or function call and instead saw an expression
expect(roundTripped.groups['0']).to.exist;
expect(roundTripped.groups['0'].required_power).to.equal(2);

expect(roundTripped.keywords).to.deep.equal(contractFixtureV1.keywords);

contract.free();
});

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 roundTripped = contract.toJSON();

expect(roundTripped.documentSchemas.card).to.exist;
expect(roundTripped.tokens).to.be.undefined;

contract.free();
});

it('should create a contract with only tokens (no documents)', () => {
// Use V1 fixture but remove documentSchemas
const contractWithOnlyTokens = {
...contractFixtureV1,
documentSchemas: {}
};

const contract = sdk.DataContract.fromJSON(contractWithOnlyTokens, PLATFORM_VERSION_CONTRACT_V1);
const roundTripped = contract.toJSON();

expect(roundTripped.documentSchemas).to.deep.equal({});

contract.free();
});
});

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);
}).to.throw(/dpp unknown version.*known versions.*\[0\].*received.*1/);
});
});

describe('Validation', () => {
it('should handle invalid JSON input gracefully', () => {
expect(() => {
sdk.DataContract.fromJSON(null, PLATFORM_VERSION_CONTRACT_V0);
}).to.throw();

expect(() => {
sdk.DataContract.fromJSON({}, PLATFORM_VERSION_CONTRACT_V0);
}).to.throw();

expect(() => {
sdk.DataContract.fromJSON({ id: 'invalid' }, PLATFORM_VERSION_CONTRACT_V0);
}).to.throw();
});

it('should reject contracts with invalid property values', () => {
// Test invalid Base58 ID
expect(() => {
sdk.DataContract.fromJSON({
...contractFixtureV0,
id: 'invalid-not-base58!'
}, PLATFORM_VERSION_CONTRACT_V0);
}).to.throw();

// Test negative version number
expect(() => {
sdk.DataContract.fromJSON({
...contractFixtureV0,
version: -1
}, PLATFORM_VERSION_CONTRACT_V0);
}).to.throw();

// Test invalid ownerId
expect(() => {
sdk.DataContract.fromJSON({
...contractFixtureV0,
ownerId: 'not-a-valid-id'
}, PLATFORM_VERSION_CONTRACT_V0);
}).to.throw();
});

it('should require at least one document type or token', () => {
const contractWithEmptySchemas = {
$format_version: '0',
id: contractFixtureV0.id,
ownerId: contractFixtureV0.ownerId,
version: 1,
config: contractFixtureV0.config,
documentSchemas: {}
};

expect(() => {
sdk.DataContract.fromJSON(contractWithEmptySchemas, PLATFORM_VERSION_CONTRACT_V0);
}).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 roundTripped = contract.toJSON();

// Create a new contract from the round-tripped JSON
const contract2 = sdk.DataContract.fromJSON(roundTripped, PLATFORM_VERSION_CONTRACT_V0);
const roundTripped2 = contract2.toJSON();

expect(roundTripped2).to.deep.equal(roundTripped);

contract.free();
contract2.free();
});

it('should preserve all data through JSON round-trip for V1 contract', async () => {
const contract = sdk.DataContract.fromJSON(contractFixtureV1, PLATFORM_VERSION_CONTRACT_V1);
const roundTripped = contract.toJSON();

// Create a new contract from the round-tripped JSON
const contract2 = sdk.DataContract.fromJSON(roundTripped, PLATFORM_VERSION_CONTRACT_V1);
const roundTripped2 = contract2.toJSON();

expect(roundTripped2).to.deep.equal(roundTripped);

contract.free();
contract2.free();
});
});

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);

expect(contract1.id()).to.equal(contractFixtureV0.id);
expect(contract2.id()).to.equal(contractFixtureV1.id);

contract1.free();
contract2.free();
});
});

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);
});

describe('Edge Cases', () => {
it('should fail with invalid version numbers', () => {
const invalidVersions = [
0, // Zero version
-1, // Negative version
LATEST_KNOWN_VERSION + 1, // One beyond latest known
LATEST_KNOWN_VERSION * 10 // Far beyond reasonable range
];

invalidVersions.forEach(version => {
expect(() => {
sdk.DataContract.fromJSON(contractFixtureV0, version);
}).to.throw(/unknown version/);
});
});

expect(contract).to.be.ok();
expect(contract.id()).to.equal(contractFixture.id);
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();

const roundTripped = contract.toJSON();
expect(roundTripped).to.be.an('object');
expect(roundTripped.id).to.equal(contractFixture.id);
// 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();

contract.free();
// V1 contract should fail in V8 (last unsupported version)
expect(() => {
sdk.DataContract.fromJSON(contractFixtureV1, 8);
}).to.throw(/dpp unknown version/);
});
});
});
});
Loading
Loading