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 { DataStoreLevel } from '../src/store/data-store-level.js';
import { DidKeyResolver } from '../src/did/did-key-resolver.js';
import { Dwn } from '../src/dwn.js';
import { Encoder } from '../src/index.js';
import { EventLogLevel } from '../src/event-log/event-log-level.js';
import { MessageStoreLevel } from '../src/store/message-store-level.js';
import { TestDataGenerator } from './utils/test-data-generator.js';
import { DwnInterfaceName, DwnMethodName, Message } from '../src/core/message.js';
import { Jws, RecordsRead } from '../src/index.js';
chai.use(chaiAsPromised);
describe('ID++ DWN', () => {
    if (process.env.TESTSOFF === 'true') {
        return;
    }
    let messageStore;
    let dataStore;
    let eventLog;
    let dwn;
    before(() => __awaiter(void 0, void 0, void 0, function* () {
        // important to follow this pattern to initialize the message 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({ messageStore, dataStore, eventLog });
    }));
    beforeEach(() => __awaiter(void 0, void 0, void 0, function* () {
        sinon.restore(); // wipe all stubs/spies/mocks/fakes from previous test
        yield messageStore.clear(); // clean up before each test rather than after so that a test does not depend on other tests to do the clean up
    }));
    after(() => __awaiter(void 0, void 0, void 0, function* () {
        yield dwn.close();
    }));
    describe('create()', () => {
        it('should be able to initialize a DWN with undefined config', () => __awaiter(void 0, void 0, void 0, function* () {
            const dwnWithoutConfig = yield Dwn.create(); // without passing in a config
            const alice = yield DidKeyResolver.generate();
            const { author, message } = yield TestDataGenerator.generateRecordsQuery({ author: alice });
            const tenant = author.did;
            const reply = yield dwnWithoutConfig.processMessage(tenant, message);
            expect(reply.status.code).to.equal(200);
            expect(reply.entries).to.be.empty;
        }));
    });
    describe('processMessage()', () => {
        it('should process RecordsWrite message signed by a `did:key` DID', () => __awaiter(void 0, void 0, void 0, function* () {
            // generate a `did:key` DID
            const alice = yield DidKeyResolver.generate();
            const { message, dataStream } = yield TestDataGenerator.generateRecordsWrite({
                author: alice,
            });
            const reply = yield dwn.processMessage(alice.did, message, dataStream);
            expect(reply.status.code).to.equal(202);
        }));
        it('should process RecordsQuery message', () => __awaiter(void 0, void 0, void 0, function* () {
            const alice = yield DidKeyResolver.generate();
            const { author, message } = yield TestDataGenerator.generateRecordsQuery({ author: alice });
            const tenant = author.did;
            const reply = yield dwn.processMessage(tenant, message);
            expect(reply.status.code).to.equal(200);
            expect(reply.entries).to.be.empty;
        }));
        it('should process an EventsGet message', () => __awaiter(void 0, void 0, void 0, function* () {
            const alice = yield DidKeyResolver.generate();
            const { message } = yield TestDataGenerator.generateEventsGet({ author: alice });
            const reply = yield dwn.processMessage(alice.did, message);
            expect(reply.status.code).to.equal(200);
            expect(reply.events).to.be.empty;
            // expect(reply['data']).to.not.exist;
            expect(reply.data).to.not.exist;
        }));
        it('regression - should run JSON schema validation', () => __awaiter(void 0, void 0, void 0, function* () {
            const invalidMessage = {
                descriptor: {
                    interface: 'Records',
                    method: 'Write',
                },
                authorization: {}
            };
            const validateJsonSchemaSpy = sinon.spy(Message, 'validateJsonSchema');
            const alice = yield DidKeyResolver.generate();
            const reply = yield dwn.processMessage(alice.did, invalidMessage);
            sinon.assert.calledOnce(validateJsonSchemaSpy);
            expect(reply.status.code).to.equal(400);
            expect(reply.status.detail).to.contain(`must have required property 'recordId'`);
        }));
        it('should throw 400 if given no interface or method found in message', () => __awaiter(void 0, void 0, void 0, function* () {
            const alice = yield DidKeyResolver.generate();
            const reply1 = yield dwn.processMessage(alice.did, undefined); // missing message entirely, thus missing both `interface` and `method`
            expect(reply1.status.code).to.equal(400);
            expect(reply1.status.detail).to.contain('Both interface and method must be present');
            const reply2 = yield dwn.processMessage(alice.did, { descriptor: { method: 'anyValue' } }); // missing `interface`
            expect(reply2.status.code).to.equal(400);
            expect(reply2.status.detail).to.contain('Both interface and method must be present');
            const reply3 = yield dwn.processMessage(alice.did, { descriptor: { interface: 'anyValue' } }); // missing `method`
            expect(reply3.status.code).to.equal(400);
            expect(reply3.status.detail).to.contain('Both interface and method must be present');
        }));
        it('should throw 401 if message is targeted at a non-tenant', () => __awaiter(void 0, void 0, void 0, function* () {
            // tenant gate that blocks everyone
            const blockAllTenantGate = {
                isTenant() {
                    return __awaiter(this, void 0, void 0, function* () {
                        return false;
                    });
                }
            };
            const messageStoreStub = sinon.createStubInstance(MessageStoreLevel);
            const dataStoreStub = sinon.createStubInstance(DataStoreLevel);
            const eventLogStub = sinon.createStubInstance(EventLogLevel);
            const dwnWithConfig = yield Dwn.create({
                tenantGate: blockAllTenantGate,
                messageStore: messageStoreStub,
                dataStore: dataStoreStub,
                eventLog: eventLogStub
            });
            const alice = yield DidKeyResolver.generate();
            const { author, message } = yield TestDataGenerator.generateRecordsQuery({ author: alice });
            const tenant = author.did;
            const reply = yield dwnWithConfig.processMessage(tenant, message);
            expect(reply.status.code).to.equal(401);
            expect(reply.status.detail).to.contain('not a tenant');
        }));
    });
    describe('handleRecordsRead', () => {
        it('should return error if preprocessing checks fail', () => __awaiter(void 0, void 0, void 0, function* () {
            const alice = yield DidKeyResolver.generate();
            const recordsRead = yield RecordsRead.create({
                recordId: 'recordId-doesnt-matter',
                authorizationSignatureInput: Jws.createSignatureInput(alice)
            });
            recordsRead.message.descriptor.method = 'Write'; // Will cause interface and method check to fail
            const reply = yield dwn.handleRecordsRead(alice.did, recordsRead.message);
            expect(reply.status.code).to.not.equal(200);
        }));
    });
    describe('handleMessagesGet', () => {
        // increases test coverage :)
        it('runs successfully', () => __awaiter(void 0, void 0, void 0, function* () {
            const did = yield DidKeyResolver.generate();
            const alice = yield TestDataGenerator.generatePersona(did);
            const messageCids = [];
            const { recordsWrite, dataStream } = yield TestDataGenerator.generateRecordsWrite({
                author: alice
            });
            const messageCid = yield Message.getCid(recordsWrite.message);
            messageCids.push(messageCid);
            const reply = yield dwn.processMessage(alice.did, recordsWrite.toJSON(), dataStream);
            expect(reply.status.code).to.equal(202);
            const { messagesGet } = yield TestDataGenerator.generateMessagesGet({
                author: alice,
                messageCids
            });
            const messagesGetReply = yield dwn.handleMessagesGet(alice.did, messagesGet.message);
            expect(messagesGetReply.status.code).to.equal(200);
            expect(messagesGetReply.messages.length).to.equal(messageCids.length);
            for (const messageReply of messagesGetReply.messages) {
                expect(messageReply.messageCid).to.not.be.undefined;
                expect(messageReply.message).to.not.be.undefined;
                expect(messageCids).to.include(messageReply.messageCid);
                const cid = yield Message.getCid(messageReply.message);
                expect(messageReply.messageCid).to.equal(cid);
            }
        }));
        it('should return error if preprocessing checks fail', () => __awaiter(void 0, void 0, void 0, function* () {
            const alice = yield DidKeyResolver.generate();
            const { recordsWrite } = yield TestDataGenerator.generateRecordsWrite({
                author: alice
            });
            const messageCids = [yield Message.getCid(recordsWrite.message)];
            const { messagesGet } = yield TestDataGenerator.generateMessagesGet({
                author: alice,
                messageCids
            });
            messagesGet.message.descriptor.interface = 'Protocols'; // Will cause interface and method check to fail
            const reply = yield dwn.handleMessagesGet(alice.did, messagesGet.message);
            expect(reply.status.code).to.not.equal(200);
        }));
    });
    describe('synchronizePrunedInitialRecordsWrite()', () => {
        it('should allow an initial `RecordsWrite` to be written without supplying data', () => __awaiter(void 0, void 0, void 0, function* () {
            var _a, _b;
            const alice = yield DidKeyResolver.generate();
            const { recordsWrite } = yield TestDataGenerator.generateRecordsWrite({ author: alice });
            // simulate synchronize of pruned initial `RecordsWrite`
            const reply = yield dwn.synchronizePrunedInitialRecordsWrite(alice.did, recordsWrite.message);
            expect(reply.status.code).to.equal(202);
            // verify `RecordsWrite` inserted can be queried but without the data returned
            const recordsQueryMessageData = yield TestDataGenerator.generateRecordsQuery({
                author: alice,
                filter: { recordId: recordsWrite.message.recordId }
            });
            const recordsQueryReply = yield dwn.processMessage(alice.did, recordsQueryMessageData.message);
            expect(recordsQueryReply.status.code).to.equal(200);
            expect((_a = recordsQueryReply.entries) === null || _a === void 0 ? void 0 : _a.length).to.equal(1);
            expect(recordsQueryReply.entries[0].encodedData).to.not.exist;
            // generate and write a new `RecordsWrite` to overwrite the existing record
            const newDataBytes = Encoder.stringToBytes('new data');
            const newDataEncoded = Encoder.bytesToBase64Url(newDataBytes);
            const newRecordsWrite = yield TestDataGenerator.generateFromRecordsWrite({
                author: alice,
                existingWrite: recordsWrite,
                data: newDataBytes
            });
            const newRecordsWriteReply = yield dwn.processMessage(alice.did, newRecordsWrite.message, newRecordsWrite.dataStream);
            expect(newRecordsWriteReply.status.code).to.equal(202);
            // verify new `RecordsWrite` has overwritten the existing record with new data
            const newRecordsQueryReply = yield dwn.processMessage(alice.did, recordsQueryMessageData.message);
            expect(newRecordsQueryReply.status.code).to.equal(200);
            expect((_b = newRecordsQueryReply.entries) === null || _b === void 0 ? void 0 : _b.length).to.equal(1);
            expect(newRecordsQueryReply.entries[0].encodedData).to.equal(newDataEncoded);
        }));
        it('should throw 401 if message is targeted at a non-tenant', () => __awaiter(void 0, void 0, void 0, function* () {
            // tenant gate that blocks everyone
            const blockAllTenantGate = {
                isTenant() {
                    return __awaiter(this, void 0, void 0, function* () {
                        return false;
                    });
                }
            };
            const messageStoreStub = sinon.createStubInstance(MessageStoreLevel);
            const dataStoreStub = sinon.createStubInstance(DataStoreLevel);
            const eventLogStub = sinon.createStubInstance(EventLogLevel);
            const dwnWithConfig = yield Dwn.create({
                tenantGate: blockAllTenantGate,
                messageStore: messageStoreStub,
                dataStore: dataStoreStub,
                eventLog: eventLogStub
            });
            const alice = yield DidKeyResolver.generate();
            const { message } = yield TestDataGenerator.generateRecordsWrite({ author: alice });
            const reply = yield dwnWithConfig.synchronizePrunedInitialRecordsWrite(alice.did, message);
            expect(reply.status.code).to.equal(401);
            expect(reply.status.detail).to.contain('not a tenant');
        }));
        it('should run JSON schema validation', () => __awaiter(void 0, void 0, void 0, function* () {
            const invalidMessage = {
                descriptor: {
                    interface: 'Records',
                    method: 'Write'
                },
                authorization: {}
            };
            const validateJsonSchemaSpy = sinon.spy(Message, 'validateJsonSchema');
            const alice = yield DidKeyResolver.generate();
            const reply = yield dwn.synchronizePrunedInitialRecordsWrite(alice.did, invalidMessage);
            sinon.assert.calledOnce(validateJsonSchemaSpy);
            expect(reply.status.code).to.equal(400);
            expect(reply.status.detail).to.contain(`must have required property 'recordId'`);
        }));
        it('should throw 400 if given incorrect DWN interface or method', () => __awaiter(void 0, void 0, void 0, function* () {
            const alice = yield DidKeyResolver.generate();
            const reply1 = yield dwn.synchronizePrunedInitialRecordsWrite(alice.did, undefined); // missing message
            expect(reply1.status.code).to.equal(400);
            expect(reply1.status.detail).to.contain('Both interface and method must be present');
            const reply2 = yield dwn.synchronizePrunedInitialRecordsWrite(alice.did, { descriptor: { interface: 'IncorrectInterface', method: DwnMethodName.Write } });
            expect(reply2.status.code).to.equal(400);
            expect(reply2.status.detail).to.contain(`Expected interface ${DwnInterfaceName.Records}`);
            const reply3 = yield dwn.synchronizePrunedInitialRecordsWrite(alice.did, { descriptor: { interface: DwnInterfaceName.Records, method: 'IncorrectMethod' } });
            expect(reply3.status.code).to.equal(400);
            expect(reply3.status.detail).to.contain(`Expected method ${DwnInterfaceName.Records}${DwnMethodName.Write}`);
        }));
    });
});
//# sourceMappingURL=dwn.spec.js.map