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 chaiAsPromised from 'chai-as-promised';
import sinon from 'sinon';
import chai, { expect } from 'chai';
import dexProtocolDefinition from '../../../vectors/protocol-definitions/dex.json' assert { type: 'json' };
import minimalProtocolDefinition from '../../../vectors/protocol-definitions/minimal.json' assert { type: 'json' };
import { DataStoreLevel } from '../../../../src/store/data-store-level.js';
import { DidKeyResolver } from '../../../../src/did/did-key-resolver.js';
import { EventLogLevel } from '../../../../src/event-log/event-log-level.js';
import { GeneralJwsSigner } from '../../../../src/jose/jws/general/signer.js';
import { lexicographicalCompare } from '../../../../src/utils/string.js';
import { Message } from '../../../../src/core/message.js';
import { MessageStoreLevel } from '../../../../src/store/message-store-level.js';
import { sleep } from '../../../../src/utils/time.js';
import { TestDataGenerator } from '../../../utils/test-data-generator.js';
import { TestStubGenerator } from '../../../utils/test-stub-generator.js';
import { DidResolver, Dwn, DwnErrorCode, Encoder, Jws } from '../../../../src/index.js';
chai.use(chaiAsPromised);
describe('ProtocolsConfigureHandler.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 allow a protocol definition with schema or dataFormat omitted', () => __awaiter(void 0, void 0, void 0, function* () {
            const alice = yield DidKeyResolver.generate();
            const protocolDefinition = minimalProtocolDefinition;
            const protocolsConfig = yield TestDataGenerator.generateProtocolsConfigure({
                author: alice,
                protocolDefinition,
            });
            const reply = yield dwn.processMessage(alice.did, protocolsConfig.message);
            expect(reply.status.code).to.equal(202);
        }));
        it('should return 400 if more than 1 signature is provided in `authorization`', () => __awaiter(void 0, void 0, void 0, function* () {
            const { author, message, protocolsConfigure } = yield TestDataGenerator.generateProtocolsConfigure();
            const tenant = author.did;
            // intentionally create more than one signature, which is not allowed
            const extraRandomPersona = yield TestDataGenerator.generatePersona();
            const signatureInput1 = Jws.createSignatureInput(author);
            const signatureInput2 = Jws.createSignatureInput(extraRandomPersona);
            const authorizationPayloadBytes = Encoder.objectToBytes(protocolsConfigure.authorizationPayload);
            const signer = yield GeneralJwsSigner.create(authorizationPayloadBytes, [signatureInput1, signatureInput2]);
            message.authorization = signer.getJws();
            TestStubGenerator.stubDidResolver(didResolver, [author]);
            const reply = yield dwn.processMessage(tenant, message);
            expect(reply.status.code).to.equal(400);
            expect(reply.status.detail).to.contain('expected no more than 1 signature');
        }));
        it('should return 401 if auth fails', () => __awaiter(void 0, void 0, void 0, function* () {
            const alice = yield DidKeyResolver.generate();
            alice.keyId = 'wrongValue'; // to fail authentication
            const { message } = yield TestDataGenerator.generateProtocolsConfigure({ 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 be able to overwrite existing protocol if timestamp is newer', () => __awaiter(void 0, void 0, void 0, function* () {
            var _a;
            const alice = yield DidKeyResolver.generate();
            const protocolDefinition = minimalProtocolDefinition;
            const oldProtocolsConfigure = yield TestDataGenerator.generateProtocolsConfigure({
                author: alice,
                protocolDefinition,
            });
            yield sleep(1);
            const middleProtocolsConfigure = yield TestDataGenerator.generateProtocolsConfigure({
                author: alice,
                protocolDefinition,
            });
            // first ProtocolsConfigure
            const reply1 = yield dwn.processMessage(alice.did, middleProtocolsConfigure.message, middleProtocolsConfigure.dataStream);
            expect(reply1.status.code).to.equal(202);
            // older messages will not overwrite the existing
            const reply2 = yield dwn.processMessage(alice.did, oldProtocolsConfigure.message, oldProtocolsConfigure.dataStream);
            expect(reply2.status.code).to.equal(409);
            // newer message can overwrite the existing message
            const newProtocolsConfigure = yield TestDataGenerator.generateProtocolsConfigure({
                author: alice,
                protocolDefinition,
            });
            const reply3 = yield dwn.processMessage(alice.did, newProtocolsConfigure.message, newProtocolsConfigure.dataStream);
            expect(reply3.status.code).to.equal(202);
            // only the newest protocol should remain
            const queryMessageData = yield TestDataGenerator.generateProtocolsQuery({
                author: alice,
                filter: { protocol: protocolDefinition.protocol }
            });
            const queryReply = yield dwn.processMessage(alice.did, queryMessageData.message);
            expect(queryReply.status.code).to.equal(200);
            expect((_a = queryReply.entries) === null || _a === void 0 ? void 0 : _a.length).to.equal(1);
        }));
        it('should only be able to overwrite existing protocol if new protocol is lexicographically larger and timestamps are identical', () => __awaiter(void 0, void 0, void 0, function* () {
            var _b;
            const alice = yield DidKeyResolver.generate();
            // Alter each protocol slightly to create lexicographic difference between them
            const protocolDefinition1 = Object.assign(Object.assign({}, minimalProtocolDefinition), { types: Object.assign(Object.assign({}, minimalProtocolDefinition.types), { foo1: { dataFormats: ['bar1'] } }) });
            const protocolDefinition2 = Object.assign(Object.assign({}, minimalProtocolDefinition), { types: Object.assign(Object.assign({}, minimalProtocolDefinition.types), { foo2: { dataFormats: ['bar2'] } }) });
            const protocolDefinition3 = Object.assign(Object.assign({}, minimalProtocolDefinition), { types: Object.assign(Object.assign({}, minimalProtocolDefinition.types), { foo3: { dataFormats: ['bar3'] } }) });
            // Create three `ProtocolsConfigure` with identical timestamp
            const messageData1 = yield TestDataGenerator.generateProtocolsConfigure({
                author: alice,
                protocolDefinition: protocolDefinition1
            });
            const messageData2 = yield TestDataGenerator.generateProtocolsConfigure({
                author: alice,
                protocolDefinition: protocolDefinition2,
                dateModified: messageData1.message.descriptor.dateModified
            });
            const messageData3 = yield TestDataGenerator.generateProtocolsConfigure({
                author: alice,
                protocolDefinition: protocolDefinition3,
                dateModified: messageData1.message.descriptor.dateModified
            });
            const messageDataWithCid = [];
            for (const messageData of [messageData1, messageData2, messageData3]) {
                const cid = yield Message.getCid(messageData.message);
                messageDataWithCid.push(Object.assign({ cid }, messageData));
            }
            // sort the message in lexicographic order
            const [lowestProtocolsConfigure, middleProtocolsConfigure, highestProtocolsConfigure] = messageDataWithCid.sort((messageDataA, messageDataB) => { return lexicographicalCompare(messageDataA.cid, messageDataB.cid); });
            // write the protocol with the middle lexicographic value
            const reply1 = yield dwn.processMessage(alice.did, middleProtocolsConfigure.message, middleProtocolsConfigure.dataStream);
            expect(reply1.status.code).to.equal(202);
            // test that the protocol with the smallest lexicographic value cannot be written
            const reply2 = yield dwn.processMessage(alice.did, lowestProtocolsConfigure.message, lowestProtocolsConfigure.dataStream);
            expect(reply2.status.code).to.equal(409);
            // test that the protocol with the largest lexicographic value can be written
            const reply3 = yield dwn.processMessage(alice.did, highestProtocolsConfigure.message, highestProtocolsConfigure.dataStream);
            expect(reply3.status.code).to.equal(202);
            // test that lower lexicographic protocol message is removed from DB and only the newer protocol message remains
            const queryMessageData = yield TestDataGenerator.generateProtocolsQuery({
                author: alice,
                filter: { protocol: protocolDefinition1.protocol }
            });
            const queryReply = yield dwn.processMessage(alice.did, queryMessageData.message);
            expect(queryReply.status.code).to.equal(200);
            expect((_b = queryReply.entries) === null || _b === void 0 ? void 0 : _b.length).to.equal(1);
        }));
        it('should return 400 if protocol is not normalized', () => __awaiter(void 0, void 0, void 0, function* () {
            const alice = yield DidKeyResolver.generate();
            // query for non-normalized protocol
            const protocolsConfig = yield TestDataGenerator.generateProtocolsConfigure({
                author: alice,
                protocolDefinition: minimalProtocolDefinition
            });
            // overwrite protocol because #create auto-normalizes protocol
            protocolsConfig.message.descriptor.definition.protocol = 'example.com/';
            // Re-create auth because we altered the descriptor after signing
            protocolsConfig.message.authorization = yield Message.signAsAuthorization(protocolsConfig.message.descriptor, Jws.createSignatureInput(alice));
            // Send records write message
            const reply = yield dwn.processMessage(alice.did, protocolsConfig.message);
            expect(reply.status.code).to.equal(400);
            expect(reply.status.detail).to.contain(DwnErrorCode.UrlProtocolNotNormalized);
        }));
        it('should return 400 if schema is not normalized', () => __awaiter(void 0, void 0, void 0, function* () {
            const alice = yield DidKeyResolver.generate();
            const protocolDefinition = dexProtocolDefinition;
            const protocolsConfig = yield TestDataGenerator.generateProtocolsConfigure({
                author: alice,
                protocolDefinition,
            });
            // overwrite schema because #create auto-normalizes schema
            protocolsConfig.message.descriptor.definition.types.ask.schema = 'ask';
            // Re-create auth because we altered the descriptor after signing
            protocolsConfig.message.authorization = yield Message.signAsAuthorization(protocolsConfig.message.descriptor, Jws.createSignatureInput(alice));
            // Send records write message
            const reply = yield dwn.processMessage(alice.did, protocolsConfig.message);
            expect(reply.status.code).to.equal(400);
            expect(reply.status.detail).to.contain(DwnErrorCode.UrlSchemaNotNormalized);
        }));
        describe('event log', () => {
            it('should add event for ProtocolsConfigure', () => __awaiter(void 0, void 0, void 0, function* () {
                const alice = yield DidKeyResolver.generate();
                const { message, dataStream } = yield TestDataGenerator.generateProtocolsConfigure({ author: alice });
                const reply = yield dwn.processMessage(alice.did, message, dataStream);
                expect(reply.status.code).to.equal(202);
                const events = yield eventLog.getEvents(alice.did);
                expect(events.length).to.equal(1);
                const messageCid = yield Message.getCid(message);
                expect(events[0].messageCid).to.equal(messageCid);
            }));
            it('should delete older ProtocolsConfigure events when one is overwritten', () => __awaiter(void 0, void 0, void 0, function* () {
                const alice = yield DidKeyResolver.generate();
                const oldestWrite = yield TestDataGenerator.generateProtocolsConfigure({ author: alice, protocolDefinition: minimalProtocolDefinition });
                yield sleep(1);
                const newestWrite = yield TestDataGenerator.generateProtocolsConfigure({ author: alice, protocolDefinition: minimalProtocolDefinition });
                let reply = yield dwn.processMessage(alice.did, oldestWrite.message, oldestWrite.dataStream);
                expect(reply.status.code).to.equal(202);
                reply = yield dwn.processMessage(alice.did, newestWrite.message, newestWrite.dataStream);
                expect(reply.status.code).to.equal(202);
                const events = yield eventLog.getEvents(alice.did);
                expect(events.length).to.equal(1);
                const newestMessageCid = yield Message.getCid(newestWrite.message);
                expect(events[0].messageCid).to.equal(newestMessageCid);
            }));
        });
    });
});
//# sourceMappingURL=protocols-configure.spec.js.map