diff --git a/examples/integration-scripts/challenge.test.ts b/examples/integration-scripts/challenge.test.ts index 12aa4a56..94c57f5e 100644 --- a/examples/integration-scripts/challenge.test.ts +++ b/examples/integration-scripts/challenge.test.ts @@ -1,7 +1,11 @@ import { strict as assert } from 'assert'; import signify, { Serder } from 'signify-ts'; import { resolveEnvironment } from './utils/resolve-env'; -import { resolveOobi, waitOperation } from './utils/test-util'; +import { + assertOperations, + resolveOobi, + waitOperation, +} from './utils/test-util'; const { url, bootUrl } = resolveEnvironment(); @@ -59,9 +63,10 @@ test('challenge', async () => { client1, await icpResult1.op() ); - await client1 + let rpyResult1 = await client1 .identifiers() .addEndRole('alice', 'agent', client1!.agent!.pre); + await waitOperation(client1, await rpyResult1.op()); console.log("Alice's AID:", aid1.i); const icpResult2 = await client2.identifiers().create('bob', { @@ -76,7 +81,10 @@ test('challenge', async () => { client2, await icpResult2.op() ); - await client2.identifiers().addEndRole('bob', 'agent', client2!.agent!.pre); + let rpyResult2 = await client2 + .identifiers() + .addEndRole('bob', 'agent', client2!.agent!.pre); + await waitOperation(client2, await rpyResult2.op()); // Exchenge OOBIs const oobi1 = await client1.oobis().get('alice', 'agent'); @@ -121,4 +129,6 @@ test('challenge', async () => { (contact: { alias: string }) => contact.alias === 'bob' ); expect(bobContact.challenges[0].authenticated).toEqual(true); + + await assertOperations(client1, client2); }, 30000); diff --git a/examples/integration-scripts/credentials.test.ts b/examples/integration-scripts/credentials.test.ts index 38e82f71..220fa276 100644 --- a/examples/integration-scripts/credentials.test.ts +++ b/examples/integration-scripts/credentials.test.ts @@ -2,6 +2,9 @@ import { strict as assert } from 'assert'; import { Saider, Serder, SignifyClient } from 'signify-ts'; import { resolveEnvironment } from './utils/resolve-env'; import { + assertNotifications, + assertOperations, + markAndRemoveNotification, resolveOobi, waitForNotifications, waitOperation, @@ -75,6 +78,21 @@ beforeAll(async () => { ]); }); +afterAll(async () => { + await assertOperations( + issuerClient, + holderClient, + verifierClient, + legalEntityClient + ); + await assertNotifications( + issuerClient, + holderClient, + verifierClient, + legalEntityClient + ); +}); + test('single signature credentials', async () => { await step('Resolve schema oobis', async () => { await Promise.all([ @@ -211,11 +229,12 @@ test('single signature credentials', async () => { datetime: dt, }); - await issuerClient + let op = await issuerClient .ipex() .submitGrant(issuerAid.name, grant, gsigs, gend, [ holderAid.prefix, ]); + await waitOperation(issuerClient, op); }); await step('holder IPEX admit', async () => { @@ -233,11 +252,20 @@ test('single signature credentials', async () => { grantNotification.a.d!, createTimestamp() ); - await holderClient + let op = await holderClient .ipex() .submitAdmit(holderAid.name, admit, sigs, aend, [issuerAid.prefix]); + await waitOperation(holderClient, op); - await holderClient.notifications().mark(grantNotification.i); + await markAndRemoveNotification(holderClient, grantNotification); + }); + + await step('issuer IPEX grant response', async () => { + const issuerNotifications = await waitForNotifications( + issuerClient, + '/exn/ipex/admit' + ); + await markAndRemoveNotification(issuerClient, issuerNotifications[0]); }); await step('holder has credential', async () => { @@ -270,16 +298,13 @@ test('single signature credentials', async () => { issAttachment: holderCredential.issAtc, datetime: createTimestamp(), }); - await holderClient - .exchanges() - .sendFromEvents( - holderAid.name, - 'presentation', - grant2, - gsigs2, - gend2, - [verifierAid.prefix] - ); + + let op = await holderClient + .ipex() + .submitGrant(holderAid.name, grant2, gsigs2, gend2, [ + verifierAid.prefix, + ]); + await waitOperation(holderClient, op); }); await step('verifier receives IPEX grant', async () => { @@ -299,13 +324,14 @@ test('single signature credentials', async () => { createTimestamp() ); - await verifierClient + let op = await verifierClient .ipex() .submitAdmit(verifierAid.name, admit3, sigs3, aend3, [ holderAid.prefix, ]); + await waitOperation(verifierClient, op); - await verifierClient.notifications().mark(verifierGrantNote.i); + await markAndRemoveNotification(verifierClient, verifierGrantNote); const verifierCredential = await retry(async () => verifierClient.credentials().get(qviCredentialId) @@ -316,6 +342,14 @@ test('single signature credentials', async () => { assert.equal(verifierCredential.status.s, '0'); // 0 = issued }); + await step('holder IPEX present response', async () => { + const holderNotifications = await waitForNotifications( + holderClient, + '/exn/ipex/admit' + ); + await markAndRemoveNotification(holderClient, holderNotifications[0]); + }); + const holderRegistry: { regk: string } = await step( 'holder create registry for LE credential', async () => { @@ -388,11 +422,12 @@ test('single signature credentials', async () => { datetime: dt, }); - await holderClient + let op = await holderClient .ipex() .submitGrant(holderAid.name, grant, gsigs, gend, [ legalEntityAid.prefix, ]); + await waitOperation(holderClient, op); }); await step('Legal Entity IPEX admit', async () => { @@ -411,13 +446,22 @@ test('single signature credentials', async () => { createTimestamp() ); - await legalEntityClient + let op = await legalEntityClient .ipex() .submitAdmit(legalEntityAid.name, admit, sigs, aend, [ holderAid.prefix, ]); + await waitOperation(legalEntityClient, op); - await legalEntityClient.notifications().mark(grantNotification.i); + await markAndRemoveNotification(legalEntityClient, grantNotification); + }); + + await step('LE credential IPEX grant response', async () => { + const notifications = await waitForNotifications( + holderClient, + '/exn/ipex/admit' + ); + await markAndRemoveNotification(holderClient, notifications[0]); }); await step('Legal Entity has chained credential', async () => { diff --git a/examples/integration-scripts/delegation-multisig.test.ts b/examples/integration-scripts/delegation-multisig.test.ts index 6285227a..9b8b7ff9 100644 --- a/examples/integration-scripts/delegation-multisig.test.ts +++ b/examples/integration-scripts/delegation-multisig.test.ts @@ -1,11 +1,14 @@ import { strict as assert } from 'assert'; import signify from 'signify-ts'; import { + assertOperations, + markAndRemoveNotification, resolveOobi, waitForNotifications, waitOperation, + warnNotifications, } from './utils/test-util'; -import { getOrCreateClient } from './utils/test-setup'; +import { getOrCreateClient, getOrCreateIdentifier } from './utils/test-setup'; test('delegation-multisig', async () => { await signify.ready(); @@ -85,14 +88,16 @@ test('delegation-multisig', async () => { // Second member check notifications and join the multisig const notifications = await waitForNotifications(client2, '/multisig/icp'); - await Promise.all( - notifications.map((note) => client2.notifications().mark(note.i)) - ); const msgSaid = notifications[notifications.length - 1].a.d; assert(msgSaid !== undefined); console.log('Member2 received exchange message to join multisig'); const res = await client2.groups().getRequest(msgSaid); + + await Promise.all( + notifications.map((note) => markAndRemoveNotification(client2, note)) + ); + const exn = res[0].exn; const icp = exn.e.icp; @@ -145,7 +150,8 @@ test('delegation-multisig', async () => { s: '0', d: delegatePrefix, }; - await client0.identifiers().interact('delegator', anchor); + let ixnResult = await client0.identifiers().interact('delegator', anchor); + await waitOperation(client0, await ixnResult.op()); console.log('Delegator approved delegation'); let op3 = await client1.keyStates().query(aid0.prefix, '1'); @@ -162,20 +168,14 @@ test('delegation-multisig', async () => { const aid_delegate = await client1.identifiers().get('multisig'); assert.equal(aid_delegate.prefix, delegatePrefix); + + await assertOperations(client0, client1, client2); + await warnNotifications(client0, client1, client2); }, 30000); async function createAID(client: signify.SignifyClient, name: string) { - const icpResult1 = await client.identifiers().create(name, { - toad: 3, - wits: [ - 'BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha', - 'BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM', - 'BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX', - ], - }); - await waitOperation(client, await icpResult1.op()); + await getOrCreateIdentifier(client, name); const aid = await client.identifiers().get(name); - await client.identifiers().addEndRole(name, 'agent', client!.agent!.pre); console.log(name, 'AID:', aid.prefix); return aid; } diff --git a/examples/integration-scripts/delegation.test.ts b/examples/integration-scripts/delegation.test.ts index c1fff692..d07b59f5 100644 --- a/examples/integration-scripts/delegation.test.ts +++ b/examples/integration-scripts/delegation.test.ts @@ -1,7 +1,11 @@ import { strict as assert } from 'assert'; import signify from 'signify-ts'; import { resolveEnvironment } from './utils/resolve-env'; -import { resolveOobi, waitOperation } from './utils/test-util'; +import { + assertOperations, + resolveOobi, + waitOperation, +} from './utils/test-util'; const { url, bootUrl } = resolveEnvironment(); @@ -52,9 +56,10 @@ test('delegation', async () => { }); await waitOperation(client1, await icpResult1.op()); const aid1 = await client1.identifiers().get('delegator'); - await client1 + const rpyResult1 = await client1 .identifiers() .addEndRole('delegator', 'agent', client1!.agent!.pre); + await waitOperation(client1, await rpyResult1.op()); console.log("Delegator's AID:", aid1.prefix); // Client 2 resolves delegator OOBI @@ -78,7 +83,10 @@ test('delegation', async () => { s: '0', d: delegatePrefix, }; - await client1.identifiers().interact('delegator', anchor); + const ixnResult1 = await client1 + .identifiers() + .interact('delegator', anchor); + await waitOperation(client1, await ixnResult1.op()); console.log('Delegator approved delegation'); let op3 = await client2.keyStates().query(aid1.prefix, '1'); @@ -89,4 +97,6 @@ test('delegation', async () => { const aid2 = await client2.identifiers().get('delegate'); assert.equal(aid2.prefix, delegatePrefix); console.log('Delegation approved for aid:', aid2.prefix); + + await assertOperations(client1, client2); }, 60000); diff --git a/examples/integration-scripts/externalModule.test.ts b/examples/integration-scripts/externalModule.test.ts index 8729ec02..2f95c3a3 100644 --- a/examples/integration-scripts/externalModule.test.ts +++ b/examples/integration-scripts/externalModule.test.ts @@ -2,6 +2,7 @@ import { strict as assert } from 'assert'; import signify from 'signify-ts'; import { BIP39Shim } from './modules/bip39_shim'; import { resolveEnvironment } from './utils/resolve-env'; +import { assertOperations, waitOperation } from './utils/test-util'; const { url, bootUrl } = resolveEnvironment(); @@ -35,6 +36,8 @@ test('bip39_shim', async () => { extern_type: 'bip39_shim', extern: { mnemonics: words }, }); - const op = await icpResult.op(); + const op = await waitOperation(client1, await icpResult.op()); assert.equal(op['done'], true); + + await assertOperations(client1); }, 30000); diff --git a/examples/integration-scripts/multisig-holder.test.ts b/examples/integration-scripts/multisig-holder.test.ts index ccc76871..6b2ed129 100644 --- a/examples/integration-scripts/multisig-holder.test.ts +++ b/examples/integration-scripts/multisig-holder.test.ts @@ -1,8 +1,18 @@ import { strict as assert } from 'assert'; -import signify, { SignifyClient, IssueCredentialArgs } from 'signify-ts'; +import signify, { + SignifyClient, + IssueCredentialArgs, + Operation, +} from 'signify-ts'; import { resolveEnvironment } from './utils/resolve-env'; -import { waitForNotifications, waitOperation } from './utils/test-util'; -import { getOrCreateClient } from './utils/test-setup'; +import { + assertOperations, + markNotification, + waitForNotifications, + waitOperation, + warnNotifications, +} from './utils/test-util'; +import { getOrCreateClient, getOrCreateIdentifier } from './utils/test-setup'; const { vleiServerUrl } = resolveEnvironment(); const WITNESS_AIDS = [ @@ -285,8 +295,8 @@ test('multisig', async function run() { `Member2 authorized agent role to ${eid1}, waiting for others to authorize...` ); // Check for completion - op1 = await waitOperation(client1, op1); - op2 = await waitOperation(client2, op2); + await waitOperation(client1, op1); + await waitOperation(client2, op2); console.log(`End role authorization for agent ${eid1} completed!`); console.log(`Starting multisig end role authorization for agent ${eid2}`); @@ -387,8 +397,8 @@ test('multisig', async function run() { `Member2 authorized agent role to ${eid2}, waiting for others to authorize...` ); // Check for completion - op1 = await waitOperation(client1, op1); - op2 = await waitOperation(client2, op2); + await waitOperation(client1, op1); + await waitOperation(client2, op2); console.log(`End role authorization for agent ${eid2} completed!`); // Holder resolve multisig OOBI @@ -396,7 +406,7 @@ test('multisig', async function run() { let oobiMultisig = oobisRes.oobis[0].split('/agent/')[0]; op3 = await client3.oobis().resolve(oobiMultisig, 'holder'); - op3 = await waitOperation(client3, op3); + await waitOperation(client3, op3); console.log(`Issuer resolved multisig holder OOBI`); let holderAid = await client1.identifiers().get('holder'); @@ -426,7 +436,7 @@ test('multisig', async function run() { let exnRes = await client1.exchanges().get(grantMsgSaid); recp = [aid2['state']].map((state) => state['i']); - await multisigAdmitCredential( + op1 = await multisigAdmitCredential( client1, 'holder', 'member1', @@ -452,7 +462,7 @@ test('multisig', async function run() { console.log(`Member2 /exn/ipex/grant msg : ` + JSON.stringify(exnRes2)); let recp2 = [aid1['state']].map((state) => state['i']); - await multisigAdmitCredential( + op2 = await multisigAdmitCredential( client2, 'holder', 'member2', @@ -464,6 +474,9 @@ test('multisig', async function run() { `Member2 admitted credential with SAID : ${exnRes.exn.e.acdc.d}` ); + await waitOperation(client1, op1); + await waitOperation(client2, op2); + let creds1 = await client1.credentials().list(); console.log(`Member1 has ${creds1.length} credential`); @@ -482,6 +495,9 @@ test('multisig', async function run() { `Member1 has ${creds1.length} credential : ` + JSON.stringify(creds1) ); assert.equal(creds1.length, 1); + + await assertOperations(client1, client2, client3); + await warnNotifications(client1, client2, client3); }, 360000); async function waitAndMarkNotification(client: SignifyClient, route: string) { @@ -489,7 +505,7 @@ async function waitAndMarkNotification(client: SignifyClient, route: string) { await Promise.all( notes.map(async (note) => { - await client.notifications().mark(note.i); + await markNotification(client, note); }) ); @@ -497,13 +513,8 @@ async function waitAndMarkNotification(client: SignifyClient, route: string) { } async function createAID(client: SignifyClient, name: string, wits: string[]) { - const icpResult1 = await client.identifiers().create(name, { - toad: wits.length, - wits: wits, - }); - await waitOperation(client, await icpResult1.op()); + await getOrCreateIdentifier(client, name); const aid = await client.identifiers().get(name); - await client.identifiers().addEndRole(name, 'agent', client!.agent!.pre); console.log(name, 'AID:', aid.prefix); return aid; } @@ -549,9 +560,10 @@ async function issueCredential( iss: result.iss, }); - await client + let op = await client .ipex() .submitGrant(args.issuerName, grant, gsigs, end, [args.recipient]); + op = await waitOperation(client, op); } console.log('Grant message sent'); @@ -571,7 +583,7 @@ async function multisigAdmitCredential( grantSaid: string, issuerPrefix: string, recipients: string[] -) { +): Promise { let mHab = await client.identifiers().get(memberAlias); let gHab = await client.identifiers().get(groupName); @@ -579,7 +591,7 @@ async function multisigAdmitCredential( .ipex() .admit(groupName, '', grantSaid, TIME); - await client + let op = await client .ipex() .submitAdmit(groupName, admit, sigs, end, [issuerPrefix]); @@ -607,4 +619,6 @@ async function multisigAdmitCredential( gembeds, recipients ); + + return op; } diff --git a/examples/integration-scripts/multisig.test.ts b/examples/integration-scripts/multisig.test.ts index 0492c9ca..7eb75625 100644 --- a/examples/integration-scripts/multisig.test.ts +++ b/examples/integration-scripts/multisig.test.ts @@ -5,8 +5,14 @@ import signify, { IssueCredentialResult, } from 'signify-ts'; import { resolveEnvironment } from './utils/resolve-env'; -import { waitForNotifications, waitOperation } from './utils/test-util'; -import { getOrCreateClient } from './utils/test-setup'; +import { + assertOperations, + markNotification, + waitForNotifications, + waitOperation, + warnNotifications, +} from './utils/test-util'; +import { getOrCreateClient, getOrCreateIdentifier } from './utils/test-setup'; const { vleiServerUrl } = resolveEnvironment(); const WITNESS_AIDS = [ @@ -963,9 +969,9 @@ test('multisig', async function run() { datetime: stamp, }); - await client1 - .exchanges() - .sendFromEvents('multisig', 'credential', grant, gsigs, end, [holder]); + op1 = await client1 + .ipex() + .submitGrant('multisig', grant, gsigs, end, [holder]); mstate = m['state']; seal = [ @@ -1011,11 +1017,9 @@ test('multisig', async function run() { datetime: stamp, }); - await client2 - .exchanges() - .sendFromEvents('multisig', 'credential', grant2, gsigs2, end2, [ - holder, - ]); + op2 = await client2 + .ipex() + .submitGrant('multisig', grant2, gsigs2, end2, [holder]); sigers = gsigs2.map((sig) => new signify.Siger({ qb64: sig })); @@ -1055,11 +1059,9 @@ test('multisig', async function run() { datetime: stamp, }); - await client3 - .exchanges() - .sendFromEvents('multisig', 'credential', grant3, gsigs3, end3, [ - holder, - ]); + op3 = await client3 + .ipex() + .submitGrant('multisig', grant3, gsigs3, end3, [holder]); sigers = gsigs3.map((sig) => new signify.Siger({ qb64: sig })); @@ -1093,16 +1095,26 @@ test('multisig', async function run() { .ipex() .admit('holder', '', res.exn.d); - res = await client4 + op4 = await client4 .ipex() .submitAdmit('holder', admit, asigs, aend, [m['prefix']]); + await Promise.all([ + waitOperation(client1, op1), + waitOperation(client2, op2), + waitOperation(client3, op3), + waitOperation(client4, op4), + ]); + console.log('Holder creates and sends admit message'); msgSaid = await waitAndMarkNotification(client1, '/exn/ipex/admit'); console.log('Member1 received exchange message with the admit response'); const creds = await client4.credentials().list(); console.log(`Holder holds ${creds.length} credential`); + + await assertOperations(client1, client2, client3, client4); + await warnNotifications(client1, client2, client3, client4); }, 360000); async function waitAndMarkNotification(client: SignifyClient, route: string) { @@ -1110,7 +1122,7 @@ async function waitAndMarkNotification(client: SignifyClient, route: string) { await Promise.all( notes.map(async (note) => { - await client.notifications().mark(note.i); + await markNotification(client, note); }) ); @@ -1118,15 +1130,11 @@ async function waitAndMarkNotification(client: SignifyClient, route: string) { } async function createAID(client: SignifyClient, name: string, wits: string[]) { - const icpResult1 = await client.identifiers().create(name, { - toad: wits.length, + await getOrCreateIdentifier(client, name, { wits: wits, + toad: wits.length, }); - await waitOperation(client, await icpResult1.op()); - const aid = await client.identifiers().get(name); - await client.identifiers().addEndRole(name, 'agent', client!.agent!.pre); - console.log(name, 'AID:', aid.prefix); - return aid; + return await client.identifiers().get(name); } async function multisigIssue( diff --git a/examples/integration-scripts/randy.test.ts b/examples/integration-scripts/randy.test.ts index 3c6ef978..cb0434e2 100644 --- a/examples/integration-scripts/randy.test.ts +++ b/examples/integration-scripts/randy.test.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'assert'; import signify from 'signify-ts'; import { resolveEnvironment } from './utils/resolve-env'; -import { waitOperation } from './utils/test-util'; +import { assertOperations, waitOperation } from './utils/test-util'; const { url, bootUrl } = resolveEnvironment(); @@ -68,4 +68,6 @@ test('randy', async () => { assert.equal(dig.qb64, icp.digers[0].qb64); log = await events.get(aid['prefix']); assert.equal(log.length, 3); + + await assertOperations(client1); }, 30000); diff --git a/examples/integration-scripts/salty.test.ts b/examples/integration-scripts/salty.test.ts index 6a84f044..95b88d19 100644 --- a/examples/integration-scripts/salty.test.ts +++ b/examples/integration-scripts/salty.test.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'assert'; import signify from 'signify-ts'; import { resolveEnvironment } from './utils/resolve-env'; -import { waitOperation } from './utils/test-util'; +import { assertOperations, waitOperation } from './utils/test-util'; const { url, bootUrl } = resolveEnvironment(); @@ -102,7 +102,8 @@ test('salty', async () => { assert.equal(salt.stem, 'signify:aid'); assert.equal(aid.prefix, icp2.pre); - await client1.identifiers().create('aid3'); + icpResult = await client1.identifiers().create('aid3'); + await waitOperation(client1, await icpResult.op()); aids = await client1.identifiers().list(); assert.equal(aids.aids.length, 3); aid = aids.aids[0]; @@ -164,5 +165,8 @@ test('salty', async () => { serder = new signify.Serder(log[2]); assert.equal(serder.pre, ixn.pre); assert.equal(serder.ked['d'], ixn.ked['d']); + + await assertOperations(client1); + console.log('Salty test passed'); }, 30000); diff --git a/examples/integration-scripts/singlesig-dip.test.ts b/examples/integration-scripts/singlesig-dip.test.ts index 857f1e99..dc154fc9 100644 --- a/examples/integration-scripts/singlesig-dip.test.ts +++ b/examples/integration-scripts/singlesig-dip.test.ts @@ -4,7 +4,7 @@ import { getOrCreateContact, getOrCreateIdentifier, } from './utils/test-setup'; -import { waitOperation } from './utils/test-util'; +import { assertOperations, waitOperation } from './utils/test-util'; import { resolveEnvironment } from './utils/resolve-env'; let client1: SignifyClient, client2: SignifyClient; @@ -20,6 +20,9 @@ beforeAll(async () => { beforeAll(async () => { contact1_id = await getOrCreateContact(client2, 'contact1', name1_oobi); }); +afterAll(async () => { + await assertOperations(client1, client2); +}); describe('singlesig-dip', () => { test('delegate1a', async () => { diff --git a/examples/integration-scripts/singlesig-drt.test.ts b/examples/integration-scripts/singlesig-drt.test.ts index 4253c752..c30e5e29 100644 --- a/examples/integration-scripts/singlesig-drt.test.ts +++ b/examples/integration-scripts/singlesig-drt.test.ts @@ -4,7 +4,7 @@ import { getOrCreateContact, getOrCreateIdentifier, } from './utils/test-setup'; -import { waitOperation } from './utils/test-util'; +import { assertOperations, waitOperation } from './utils/test-util'; let delegator: SignifyClient, delegate: SignifyClient; let name1_id: string, name1_oobi: string; @@ -19,6 +19,9 @@ beforeAll(async () => { beforeAll(async () => { contact1_id = await getOrCreateContact(delegate, 'contact1', name1_oobi); }); +afterAll(async () => { + await assertOperations(delegator, delegate); +}); describe('singlesig-drt', () => { test('delegate1a', async () => { diff --git a/examples/integration-scripts/singlesig-ixn.test.ts b/examples/integration-scripts/singlesig-ixn.test.ts index b65bbcd1..73fe67e1 100644 --- a/examples/integration-scripts/singlesig-ixn.test.ts +++ b/examples/integration-scripts/singlesig-ixn.test.ts @@ -4,7 +4,7 @@ import { getOrCreateContact, getOrCreateIdentifier, } from './utils/test-setup'; -import { waitOperation } from './utils/test-util'; +import { assertOperations, waitOperation } from './utils/test-util'; let client1: SignifyClient, client2: SignifyClient; let name1_id: string, name1_oobi: string; @@ -19,6 +19,9 @@ beforeAll(async () => { beforeAll(async () => { contact1_id = await getOrCreateContact(client2, 'contact1', name1_oobi); }); +afterAll(async () => { + await assertOperations(client1, client2); +}); interface KeyState { i: string; diff --git a/examples/integration-scripts/singlesig-rot.test.ts b/examples/integration-scripts/singlesig-rot.test.ts index f40c7dd0..89a3bf40 100644 --- a/examples/integration-scripts/singlesig-rot.test.ts +++ b/examples/integration-scripts/singlesig-rot.test.ts @@ -4,7 +4,7 @@ import { getOrCreateContact, getOrCreateIdentifier, } from './utils/test-setup'; -import { waitOperation } from './utils/test-util'; +import { assertOperations, waitOperation } from './utils/test-util'; let client1: SignifyClient, client2: SignifyClient; let name1_id: string, name1_oobi: string; @@ -19,6 +19,9 @@ beforeAll(async () => { beforeAll(async () => { contact1_id = await getOrCreateContact(client2, 'contact1', name1_oobi); }); +afterAll(async () => { + await assertOperations(client1, client2); +}); interface KeyState { i: string; diff --git a/examples/integration-scripts/singlesig-vlei-issuance.test.ts b/examples/integration-scripts/singlesig-vlei-issuance.test.ts index 7347a083..7eee0d1b 100644 --- a/examples/integration-scripts/singlesig-vlei-issuance.test.ts +++ b/examples/integration-scripts/singlesig-vlei-issuance.test.ts @@ -2,9 +2,12 @@ import { strict as assert } from 'assert'; import { Saider, Serder, SignifyClient } from 'signify-ts'; import { resolveEnvironment } from './utils/resolve-env'; import { + assertOperations, + markAndRemoveNotification, resolveOobi, waitForNotifications, waitOperation, + warnNotifications, } from './utils/test-util'; import { retry } from './utils/retry'; import { @@ -456,6 +459,9 @@ test('singlesig-vlei-issuance', async function run() { assert.equal(oorCredHolder.sad.e.auth.n, oorAuthCred.sad.d); assert.equal(oorCredHolder.status.s, '0'); assert(oorCredHolder.atc !== undefined); + + await assertOperations(gleifClient, qviClient, leClient, roleClient); + await warnNotifications(gleifClient, qviClient, leClient, roleClient); }, 360000); async function getOrCreateRegistry( @@ -551,9 +557,10 @@ async function sendGrantMessage( datetime: createTimestamp(), }); - await senderClient + let op = await senderClient .ipex() .submitGrant(senderAid.name, grant, gsigs, gend, [recipientAid.prefix]); + op = await waitOperation(senderClient, op); } async function sendAdmitMessage( @@ -572,9 +579,10 @@ async function sendAdmitMessage( .ipex() .admit(senderAid.name, '', grantNotification.a.d!, createTimestamp()); - await senderClient + let op = await senderClient .ipex() .submitAdmit(senderAid.name, admit, sigs, aend, [recipientAid.prefix]); + op = await waitOperation(senderClient, op); - await senderClient.notifications().mark(grantNotification.i); + await markAndRemoveNotification(senderClient, grantNotification); } diff --git a/examples/integration-scripts/test-setup-clients.test.ts b/examples/integration-scripts/test-setup-clients.test.ts index 906f8528..dd06adc1 100644 --- a/examples/integration-scripts/test-setup-clients.test.ts +++ b/examples/integration-scripts/test-setup-clients.test.ts @@ -4,6 +4,7 @@ import { getOrCreateContact, getOrCreateIdentifier, } from './utils/test-setup'; +import { assertOperations } from './utils/test-util'; let client1: SignifyClient, client2: SignifyClient; let name1_id: string, name1_oobi: string; @@ -22,6 +23,9 @@ beforeAll(async () => { contact1_id = await getOrCreateContact(client2, 'contact1', name1_oobi); contact2_id = await getOrCreateContact(client1, 'contact2', name2_oobi); }); +afterAll(async () => { + await assertOperations(client1, client2); +}); describe('test-setup-clients', () => { test('step1', async () => { diff --git a/examples/integration-scripts/test-setup-single-client.test.ts b/examples/integration-scripts/test-setup-single-client.test.ts index f01957e8..ecbf4e81 100644 --- a/examples/integration-scripts/test-setup-single-client.test.ts +++ b/examples/integration-scripts/test-setup-single-client.test.ts @@ -1,6 +1,7 @@ import { SignifyClient } from 'signify-ts'; import { getOrCreateClients, getOrCreateIdentifier } from './utils/test-setup'; import { resolveEnvironment } from './utils/resolve-env'; +import { assertOperations, waitOperation } from './utils/test-util'; let client: SignifyClient; let name1_id: string, name1_oobi: string; @@ -12,6 +13,9 @@ beforeAll(async () => { beforeAll(async () => { [name1_id, name1_oobi] = await getOrCreateIdentifier(client, 'name1'); }); +afterAll(async () => { + await assertOperations(client); +}); describe('test-setup-single-client', () => { test('step1', async () => { diff --git a/examples/integration-scripts/utils/test-util.ts b/examples/integration-scripts/utils/test-util.ts index 6ebecdee..4a08262b 100644 --- a/examples/integration-scripts/utils/test-util.ts +++ b/examples/integration-scripts/utils/test-util.ts @@ -8,23 +8,122 @@ export function sleep(ms: number): Promise { } /** - * Poll for operation to become completed + * Assert that all operations were waited for. + *

This is a postcondition check to make sure all long-running operations have been waited for + * @see waitOperation + */ +export async function assertOperations( + ...clients: SignifyClient[] +): Promise { + for (let client of clients) { + let operations = await client.operations().list(); + expect(operations).toHaveLength(0); + } +} + +/** + * Assert that all notifications were handled. + *

This is a postcondition check to make sure all notifications have been handled + * @see markNotification + * @see markAndRemoveNotification + */ +export async function assertNotifications( + ...clients: SignifyClient[] +): Promise { + for (let client of clients) { + let res = await client.notifications().list(); + let notes = res.notes.filter((i: any) => i.r === false); + expect(notes).toHaveLength(0); + } +} + +/** + * Logs a warning for each un-handled notification. + *

Replace warnNotifications with assertNotifications when test handles all notifications + * @see assertNotifications + */ +export async function warnNotifications( + ...clients: SignifyClient[] +): Promise { + let count = 0; + for (let client of clients) { + let res = await client.notifications().list(); + let notes = res.notes.filter((i: any) => i.r === false); + if (notes.length > 0) { + count += notes.length; + console.warn('notifications', notes); + } + } + expect(count).toBeGreaterThan(0); // replace warnNotifications with assertNotifications +} + +/** + * Get status of operation. + * If parameter recurse is set then also checks status of dependent operations. + */ +async function getOperation( + client: SignifyClient, + name: string, + recurse?: boolean +): Promise> { + const result = await client.operations().get(name); + if (recurse === true) { + let i: Operation | undefined = result; + while (result.done && i?.metadata?.depends !== undefined) { + let depends: Operation = await client + .operations() + .get(i.metadata.depends.name); + result.done = result.done && depends.done; + i.metadata.depends = depends; + i = depends.metadata?.depends; + } + } + return result; +} + +/** + * Poll for operation to become completed. + * Removes completed operation */ export async function waitOperation( client: SignifyClient, - op: Operation, + op: Operation | string, options: RetryOptions = {} ): Promise> { - return retry(async () => { - op = await client.operations().get(op.name); - - if (op.done !== true) { - throw new Error(`Operation ${op.name} not done`); + const ctrl = new AbortController(); + options.signal?.addEventListener('abort', (e: Event) => { + const s = e.target as AbortSignal; + ctrl.abort(s.reason); + }); + let name: string; + if (typeof op === 'string') { + name = op; + } else if (typeof op === 'object' && 'name' in op) { + name = op.name; + } else { + throw new Error(); + } + const result: Operation = await retry(async () => { + let t: Operation; + try { + t = await getOperation(client, name, true); + } catch (e) { + ctrl.abort(e); + throw e; } - - console.log('DONE', op.name); - return op; + if (t.done !== true) { + throw new Error(`Operation ${name} not done`); + } + console.log('DONE', name); + return t; }, options); + let i: Operation | undefined = result; + while (i !== undefined) { + // console.log('DELETE', i.name); + await client.operations().delete(i.name); + i = i.metadata?.depends; + } + return result; } export async function resolveOobi( @@ -64,3 +163,27 @@ export async function waitForNotifications( return notes; }, options); } + +/** + * Mark notification as read. + */ +export async function markNotification( + client: SignifyClient, + note: Notification +): Promise { + await client.notifications().mark(note.i); +} + +/** + * Mark and remove notification. + */ +export async function markAndRemoveNotification( + client: SignifyClient, + note: Notification +): Promise { + try { + await client.notifications().mark(note.i); + } finally { + await client.notifications().delete(note.i); + } +} diff --git a/src/keri/app/aiding.ts b/src/keri/app/aiding.ts index fb3272c7..4b87956a 100644 --- a/src/keri/app/aiding.ts +++ b/src/keri/app/aiding.ts @@ -381,7 +381,7 @@ export class Identifier { role: string, eid?: string, stamp?: string - ): Promise { + ): Promise { const hab = await this.get(name); const pre = hab.prefix; diff --git a/src/keri/app/coring.ts b/src/keri/app/coring.ts index 3cfce93e..a1d6af5b 100644 --- a/src/keri/app/coring.ts +++ b/src/keri/app/coring.ts @@ -61,9 +61,14 @@ export class Oobis { } export interface Operation { - done: boolean; name: string; - response: T; + metadata?: { + depends?: Operation; + [property: string]: any; + }; + done?: boolean; + error?: any; + response?: T; } /** @@ -94,6 +99,34 @@ export class Operations { const res = await this.client.fetch(path, method, data); return await res.json(); } + /** + * List operations + * @async + * @param {string} type Select operations by type + * @returns {Promise} A list of operations + */ + async list(type?: string): Promise[]> { + const params = new URLSearchParams(); + if (type !== undefined) { + params.append('type', type); + } + const path = `/operations?${params.toString()}`; + const data = null; + const method = 'GET'; + const res = await this.client.fetch(path, method, data); + return await res.json(); + } + /** + * Delete operation + * @async + * @param {string} name Name of the operation + */ + async delete(name: string): Promise { + const path = `/operations/${name}`; + const data = null; + const method = 'DELETE'; + await this.client.fetch(path, method, data); + } } /** diff --git a/src/keri/app/notifying.ts b/src/keri/app/notifying.ts index 5b631f96..17de435b 100644 --- a/src/keri/app/notifying.ts +++ b/src/keri/app/notifying.ts @@ -61,10 +61,9 @@ export class Notifications { * @param {string} said SAID of the notification * @returns {Promise} A promise to the result of the deletion */ - async delete(said: string): Promise { + async delete(said: string): Promise { const path = `/notifications/` + said; const method = 'DELETE'; - const res = await this.client.fetch(path, method, null); - return await res.json(); + await this.client.fetch(path, method, null); } } diff --git a/test/app/coring.test.ts b/test/app/coring.test.ts index ea68a0d7..15066d31 100644 --- a/test/app/coring.test.ts +++ b/test/app/coring.test.ts @@ -214,9 +214,24 @@ describe('Coring', () => { const ops = client.operations(); await ops.get('operationName'); - const lastCall = fetchMock.mock.calls[fetchMock.mock.calls.length - 1]!; + let lastCall = fetchMock.mock.calls[fetchMock.mock.calls.length - 1]!; assert.equal(lastCall[0]!, url + '/operations/operationName'); assert.equal(lastCall[1]!.method, 'GET'); + + await ops.list(); + lastCall = fetchMock.mock.calls[fetchMock.mock.calls.length - 1]!; + assert.equal(lastCall[0]!, url + '/operations?'); + assert.equal(lastCall[1]!.method, 'GET'); + + await ops.list('witness'); + lastCall = fetchMock.mock.calls[fetchMock.mock.calls.length - 1]!; + assert.equal(lastCall[0]!, url + '/operations?type=witness'); + assert.equal(lastCall[1]!.method, 'GET'); + + await ops.delete('operationName'); + lastCall = fetchMock.mock.calls[fetchMock.mock.calls.length - 1]!; + assert.equal(lastCall[0]!, url + '/operations/operationName'); + assert.equal(lastCall[1]!.method, 'DELETE'); }); it('Events and states', async () => {