var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { expect } from 'chai';
import sinon from 'sinon';
import { DataStoreLevel } from '../../src/store/data-store-level.js';
import { DidKeyResolver } from '../../src/did/did-key-resolver.js';
import { DidResolver } from '../../src/did/did-resolver.js';
import { Dwn } from '../../src/dwn.js';
import { DwnErrorCode } from '../../src/core/dwn-error.js';
import { EventLogLevel } from '../../src/event-log/event-log-level.js';
import { Message } from '../../src/core/message.js';
import { MessageStoreLevel } from '../../src/store/message-store-level.js';
import { PermissionsRevoke } from '../../src/interfaces/permissions-revoke.js';
import { TestDataGenerator } from '../utils/test-data-generator.js';
import { getCurrentTimeInHighPrecision, sleep } from '../../src/utils/time.js';
describe('PermissionsRevokeHandler.handle()', () => {
    if (process.env.TESTSOFF === 'true') {
        return;
    }
    let didResolver;
    let messageStore;
    let dataStore;
    let eventLog;
    let dwn;
    describe('functional tests', () => {
        before(() => __awaiter(void 0, void 0, void 0, function* () {
            didResolver = new DidResolver([new DidKeyResolver()]);
            // important to follow this pattern to initialize and clean the message and data store in tests
            // so that different suites can reuse the same block store and index location for testing
            messageStore = new MessageStoreLevel({
                blockstoreLocation: 'tests/data/TEST-MESSAGESTORE',
                indexLocation: 'tests/data/TEST-INDEX'
            });
            dataStore = new DataStoreLevel({
                blockstoreLocation: 'tests/data/TEST-DATASTORE'
            });
            eventLog = new EventLogLevel({
                location: 'tests/data/TEST-EVENTLOG'
            });
            dwn = yield Dwn.create({ didResolver, messageStore, dataStore, eventLog });
        }));
        beforeEach(() => __awaiter(void 0, void 0, void 0, function* () {
            sinon.restore(); // wipe all previous stubs/spies/mocks/fakes
            // clean up before each test rather than after so that a test does not depend on other tests to do the clean up
            yield messageStore.clear();
            yield dataStore.clear();
            yield eventLog.clear();
        }));
        after(() => __awaiter(void 0, void 0, void 0, function* () {
            yield dwn.close();
        }));
        it('should accept a PermissionsRevoke that revokes an existing grant', () => __awaiter(void 0, void 0, void 0, function* () {
            // scenario: Alice issues a grant to Bob, then she revokes the grant.
            const alice = yield DidKeyResolver.generate();
            const bob = yield DidKeyResolver.generate();
            // Alice issues a grant
            const { permissionsGrant } = yield TestDataGenerator.generatePermissionsGrant({
                author: alice,
                grantedBy: alice.did,
                grantedTo: bob.did,
                grantedFor: alice.did,
            });
            const permissionsGrantReply = yield dwn.processMessage(alice.did, permissionsGrant.message);
            expect(permissionsGrantReply.status.code).to.eq(202);
            // Alice revokes the grant
            const { permissionsRevoke } = yield TestDataGenerator.generatePermissionsRevoke({
                author: alice,
                permissionsGrantId: yield Message.getCid(permissionsGrant.message)
            });
            const permissionsRevokeReply = yield dwn.processMessage(alice.did, permissionsRevoke.message);
            expect(permissionsRevokeReply.status.code).to.eq(202);
        }));
        it('should return 401 if authentication fails', () => __awaiter(void 0, void 0, void 0, function* () {
            const alice = yield DidKeyResolver.generate();
            alice.keyId = 'wrongValue'; // to fail authentication
            const { message } = yield TestDataGenerator.generatePermissionsRevoke({
                author: alice,
            });
            const reply = yield dwn.processMessage(alice.did, message);
            expect(reply.status.code).to.equal(401);
            expect(reply.status.detail).to.contain('not a valid DID');
        }));
        it('should reject with 400 if failure parsing the message', () => __awaiter(void 0, void 0, void 0, function* () {
            const alice = yield DidKeyResolver.generate();
            const { message } = yield TestDataGenerator.generatePermissionsRevoke();
            // stub the `parse()` function to throw an error
            sinon.stub(PermissionsRevoke, 'parse').throws('anyError');
            const reply = yield dwn.processMessage(alice.did, message);
            expect(reply.status.code).to.equal(400);
        }));
        it('should reject with 400 if the associated grant cannot be found', () => __awaiter(void 0, void 0, void 0, function* () {
            const alice = yield DidKeyResolver.generate();
            const { permissionsRevoke } = yield TestDataGenerator.generatePermissionsRevoke({
                author: alice,
                permissionsGrantId: yield TestDataGenerator.randomCborSha256Cid()
            });
            const permissionsRevokeReply = yield dwn.processMessage(alice.did, permissionsRevoke.message);
            expect(permissionsRevokeReply.status.code).to.eq(400);
            expect(permissionsRevokeReply.status.detail).to.contain('Could not find PermissionsGrant');
        }));
        it('should reject with 400 if the associated grant was issued after the revoke was created', () => __awaiter(void 0, void 0, void 0, function* () {
            const alice = yield DidKeyResolver.generate();
            const preGrantTimeStamp = getCurrentTimeInHighPrecision();
            yield sleep(10);
            // Create grant
            const { permissionsGrant } = yield TestDataGenerator.generatePermissionsGrant({
                author: alice,
                grantedBy: alice.did,
                grantedFor: alice.did,
            });
            const permissionsGrantReply = yield dwn.processMessage(alice.did, permissionsGrant.message);
            expect(permissionsGrantReply.status.code).to.eq(202);
            // Create revoke with timestamp before grant
            const { permissionsRevoke } = yield TestDataGenerator.generatePermissionsRevoke({
                author: alice,
                permissionsGrantId: yield Message.getCid(permissionsGrant.message),
                dateCreated: preGrantTimeStamp
            });
            const permissionsRevokeReply = yield dwn.processMessage(alice.did, permissionsRevoke.message);
            expect(permissionsRevokeReply.status.code).to.eq(400);
            expect(permissionsRevokeReply.status.detail).to.contain('PermissionsRevoke has earlier date than associated PermissionsGrant');
        }));
        it('should reject with 401 if the revoke was not authored by the DID in the `grantedFor` of the grant', () => __awaiter(void 0, void 0, void 0, function* () {
            // scenario: Alice issues a grant. Bob tries and failes to revoke the grant.
            const alice = yield DidKeyResolver.generate();
            const bob = yield DidKeyResolver.generate();
            const { permissionsGrant } = yield TestDataGenerator.generatePermissionsGrant({
                author: alice,
                grantedBy: alice.did,
                grantedFor: alice.did,
            });
            const permissionsGrantReply = yield dwn.processMessage(alice.did, permissionsGrant.message);
            expect(permissionsGrantReply.status.code).to.eq(202);
            const { permissionsRevoke } = yield TestDataGenerator.generatePermissionsRevoke({
                author: bob,
                permissionsGrantId: yield Message.getCid(permissionsGrant.message),
            });
            const permissionsRevokeReply = yield dwn.processMessage(alice.did, permissionsRevoke.message);
            expect(permissionsRevokeReply.status.code).to.eq(401);
            expect(permissionsRevokeReply.status.detail).to.contain(DwnErrorCode.PermissionsRevokeUnauthorizedRevoke);
        }));
        it('should reject with 409 if older PermissionsRevoke messages exist for the same grant', () => __awaiter(void 0, void 0, void 0, function* () {
            const alice = yield DidKeyResolver.generate();
            // Create a grant
            const { permissionsGrant } = yield TestDataGenerator.generatePermissionsGrant({
                author: alice,
                grantedBy: alice.did,
                grantedFor: alice.did,
            });
            const permissionsGrantReply = yield dwn.processMessage(alice.did, permissionsGrant.message);
            expect(permissionsGrantReply.status.code).to.eq(202);
            // Revoke the grant
            const { permissionsRevoke: permissionsRevoke1 } = yield TestDataGenerator.generatePermissionsRevoke({
                author: alice,
                permissionsGrantId: yield Message.getCid(permissionsGrant.message),
            });
            const permissionsRevokeReply1 = yield dwn.processMessage(alice.did, permissionsRevoke1.message);
            expect(permissionsRevokeReply1.status.code).to.eq(202);
            // Try to revoke the grant again, and receive 409 because we already revoked it.
            const { permissionsRevoke: permissionsRevoke2 } = yield TestDataGenerator.generatePermissionsRevoke({
                author: alice,
                permissionsGrantId: yield Message.getCid(permissionsGrant.message),
            });
            const permissionsRevokeReply2 = yield dwn.processMessage(alice.did, permissionsRevoke2.message);
            expect(permissionsRevokeReply2.status.code).to.eq(409);
        }));
        it('should reject with 409 if a PermissionsRevoke message exists for same grant, same revocation time, and lower lexicographic CID', () => __awaiter(void 0, void 0, void 0, function* () {
            const alice = yield DidKeyResolver.generate();
            // Create a grant
            const { permissionsGrant } = yield TestDataGenerator.generatePermissionsGrant({
                author: alice,
                grantedBy: alice.did,
                grantedFor: alice.did,
            });
            const permissionsGrantReply = yield dwn.processMessage(alice.did, permissionsGrant.message);
            expect(permissionsGrantReply.status.code).to.eq(202);
            // Create two revokes with same timestamp
            const revokeTimestamp = getCurrentTimeInHighPrecision();
            const { permissionsRevoke: revoke1 } = yield TestDataGenerator.generatePermissionsRevoke({
                author: alice,
                permissionsGrantId: yield Message.getCid(permissionsGrant.message),
                dateCreated: revokeTimestamp,
            });
            const { permissionsRevoke: revoke2 } = yield TestDataGenerator.generatePermissionsRevoke({
                author: alice,
                permissionsGrantId: yield Message.getCid(permissionsGrant.message),
                dateCreated: revokeTimestamp,
            });
            // Sort revokes by message CID
            let revokeWithHigherLexicographic;
            let revokeWithLowerLexicographic;
            if ((yield Message.getCid(revoke1.message)) > (yield Message.getCid(revoke2.message))) {
                revokeWithHigherLexicographic = revoke1;
                revokeWithLowerLexicographic = revoke2;
            }
            else {
                revokeWithHigherLexicographic = revoke2;
                revokeWithLowerLexicographic = revoke1;
            }
            // Process revoke with lower lexicographic value
            const permissionsRevokeReply1 = yield dwn.processMessage(alice.did, revokeWithLowerLexicographic.message);
            expect(permissionsRevokeReply1.status.code).to.eq(202);
            // Process revoke with same timestamp but lower lexicographic value, receive 409
            const permissionsRevokeReply2 = yield dwn.processMessage(alice.did, revokeWithHigherLexicographic.message);
            expect(permissionsRevokeReply2.status.code).to.eq(409);
        }));
        it('should accept revokes that are older than the oldest existing revoke', () => __awaiter(void 0, void 0, void 0, function* () {
            const alice = yield DidKeyResolver.generate();
            // Create a grant
            const { permissionsGrant } = yield TestDataGenerator.generatePermissionsGrant({
                author: alice,
                grantedBy: alice.did,
                grantedFor: alice.did,
            });
            const permissionsGrantReply = yield dwn.processMessage(alice.did, permissionsGrant.message);
            expect(permissionsGrantReply.status.code).to.eq(202);
            // Pre-create a Revoke message with earlier timestamp, to be processed later
            const { permissionsRevoke: permissionsRevoke1 } = yield TestDataGenerator.generatePermissionsRevoke({
                author: alice,
                permissionsGrantId: yield Message.getCid(permissionsGrant.message),
            });
            yield sleep(1);
            // Revoke the grant using a later timestamp than the pre-created revoke
            const { permissionsRevoke: permissionsRevoke2 } = yield TestDataGenerator.generatePermissionsRevoke({
                author: alice,
                permissionsGrantId: yield Message.getCid(permissionsGrant.message),
            });
            const permissionsRevokeReply2 = yield dwn.processMessage(alice.did, permissionsRevoke2.message);
            expect(permissionsRevokeReply2.status.code).to.eq(202);
            // Process the pre-created Revoke
            const permissionsRevokeReply1 = yield dwn.processMessage(alice.did, permissionsRevoke1.message);
            expect(permissionsRevokeReply1.status.code).to.eq(202);
        }));
        describe('event log', () => {
            it('should add event for PermissionsRevoke', () => __awaiter(void 0, void 0, void 0, function* () {
                const alice = yield DidKeyResolver.generate();
                // Create a grant, adding one event
                const { permissionsGrant } = yield TestDataGenerator.generatePermissionsGrant({
                    author: alice,
                    grantedBy: alice.did,
                    grantedFor: alice.did,
                });
                const permissionsGrantReply = yield dwn.processMessage(alice.did, permissionsGrant.message);
                expect(permissionsGrantReply.status.code).to.eq(202);
                let events = yield eventLog.getEvents(alice.did);
                expect(events.length).to.equal(1);
                // Revoke the grant, adding a second event
                const { permissionsRevoke } = yield TestDataGenerator.generatePermissionsRevoke({
                    author: alice,
                    permissionsGrantId: yield Message.getCid(permissionsGrant.message),
                });
                const reply = yield dwn.processMessage(alice.did, permissionsRevoke.message);
                expect(reply.status.code).to.equal(202);
                events = yield eventLog.getEvents(alice.did);
                expect(events.length).to.equal(2);
                // The revoke should be the second event
                const messageCid = yield Message.getCid(permissionsRevoke.message);
                expect(events[1].messageCid).to.equal(messageCid);
            }));
            it('should remove events for existing PermissionsRevoke messages with timestamp after the incoming message', () => __awaiter(void 0, void 0, void 0, function* () {
                // scenario: A grant is issued, adding one event. Then the grant is revoked, adding another event.
                //           Then, a slightly earlier revoke is processed, causing the existing revoke to be purged.
                const alice = yield DidKeyResolver.generate();
                // Create a grant, adding one event
                const { permissionsGrant } = yield TestDataGenerator.generatePermissionsGrant({
                    author: alice,
                    grantedBy: alice.did,
                    grantedFor: alice.did,
                });
                const permissionsGrantReply = yield dwn.processMessage(alice.did, permissionsGrant.message);
                expect(permissionsGrantReply.status.code).to.eq(202);
                let events = yield eventLog.getEvents(alice.did);
                expect(events.length).to.equal(1);
                // Pre-create a Revoke message with earlier timestamp, to be processed later
                const { permissionsRevoke: permissionsRevoke1 } = yield TestDataGenerator.generatePermissionsRevoke({
                    author: alice,
                    permissionsGrantId: yield Message.getCid(permissionsGrant.message),
                });
                yield sleep(10);
                // Revoke the grant using a later timestamp than the pre-created revoke
                const { permissionsRevoke: permissionsRevoke2 } = yield TestDataGenerator.generatePermissionsRevoke({
                    author: alice,
                    permissionsGrantId: yield Message.getCid(permissionsGrant.message),
                });
                const permissionsRevokeReply2 = yield dwn.processMessage(alice.did, permissionsRevoke2.message);
                expect(permissionsRevokeReply2.status.code).to.eq(202);
                // An event is added for the revoke
                const permissionsRevokeCid2 = yield Message.getCid(permissionsRevoke2.message);
                events = yield eventLog.getEvents(alice.did);
                expect(events.length).to.equal(2);
                expect(events[1].messageCid).to.equal(permissionsRevokeCid2);
                // Process the pre-created Revoke
                const permissionsRevokeReply1 = yield dwn.processMessage(alice.did, permissionsRevoke1.message);
                expect(permissionsRevokeReply1.status.code).to.eq(202);
                // The existing Revoke event is purged from the eventLog. The pre-created Revoke is added to the eventLog
                const permissionsRevokeCid1 = yield Message.getCid(permissionsRevoke1.message);
                events = yield eventLog.getEvents(alice.did);
                expect(events.length).to.equal(2);
                expect(events[1].messageCid).to.equal(permissionsRevokeCid1);
            }));
        });
    });
});
//# sourceMappingURL=permissions-revoke.spec.js.map