From eb8e8e6dc7c8d756fe267043832b53dd18c05747 Mon Sep 17 00:00:00 2001 From: pasta Date: Thu, 28 May 2026 13:13:50 -0500 Subject: [PATCH 1/2] feat(js-evo-sdk): expose document count/sum/average aggregate query family Surface the SUM/AVG/COUNT aggregate queries (#3457, #3661, #3652) through DocumentsFacade so consumers no longer need to drop down to getWasmSdkConnected() to reach them. Adds six thin-delegate methods (count/countWithProof, sum/sumWithProof, average/averageWithProof) mirroring the existing query/queryWithProof convention. Sinon-stub proxy tests cover argument propagation. README updated to mention the new aggregate surface. --- packages/js-evo-sdk/README.md | 2 +- packages/js-evo-sdk/src/documents/facade.ts | 44 +++++++ .../tests/unit/facades/documents.spec.ts | 113 ++++++++++++++++++ 3 files changed, 158 insertions(+), 1 deletion(-) diff --git a/packages/js-evo-sdk/README.md b/packages/js-evo-sdk/README.md index 324a6c05413..d23ffe79fab 100644 --- a/packages/js-evo-sdk/README.md +++ b/packages/js-evo-sdk/README.md @@ -67,7 +67,7 @@ The SDK organises its API into domain-specific facades, each accessible as a pro |--------|-------------| | [`sdk.addresses`](src/addresses/facade.ts) | Query balances, transfer credits, withdraw to L1 | | [`sdk.identities`](src/identities/facade.ts) | Fetch, create, update, and top up identities | -| [`sdk.documents`](src/documents/facade.ts) | Query, create, replace, delete, and transfer documents | +| [`sdk.documents`](src/documents/facade.ts) | Query, create, replace, delete, and transfer documents; aggregate `count` / `sum` / `average` over indexed fields | | [`sdk.contracts`](src/contracts/facade.ts) | Fetch, publish, and update data contracts | | [`sdk.tokens`](src/tokens/facade.ts) | Mint, burn, transfer, freeze tokens and query balances | | [`sdk.dpns`](src/dpns/facade.ts) | Register and resolve Dash Platform names | diff --git a/packages/js-evo-sdk/src/documents/facade.ts b/packages/js-evo-sdk/src/documents/facade.ts index 3c3acc66dda..b0cb314fe3a 100644 --- a/packages/js-evo-sdk/src/documents/facade.ts +++ b/packages/js-evo-sdk/src/documents/facade.ts @@ -67,4 +67,48 @@ export class DocumentsFacade { const w = await this.sdk.getWasmSdkConnected(); return w.documentSetPrice(options); } + + async count(query: wasm.DocumentsQuery): Promise> { + const w = await this.sdk.getWasmSdkConnected(); + return w.getDocumentsCount(query); + } + + async countWithProof( + query: wasm.DocumentsQuery, + ): Promise>> { + const w = await this.sdk.getWasmSdkConnected(); + return w.getDocumentsCountWithProofInfo(query); + } + + async sum( + query: wasm.DocumentsQuery, + sumProperty: string, + ): Promise> { + const w = await this.sdk.getWasmSdkConnected(); + return w.getDocumentsSum(query, sumProperty); + } + + async sumWithProof( + query: wasm.DocumentsQuery, + sumProperty: string, + ): Promise>> { + const w = await this.sdk.getWasmSdkConnected(); + return w.getDocumentsSumWithProofInfo(query, sumProperty); + } + + async average( + query: wasm.DocumentsQuery, + averageProperty: string, + ): Promise> { + const w = await this.sdk.getWasmSdkConnected(); + return w.getDocumentsAverage(query, averageProperty); + } + + async averageWithProof( + query: wasm.DocumentsQuery, + averageProperty: string, + ): Promise>> { + const w = await this.sdk.getWasmSdkConnected(); + return w.getDocumentsAverageWithProofInfo(query, averageProperty); + } } diff --git a/packages/js-evo-sdk/tests/unit/facades/documents.spec.ts b/packages/js-evo-sdk/tests/unit/facades/documents.spec.ts index 3e776ffa403..4b060b3addb 100644 --- a/packages/js-evo-sdk/tests/unit/facades/documents.spec.ts +++ b/packages/js-evo-sdk/tests/unit/facades/documents.spec.ts @@ -27,6 +27,12 @@ describe('DocumentsFacade', () => { let documentTransferStub: SinonStub; let documentPurchaseStub: SinonStub; let documentSetPriceStub: SinonStub; + let getDocumentsCountStub: SinonStub; + let getDocumentsCountWithProofInfoStub: SinonStub; + let getDocumentsSumStub: SinonStub; + let getDocumentsSumWithProofInfoStub: SinonStub; + let getDocumentsAverageStub: SinonStub; + let getDocumentsAverageWithProofInfoStub: SinonStub; beforeEach(async function setup() { await init(); @@ -60,6 +66,26 @@ describe('DocumentsFacade', () => { documentTransferStub = this.sinon.stub(wasmSdk, 'documentTransfer').resolves(); documentPurchaseStub = this.sinon.stub(wasmSdk, 'documentPurchase').resolves(); documentSetPriceStub = this.sinon.stub(wasmSdk, 'documentSetPrice').resolves(); + + // Stub aggregate query methods + getDocumentsCountStub = this.sinon.stub(wasmSdk, 'getDocumentsCount').resolves(new Map()); + getDocumentsCountWithProofInfoStub = this.sinon.stub(wasmSdk, 'getDocumentsCountWithProofInfo').resolves({ + data: new Map(), + proof: {}, + metadata: {}, + }); + getDocumentsSumStub = this.sinon.stub(wasmSdk, 'getDocumentsSum').resolves(new Map()); + getDocumentsSumWithProofInfoStub = this.sinon.stub(wasmSdk, 'getDocumentsSumWithProofInfo').resolves({ + data: new Map(), + proof: {}, + metadata: {}, + }); + getDocumentsAverageStub = this.sinon.stub(wasmSdk, 'getDocumentsAverage').resolves(new Map()); + getDocumentsAverageWithProofInfoStub = this.sinon.stub(wasmSdk, 'getDocumentsAverageWithProofInfo').resolves({ + data: new Map(), + proof: {}, + metadata: {}, + }); }); describe('query()', () => { @@ -231,4 +257,91 @@ describe('DocumentsFacade', () => { expect(documentSetPriceStub).to.be.calledOnceWithExactly(options); }); }); + + describe('count()', () => { + it('should count documents matching a query', async () => { + const query = { + dataContractId: 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec', + documentTypeName: 'grade', + }; + + await client.documents.count(query); + + expect(getDocumentsCountStub).to.be.calledOnceWithExactly(query); + }); + }); + + describe('countWithProof()', () => { + it('should count documents and return proof metadata', async () => { + const query = { + dataContractId: 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec', + documentTypeName: 'grade', + where: [['class', '==', 'CS101']], + }; + + await client.documents.countWithProof(query); + + expect(getDocumentsCountWithProofInfoStub).to.be.calledOnceWithExactly(query); + }); + }); + + describe('sum()', () => { + it('should aggregate a summable property across matching documents', async () => { + const query = { + dataContractId: 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec', + documentTypeName: 'grade', + where: [['semester', '==', 20251]], + }; + const sumProperty = 'score'; + + await client.documents.sum(query, sumProperty); + + expect(getDocumentsSumStub).to.be.calledOnceWithExactly(query, sumProperty); + }); + }); + + describe('sumWithProof()', () => { + it('should aggregate a summable property with proof metadata', async () => { + const query = { + dataContractId: 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec', + documentTypeName: 'grade', + }; + const sumProperty = 'score'; + + await client.documents.sumWithProof(query, sumProperty); + + expect(getDocumentsSumWithProofInfoStub).to.be.calledOnceWithExactly(query, sumProperty); + }); + }); + + describe('average()', () => { + it('should return the (count, sum) pair for a summable property', async () => { + const query = { + dataContractId: 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec', + documentTypeName: 'grade', + groupBy: ['class'], + }; + const sumProperty = 'score'; + + await client.documents.average(query, sumProperty); + + expect(getDocumentsAverageStub).to.be.calledOnceWithExactly(query, sumProperty); + }); + }); + + describe('averageWithProof()', () => { + it('should return the (count, sum) pair with proof metadata', async () => { + const query = { + dataContractId: 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec', + documentTypeName: 'grade', + where: [['class', '==', 'CS101']], + groupBy: ['semester'], + }; + const sumProperty = 'score'; + + await client.documents.averageWithProof(query, sumProperty); + + expect(getDocumentsAverageWithProofInfoStub).to.be.calledOnceWithExactly(query, sumProperty); + }); + }); }); From 4aefb0d864c6dacccc4bd601460f030b02e6f785 Mon Sep 17 00:00:00 2001 From: pasta Date: Thu, 28 May 2026 13:19:55 -0500 Subject: [PATCH 2/2] test(js-evo-sdk): align average-test local var name with renamed facade param MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trailing follow-up to the averageProperty rename on DocumentsFacade.average / averageWithProof — the local variable in the spec was still named sumProperty. No behavior change (positional arg). --- .../js-evo-sdk/tests/unit/facades/documents.spec.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/js-evo-sdk/tests/unit/facades/documents.spec.ts b/packages/js-evo-sdk/tests/unit/facades/documents.spec.ts index 4b060b3addb..a5bc6a93fb7 100644 --- a/packages/js-evo-sdk/tests/unit/facades/documents.spec.ts +++ b/packages/js-evo-sdk/tests/unit/facades/documents.spec.ts @@ -321,11 +321,11 @@ describe('DocumentsFacade', () => { documentTypeName: 'grade', groupBy: ['class'], }; - const sumProperty = 'score'; + const averageProperty = 'score'; - await client.documents.average(query, sumProperty); + await client.documents.average(query, averageProperty); - expect(getDocumentsAverageStub).to.be.calledOnceWithExactly(query, sumProperty); + expect(getDocumentsAverageStub).to.be.calledOnceWithExactly(query, averageProperty); }); }); @@ -337,11 +337,11 @@ describe('DocumentsFacade', () => { where: [['class', '==', 'CS101']], groupBy: ['semester'], }; - const sumProperty = 'score'; + const averageProperty = 'score'; - await client.documents.averageWithProof(query, sumProperty); + await client.documents.averageWithProof(query, averageProperty); - expect(getDocumentsAverageWithProofInfoStub).to.be.calledOnceWithExactly(query, sumProperty); + expect(getDocumentsAverageWithProofInfoStub).to.be.calledOnceWithExactly(query, averageProperty); }); }); });