From ea0016a4eeef54d9f48d0d1a80bc2d66f1ce40d2 Mon Sep 17 00:00:00 2001 From: Liran Cohen Date: Wed, 21 Aug 2024 19:29:55 -0400 Subject: [PATCH] increase testing for sync edge cases --- packages/agent/src/sync-engine-level.ts | 6 +- .../agent/tests/sync-engine-level.spec.ts | 250 ++++++++++++++++++ 2 files changed, 253 insertions(+), 3 deletions(-) diff --git a/packages/agent/src/sync-engine-level.ts b/packages/agent/src/sync-engine-level.ts index 181ade681..259667cb8 100644 --- a/packages/agent/src/sync-engine-level.ts +++ b/packages/agent/src/sync-engine-level.ts @@ -143,7 +143,7 @@ export class SyncEngineLevel implements SyncEngine { permissionGrantId = messagesReadGrant.grant.id; granteeDid = delegateDid; } catch(error:any) { - console.log('SyncEngineLevel: Error fetching permission grant for delegate DID', error); + console.error('SyncEngineLevel: pull - Error fetching MessagesRead permission grant for delegate DID', error); continue; } } @@ -407,7 +407,7 @@ export class SyncEngineLevel implements SyncEngine { permissionGrantId = messagesQueryGrant.grant.id; } catch(error:any) { - console.log('SyncEngineLevel: Error fetching permission grant for delegate DID', error); + console.error('SyncEngineLevel: Error fetching MessagesQuery permission grant for delegate DID', error); return []; } } @@ -474,7 +474,7 @@ export class SyncEngineLevel implements SyncEngine { permissionGrantId = messagesReadGrant.grant.id; } catch(error:any) { - console.log('SyncEngineLevel: Error fetching permission grant for delegate DID', error); + console.error('SyncEngineLevel: push - Error fetching MessagesRead permission grant for delegate DID', error); return; } } diff --git a/packages/agent/tests/sync-engine-level.spec.ts b/packages/agent/tests/sync-engine-level.spec.ts index 5ff25fca6..7365148a6 100644 --- a/packages/agent/tests/sync-engine-level.spec.ts +++ b/packages/agent/tests/sync-engine-level.spec.ts @@ -1397,6 +1397,136 @@ describe('SyncEngineLevel', () => { sendDwnRequestSpy.restore(); }); + it('logs an error if could not fetch MessagesQuery permission needed for a sync', async () => { + // create new identity to not conflict the previous tests's remote records + const aliceSync = await testHarness.createIdentity({ name: 'Alice', testDwnUrls }); + + const delegateDid = await testHarness.agent.identity.create({ + store : true, + didMethod : 'jwk', + metadata : { name: 'Alice Delegate', connectedDid: aliceSync.did.uri } + }); + + await testHarness.agent.sync.registerIdentity({ + did : aliceSync.did.uri, + options : { + delegateDid : delegateDid.did.uri, + protocols : [ 'https://protocol.xyz/foo' ] + } + }); + + // spy on console.error to check if the error message is logged + const consoleErrorSpy = sinon.stub(console, 'error').resolves(); + + await syncEngine.sync('pull'); + expect(consoleErrorSpy.called).to.be.true; + expect(consoleErrorSpy.args[0][0]).to.include('SyncEngineLevel: Error fetching MessagesQuery permission grant for delegate DID'); + }); + + it('logs an error if could not fetch MessagesRead permission needed for a sync', async () => { + // create new identity to not conflict the previous tests's remote records + const aliceSync = await testHarness.createIdentity({ name: 'Alice', testDwnUrls }); + + // create 3 local protocols + const protocolFoo: ProtocolDefinition = { + published : true, + protocol : 'https://protocol.xyz/foo', + types : { + foo: { + schema : 'https://schemas.xyz/foo', + dataFormats : ['text/plain', 'application/json'] + } + }, + structure: { + foo: {} + } + }; + + // install a protocol on the remote node for aliceSync + const protocolsFoo = await testHarness.agent.sendDwnRequest({ + author : aliceSync.did.uri, + target : aliceSync.did.uri, + messageType : DwnInterface.ProtocolsConfigure, + messageParams : { + definition: protocolFoo + } + }); + expect(protocolsFoo.reply.status.code).to.equal(202); + + + // create a record that will be read as a part of sync + const record1 = await testHarness.agent.sendDwnRequest({ + author : aliceSync.did.uri, + target : aliceSync.did.uri, + messageType : DwnInterface.RecordsWrite, + messageParams : { + protocol : 'https://protocol.xyz/foo', + protocolPath : 'foo', + schema : 'https://schemas.xyz/foo', + dataFormat : 'text/plain', + }, + dataStream: new Blob(['Hello, world!']) + }); + expect(record1.reply.status.code).to.equal(202); + + + const delegateDid = await testHarness.agent.identity.create({ + store : true, + didMethod : 'jwk', + metadata : { name: 'Alice Delegate', connectedDid: aliceSync.did.uri } + }); + + // write a MessagesQuery permission grant for the delegate DID + const messagesQueryGrant = await testHarness.agent.permissions.createGrant({ + store : true, + author : aliceSync.did.uri, + grantedTo : delegateDid.did.uri, + dateExpires : Time.createOffsetTimestamp({ seconds: 60 }), + scope : { + interface : DwnInterfaceName.Messages, + method : DwnMethodName.Query, + protocol : 'https://protocol.xyz/foo' + } + }); + + const { encodedData: messagesQueryGrantData, ...messagesQueryGrantMessage } = messagesQueryGrant.message; + // send to the remote node + const sendGrant = await testHarness.agent.sendDwnRequest({ + author : aliceSync.did.uri, + target : aliceSync.did.uri, + messageType : DwnInterface.RecordsWrite, + rawMessage : messagesQueryGrantMessage, + dataStream : new Blob([ Convert.base64Url(messagesQueryGrantData).toUint8Array() ]), + }); + expect(sendGrant.reply.status.code).to.equal(202); + + // store it as the delegate DID so that it can be fetched during sync + const processGrant = await testHarness.agent.processDwnRequest({ + author : delegateDid.did.uri, + target : delegateDid.did.uri, + messageType : DwnInterface.RecordsWrite, + rawMessage : messagesQueryGrantMessage, + dataStream : new Blob([ Convert.base64Url(messagesQueryGrantData).toUint8Array() ]), + signAsOwner : true + }); + expect(processGrant.reply.status.code).to.equal(202); + + await testHarness.agent.sync.registerIdentity({ + did : aliceSync.did.uri, + options : { + delegateDid : delegateDid.did.uri, + protocols : [ 'https://protocol.xyz/foo' ] + } + }); + + // spy on console.error to check if the error message is logged + const consoleErrorSpy = sinon.stub(console, 'error').resolves(); + + await syncEngine.sync('pull'); + expect(consoleErrorSpy.called).to.be.true; + expect(consoleErrorSpy.args[0][0]).to.include('SyncEngineLevel: pull - Error fetching MessagesRead permission grant for delegate DID'); + }); + it('synchronizes records for 1 identity from remote DWN to local DWN', async () => { // Write a test record to Alice's remote DWN. let writeResponse = await testHarness.agent.dwn.sendRequest({ @@ -1973,6 +2103,126 @@ describe('SyncEngineLevel', () => { processRequestSpy.restore(); }); + it('logs an error if could not fetch MessagesQuery permission needed for a sync', async () => { + // create new identity to not conflict the previous tests's remote records + const aliceSync = await testHarness.createIdentity({ name: 'Alice', testDwnUrls }); + + const delegateDid = await testHarness.agent.identity.create({ + store : true, + didMethod : 'jwk', + metadata : { name: 'Alice Delegate', connectedDid: aliceSync.did.uri } + }); + + await testHarness.agent.sync.registerIdentity({ + did : aliceSync.did.uri, + options : { + delegateDid : delegateDid.did.uri, + protocols : [ 'https://protocol.xyz/foo' ] + } + }); + + // spy on console.error to check if the error message is logged + const consoleErrorSpy = sinon.stub(console, 'error').resolves(); + + await syncEngine.sync('push'); + expect(consoleErrorSpy.called).to.be.true; + expect(consoleErrorSpy.args[0][0]).to.include('SyncEngineLevel: Error fetching MessagesQuery permission grant for delegate DID'); + }); + + it('logs an error if could not fetch MessagesRead permission needed for a sync', async () => { + // create new identity to not conflict the previous tests's remote records + const aliceSync = await testHarness.createIdentity({ name: 'Alice', testDwnUrls }); + + // create 3 local protocols + const protocolFoo: ProtocolDefinition = { + published : true, + protocol : 'https://protocol.xyz/foo', + types : { + foo: { + schema : 'https://schemas.xyz/foo', + dataFormats : ['text/plain', 'application/json'] + } + }, + structure: { + foo: {} + } + }; + + // install a protocol on the local node for aliceSync + const protocolsFoo = await testHarness.agent.processDwnRequest({ + author : aliceSync.did.uri, + target : aliceSync.did.uri, + messageType : DwnInterface.ProtocolsConfigure, + messageParams : { + definition: protocolFoo + } + }); + expect(protocolsFoo.reply.status.code).to.equal(202); + + + // create a record that will be read as a part of sync + const record1 = await testHarness.agent.processDwnRequest({ + author : aliceSync.did.uri, + target : aliceSync.did.uri, + messageType : DwnInterface.RecordsWrite, + messageParams : { + protocol : 'https://protocol.xyz/foo', + protocolPath : 'foo', + schema : 'https://schemas.xyz/foo', + dataFormat : 'text/plain', + }, + dataStream: new Blob(['Hello, world!']) + }); + expect(record1.reply.status.code).to.equal(202); + + + const delegateDid = await testHarness.agent.identity.create({ + store : true, + didMethod : 'jwk', + metadata : { name: 'Alice Delegate', connectedDid: aliceSync.did.uri } + }); + + // write a MessagesQuery permission grant for the delegate DID + const messagesQueryGrant = await testHarness.agent.permissions.createGrant({ + store : true, + author : aliceSync.did.uri, + grantedTo : delegateDid.did.uri, + dateExpires : Time.createOffsetTimestamp({ seconds: 60 }), + scope : { + interface : DwnInterfaceName.Messages, + method : DwnMethodName.Query, + protocol : 'https://protocol.xyz/foo' + } + }); + + // store it as the delegate DID so that it can be fetched during sync + const { encodedData: messagesQueryGrantData, ...messagesQueryGrantMessage } = messagesQueryGrant.message; + const processGrant = await testHarness.agent.processDwnRequest({ + author : delegateDid.did.uri, + target : delegateDid.did.uri, + messageType : DwnInterface.RecordsWrite, + rawMessage : messagesQueryGrantMessage, + dataStream : new Blob([ Convert.base64Url(messagesQueryGrantData).toUint8Array() ]), + signAsOwner : true + }); + expect(processGrant.reply.status.code).to.equal(202); + + await testHarness.agent.sync.registerIdentity({ + did : aliceSync.did.uri, + options : { + delegateDid : delegateDid.did.uri, + protocols : [ 'https://protocol.xyz/foo' ] + } + }); + + // spy on console.error to check if the error message is logged + const consoleErrorSpy = sinon.stub(console, 'error').resolves(); + + await syncEngine.sync('push'); + expect(consoleErrorSpy.called).to.be.true; + expect(consoleErrorSpy.args[0][0]).to.include('SyncEngineLevel: push - Error fetching MessagesRead permission grant for delegate DID'); + }); + it('synchronizes records for 1 identity from local DWN to remote DWN', async () => { // Write a record that we can use for this test. let writeResponse = await testHarness.agent.dwn.processRequest({