From 862d2b94b7f6f60059447009fe028f9e67c6b2bf Mon Sep 17 00:00:00 2001 From: Liran Cohen Date: Mon, 24 Jun 2024 19:19:58 -0400 Subject: [PATCH] add tests for EventsSubscribe by protocol including permission messages --- tests/scenarios/subscriptions.spec.ts | 149 +++++++++++++++++++++----- 1 file changed, 125 insertions(+), 24 deletions(-) diff --git a/tests/scenarios/subscriptions.spec.ts b/tests/scenarios/subscriptions.spec.ts index 2643d9988..d6760b2c7 100644 --- a/tests/scenarios/subscriptions.spec.ts +++ b/tests/scenarios/subscriptions.spec.ts @@ -16,8 +16,8 @@ import { Poller } from '../utils/poller.js'; import { TestDataGenerator } from '../utils/test-data-generator.js'; import { TestEventStream } from '../test-event-stream.js'; import { TestStores } from '../test-stores.js'; +import { DataStream, Dwn, DwnInterfaceName, DwnMethodName, Jws, Message, PermissionGrant, PermissionsProtocol, Time } from '../../src/index.js'; import { DidKey, UniversalResolver } from '@web5/dids'; -import { Dwn, DwnInterfaceName, DwnMethodName, Message } from '../../src/index.js'; import { expect } from 'chai'; @@ -310,12 +310,23 @@ export function testSubscriptionScenarios(): void { }); it('filters by a protocol across different message types', async () => { + // NOTE: This test validates the ability to filter by a specific protocol across different message types. + // This will return any of the `RecordsWrite`, `RecordsDelete` and `ProtocolConfigure` messages that are associated with the protocol + // Additionally this will return permission-protocol `RecordsWrite` messages that are associated with the protocol. + // scenario: - // alice creates (3) different message types all related to "proto1" (Configure, RecordsWrite, RecordsDelete) - // alice creates (3) different message types all related to "proto2" (Configure, RecordsWrite, RecordsDelete) - // when subscribing for a specific protocol, only Messages related to it should be received by the handler. + // alice creates two different subscriptions, one for each protocol (proto1, proto2) + // alice configures the two different protocols (proto1, proto2) + // alice creates records for each protocol + // bob requests permissions for both protocols + // alice grants bob permissions for both protocols + // when checking the handler arrays for the specific protocol, only Events related to it should be present. + // alice then deletes the records for each protocol + // alice revokes bob's permissions for both protocols + // now when checking the handler arrays, the delete and revocation messages should be present const alice = await TestDataGenerator.generateDidKeyPersona(); + const bob = await TestDataGenerator.generateDidKeyPersona(); const proto1Messages:string[] = []; const proto1Handler = async (event: MessageEvent):Promise => { @@ -355,16 +366,9 @@ export function testSubscriptionScenarios(): void { protocolDefinition : { ...freeForAll, protocol: 'http://proto1' } }); - const postProperties = { - protocolPath : 'post', - schema : freeForAll.types.post.schema, - dataFormat : freeForAll.types.post.dataFormats[0], - }; - const proto1 = protoConf1.message.descriptor.definition.protocol; const protoConf1Response = await dwn.processMessage(alice.did, protoConf1.message); expect(protoConf1Response.status.code).equals(202); - const proto1ConfMessageCid = await Message.getCid(protoConf1.message); // configure proto2 const protoConf2 = await TestDataGenerator.generateProtocolsConfigure({ @@ -374,29 +378,96 @@ export function testSubscriptionScenarios(): void { const proto2 = protoConf2.message.descriptor.definition.protocol; const protoConf2Response = await dwn.processMessage(alice.did, protoConf2.message); expect(protoConf2Response.status.code).equals(202); - const proto2ConfMessageCid = await Message.getCid(protoConf2.message); + + const postProperties = { + protocolPath : 'post', + schema : freeForAll.types.post.schema, + dataFormat : freeForAll.types.post.dataFormats[0], + }; // create a record for proto1 const write1proto1 = await TestDataGenerator.generateRecordsWrite({ author: alice, protocol: proto1, ...postProperties }); const write1Response = await dwn.processMessage(alice.did, write1proto1.message, { dataStream: write1proto1.dataStream }); expect(write1Response.status.code).equals(202); - const write1Proto1MessageCid = await Message.getCid(write1proto1.message); // create a record for proto2 const write1proto2 = await TestDataGenerator.generateRecordsWrite({ author: alice, protocol: proto2, ...postProperties }); const write1Proto2Response = await dwn.processMessage(alice.did, write1proto2.message, { dataStream: write1proto2.dataStream }); expect(write1Proto2Response.status.code).equals(202); - const write1Proto2MessageCid = await Message.getCid(write1proto2.message); + + // bob requests permissions for proto1 + const requestProto1 = await PermissionsProtocol.createRequest({ + signer : Jws.createSigner(bob), + scope : { interface: DwnInterfaceName.Records, method: DwnMethodName.Write, protocol: proto1 }, + delegated : false, + }); + const requestProto1Response = await dwn.processMessage( + alice.did, + requestProto1.recordsWrite.message, + { dataStream: DataStream.fromBytes(requestProto1.permissionRequestBytes) } + ); + expect(requestProto1Response.status.code).equals(202); + + // bob requests permissions for proto2 + const requestProto2 = await PermissionsProtocol.createRequest({ + signer : Jws.createSigner(bob), + scope : { interface: DwnInterfaceName.Records, method: DwnMethodName.Write, protocol: proto2 }, + delegated : false, + }); + const requestProto2Response = await dwn.processMessage( + alice.did, + requestProto2.recordsWrite.message, + { dataStream: DataStream.fromBytes(requestProto2.permissionRequestBytes) } + ); + expect(requestProto2Response.status.code).equals(202); + + // alice grants permissions for proto1 + const grantProto1 = await PermissionsProtocol.createGrant({ + signer : Jws.createSigner(alice), + scope : requestProto1.permissionRequestData.scope, + dateExpires : Time.createOffsetTimestamp({ seconds: 5 }), + grantedTo : bob.did, + }); + const grantProto1Response = await dwn.processMessage( + alice.did, + grantProto1.recordsWrite.message, + { dataStream: DataStream.fromBytes(grantProto1.permissionGrantBytes) } + ); + expect(grantProto1Response.status.code).equals(202); + + // alice grants permissions for proto2 + const grantProto2 = await PermissionsProtocol.createGrant({ + signer : Jws.createSigner(alice), + scope : requestProto2.permissionRequestData.scope, + dateExpires : Time.createOffsetTimestamp({ seconds: 5 }), + grantedTo : bob.did, + }); + const grantProto2Response = await dwn.processMessage( + alice.did, + grantProto2.recordsWrite.message, + { dataStream: DataStream.fromBytes(grantProto2.permissionGrantBytes) } + ); + expect(grantProto2Response.status.code).equals(202); // poll until the messages are received by the handlers await Poller.pollUntilSuccessOrTimeout(async () => { // check for proto1 messages - expect(proto1Messages.length).to.equal(2); - expect(proto1Messages).to.have.members([ proto1ConfMessageCid, write1Proto1MessageCid ]); + expect(proto1Messages.length).to.equal(4); + expect(proto1Messages).to.have.members([ + await Message.getCid(protoConf1.message), + await Message.getCid(write1proto1.message), + await Message.getCid(requestProto1.recordsWrite.message), + await Message.getCid(grantProto1.recordsWrite.message), + ]); // check for proto2 messages - expect(proto2Messages.length).to.equal(2); - expect(proto2Messages).to.have.members([ proto2ConfMessageCid, write1Proto2MessageCid ]); + expect(proto2Messages.length).to.equal(4); + expect(proto2Messages).to.have.members([ + await Message.getCid(protoConf2.message), + await Message.getCid(write1proto2.message), + await Message.getCid(requestProto2.recordsWrite.message), + await Message.getCid(grantProto2.recordsWrite.message), + ]); }); // delete proto1 message @@ -409,15 +480,45 @@ export function testSubscriptionScenarios(): void { const deleteProto2MessageReply = await dwn.processMessage(alice.did, deleteProto2Message.message); expect(deleteProto2MessageReply.status.code).to.equal(202); + // revoke permissions for proto1 + const revokeProto1 = await PermissionsProtocol.createRevocation({ + signer : Jws.createSigner(alice), + grant : await PermissionGrant.parse(grantProto1.dataEncodedMessage), + }); + const revokeProto1Response = await dwn.processMessage( + alice.did, + revokeProto1.recordsWrite.message, + { dataStream: DataStream.fromBytes(revokeProto1.permissionRevocationBytes) } + ); + expect(revokeProto1Response.status.code).equals(202); + + // revoke permissions for proto2 + const revokeProto2 = await PermissionsProtocol.createRevocation({ + signer : Jws.createSigner(alice), + grant : await PermissionGrant.parse(grantProto2.dataEncodedMessage), + }); + const revokeProto2Response = await dwn.processMessage( + alice.did, + revokeProto2.recordsWrite.message, + { dataStream: DataStream.fromBytes(revokeProto2.permissionRevocationBytes) } + ); + expect(revokeProto2Response.status.code).equals(202); + // poll until the messages are received by the handlers await Poller.pollUntilSuccessOrTimeout(async () => { - // check for the delete in proto1 messages - expect(proto1Messages.length).to.equal(3); - expect(proto1Messages).to.include.members([ await Message.getCid(deleteProto1Message.message) ]); + // check for the delete and revocation in proto1 messages + expect(proto1Messages.length).to.equal(6); // 2 additional messages + expect(proto1Messages).to.include.members([ + await Message.getCid(deleteProto1Message.message), + await Message.getCid(revokeProto1.recordsWrite.message), + ]); - // check for the delete in proto2 messages - expect(proto2Messages.length).to.equal(3); - expect(proto2Messages).to.include.members([ await Message.getCid(deleteProto2Message.message) ]); + // check for the delete and revocation in proto2 messages + expect(proto2Messages.length).to.equal(6); // 2 additional messages + expect(proto2Messages).to.include.members([ + await Message.getCid(deleteProto2Message.message), + await Message.getCid(revokeProto2.recordsWrite.message), + ]); }); });