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 emailProtocolDefinition from '../vectors/protocol-definitions/email.json' assert { type: 'json' };
import sinon from 'sinon';
import chai, { expect } from 'chai';
import { ArrayUtility } from '../../src/utils/array.js';
import { DidKeyResolver } from '../../src/did/did-key-resolver.js';
import { DwnConstant } from '../../src/core/dwn-constant.js';
import { DwnErrorCode } from '../../src/index.js';
import { Encoder } from '../../src/utils/encoder.js';
import { Encryption } from '../../src/index.js';
import { Jws } from '../../src/utils/jws.js';
import { Message } from '../../src/core/message.js';
import { RecordsQueryHandler } from '../../src/handlers/records-query.js';
import { stubInterface } from 'ts-sinon';
import { TestDataGenerator } from '../utils/test-data-generator.js';
import { TestStores } from '../test-stores.js';
import { TestStubGenerator } from '../utils/test-stub-generator.js';
import { toTemporalInstant } from '@js-temporal/polyfill';
import { constructRecordsWriteIndexes, RecordsWriteHandler } from '../../src/handlers/records-write.js';
import { DataStream, DidResolver, Dwn, HdKey, KeyDerivationScheme, Records } from '../../src/index.js';
import { DateSort, RecordsQuery } from '../../src/interfaces/records-query.js';
chai.use(chaiAsPromised);
function createDateString(d) {
    return toTemporalInstant.call(d).toString({ smallestUnit: 'microseconds' });
}
export function testRecordsQueryHandler() {
    describe('RecordsQueryHandler.handle()', () => {
        if (process.env.TESTSOFF === 'true') {
            return;
        }
        describe('functional tests', () => {
            let didResolver;
            let messageStore;
            let dataStore;
            let eventLog;
            let dwn;
            // important to follow the `before` and `after` pattern to initialize and clean the stores in tests
            // so that different test suites can reuse the same backend store for testing
            before(() => __awaiter(this, void 0, void 0, function* () {
                didResolver = new DidResolver([new DidKeyResolver()]);
                const stores = TestStores.get();
                messageStore = stores.messageStore;
                dataStore = stores.dataStore;
                eventLog = stores.eventLog;
                dwn = yield Dwn.create({ didResolver, messageStore, dataStore, eventLog });
            }));
            beforeEach(() => __awaiter(this, 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(this, void 0, void 0, function* () {
                yield dwn.close();
            }));
            it('should return records matching the query', () => __awaiter(this, void 0, void 0, function* () {
                var _a, _b;
                // insert three messages into DB, two with matching protocol
                const alice = yield TestDataGenerator.generatePersona();
                const dataFormat = 'myAwesomeDataFormat';
                const write1 = yield TestDataGenerator.generateRecordsWrite({ author: alice });
                const write2 = yield TestDataGenerator.generateRecordsWrite({ author: alice, dataFormat, schema: 'schema1' });
                const write3 = yield TestDataGenerator.generateRecordsWrite({ author: alice, dataFormat, schema: 'schema2' });
                // setting up a stub resolver
                const mockResolution = TestDataGenerator.createDidResolutionResult(alice);
                ;
                sinon.stub(didResolver, 'resolve').resolves(mockResolution);
                // insert data
                const writeReply1 = yield dwn.processMessage(alice.did, write1.message, write1.dataStream);
                const writeReply2 = yield dwn.processMessage(alice.did, write2.message, write2.dataStream);
                const writeReply3 = yield dwn.processMessage(alice.did, write3.message, write3.dataStream);
                expect(writeReply1.status.code).to.equal(202);
                expect(writeReply2.status.code).to.equal(202);
                expect(writeReply3.status.code).to.equal(202);
                // testing singular conditional query
                const messageData = yield TestDataGenerator.generateRecordsQuery({ author: alice, filter: { dataFormat } });
                const reply = yield dwn.processMessage(alice.did, messageData.message);
                expect(reply.status.code).to.equal(200);
                expect((_a = reply.entries) === null || _a === void 0 ? void 0 : _a.length).to.equal(2); // only 2 entries should match the query on protocol
                // testing multi-conditional query, reuse data generated above for bob
                const messageData2 = yield TestDataGenerator.generateRecordsQuery({
                    author: alice,
                    filter: {
                        dataFormat,
                        schema: 'schema1'
                    }
                });
                const reply2 = yield dwn.processMessage(alice.did, messageData2.message);
                expect(reply2.status.code).to.equal(200);
                expect((_b = reply2.entries) === null || _b === void 0 ? void 0 : _b.length).to.equal(1); // only 1 entry should match the query
            }));
            it('should return `encodedData` if data size is within the spec threshold', () => __awaiter(this, void 0, void 0, function* () {
                var _c;
                const data = TestDataGenerator.randomBytes(DwnConstant.maxDataSizeAllowedToBeEncoded); // within/on threshold
                const alice = yield DidKeyResolver.generate();
                const write = yield TestDataGenerator.generateRecordsWrite({ author: alice, data });
                const writeReply = yield dwn.processMessage(alice.did, write.message, write.dataStream);
                expect(writeReply.status.code).to.equal(202);
                const messageData = yield TestDataGenerator.generateRecordsQuery({ author: alice, filter: { recordId: write.message.recordId } });
                const reply = yield dwn.processMessage(alice.did, messageData.message);
                expect(reply.status.code).to.equal(200);
                expect((_c = reply.entries) === null || _c === void 0 ? void 0 : _c.length).to.equal(1);
                expect(reply.entries[0].encodedData).to.equal(Encoder.bytesToBase64Url(data));
            }));
            it('should not return `encodedData` if data size is greater then spec threshold', () => __awaiter(this, void 0, void 0, function* () {
                var _d;
                const data = TestDataGenerator.randomBytes(DwnConstant.maxDataSizeAllowedToBeEncoded + 1); // exceeding threshold
                const alice = yield DidKeyResolver.generate();
                const write = yield TestDataGenerator.generateRecordsWrite({ author: alice, data });
                const writeReply = yield dwn.processMessage(alice.did, write.message, write.dataStream);
                expect(writeReply.status.code).to.equal(202);
                const messageData = yield TestDataGenerator.generateRecordsQuery({ author: alice, filter: { recordId: write.message.recordId } });
                const reply = yield dwn.processMessage(alice.did, messageData.message);
                expect(reply.status.code).to.equal(200);
                expect((_d = reply.entries) === null || _d === void 0 ? void 0 : _d.length).to.equal(1);
                expect(reply.entries[0].encodedData).to.be.undefined;
            }));
            it('should be able to query by attester', () => __awaiter(this, void 0, void 0, function* () {
                var _e, _f, _g;
                // scenario: 2 records authored by alice, 1st attested by alice, 2nd attested by bob
                const alice = yield DidKeyResolver.generate();
                const bob = yield DidKeyResolver.generate();
                const recordsWrite1 = yield TestDataGenerator.generateRecordsWrite({ author: alice, attesters: [alice] });
                const recordsWrite2 = yield TestDataGenerator.generateRecordsWrite({ author: alice, attesters: [bob] });
                // insert data
                const writeReply1 = yield dwn.processMessage(alice.did, recordsWrite1.message, recordsWrite1.dataStream);
                const writeReply2 = yield dwn.processMessage(alice.did, recordsWrite2.message, recordsWrite2.dataStream);
                expect(writeReply1.status.code).to.equal(202);
                expect(writeReply2.status.code).to.equal(202);
                // testing attester filter
                const recordsQuery1 = yield TestDataGenerator.generateRecordsQuery({ author: alice, filter: { attester: alice.did } });
                const reply1 = yield dwn.processMessage(alice.did, recordsQuery1.message);
                expect((_e = reply1.entries) === null || _e === void 0 ? void 0 : _e.length).to.equal(1);
                const reply1Attester = Jws.getSignerDid(reply1.entries[0].attestation.signatures[0]);
                expect(reply1Attester).to.equal(alice.did);
                // testing attester + another filter
                const recordsQuery2 = yield TestDataGenerator.generateRecordsQuery({
                    author: alice,
                    filter: { attester: bob.did, schema: recordsWrite2.message.descriptor.schema }
                });
                const reply2 = yield dwn.processMessage(alice.did, recordsQuery2.message);
                expect((_f = reply2.entries) === null || _f === void 0 ? void 0 : _f.length).to.equal(1);
                const reply2Attester = Jws.getSignerDid(reply2.entries[0].attestation.signatures[0]);
                expect(reply2Attester).to.equal(bob.did);
                // testing attester filter that yields no results
                const carol = yield DidKeyResolver.generate();
                const recordsQuery3 = yield TestDataGenerator.generateRecordsQuery({ author: alice, filter: { attester: carol.did } });
                const reply3 = yield dwn.processMessage(alice.did, recordsQuery3.message);
                expect((_g = reply3.entries) === null || _g === void 0 ? void 0 : _g.length).to.equal(0);
            }));
            it('should be able to range query by `dateCreated`', () => __awaiter(this, void 0, void 0, function* () {
                var _h, _j, _k, _l;
                // scenario: 3 records authored by alice, created on first of 2021, 2022, and 2023 respectively, only the first 2 records share the same schema
                const firstDayOf2021 = createDateString(new Date(2021, 1, 1));
                const firstDayOf2022 = createDateString(new Date(2022, 1, 1));
                const firstDayOf2023 = createDateString(new Date(2023, 1, 1));
                const alice = yield DidKeyResolver.generate();
                const write1 = yield TestDataGenerator.generateRecordsWrite({ author: alice, dateCreated: firstDayOf2021, messageTimestamp: firstDayOf2021 });
                const write2 = yield TestDataGenerator.generateRecordsWrite({ author: alice, dateCreated: firstDayOf2022, messageTimestamp: firstDayOf2022 });
                const write3 = yield TestDataGenerator.generateRecordsWrite({ author: alice, dateCreated: firstDayOf2023, messageTimestamp: firstDayOf2023 });
                // insert data
                const writeReply1 = yield dwn.processMessage(alice.did, write1.message, write1.dataStream);
                const writeReply2 = yield dwn.processMessage(alice.did, write2.message, write2.dataStream);
                const writeReply3 = yield dwn.processMessage(alice.did, write3.message, write3.dataStream);
                expect(writeReply1.status.code).to.equal(202);
                expect(writeReply2.status.code).to.equal(202);
                expect(writeReply3.status.code).to.equal(202);
                // testing `from` range
                const lastDayOf2021 = createDateString(new Date(2021, 12, 31));
                const recordsQuery1 = yield TestDataGenerator.generateRecordsQuery({
                    author: alice,
                    filter: { dateCreated: { from: lastDayOf2021 } },
                    dateSort: DateSort.CreatedAscending
                });
                const reply1 = yield dwn.processMessage(alice.did, recordsQuery1.message);
                expect((_h = reply1.entries) === null || _h === void 0 ? void 0 : _h.length).to.equal(2);
                expect(reply1.entries[0].encodedData).to.equal(Encoder.bytesToBase64Url(write2.dataBytes));
                expect(reply1.entries[1].encodedData).to.equal(Encoder.bytesToBase64Url(write3.dataBytes));
                // testing `to` range
                const lastDayOf2022 = createDateString(new Date(2022, 12, 31));
                const recordsQuery2 = yield TestDataGenerator.generateRecordsQuery({
                    author: alice,
                    filter: { dateCreated: { to: lastDayOf2022 } },
                    dateSort: DateSort.CreatedAscending
                });
                const reply2 = yield dwn.processMessage(alice.did, recordsQuery2.message);
                expect((_j = reply2.entries) === null || _j === void 0 ? void 0 : _j.length).to.equal(2);
                expect(reply2.entries[0].encodedData).to.equal(Encoder.bytesToBase64Url(write1.dataBytes));
                expect(reply2.entries[1].encodedData).to.equal(Encoder.bytesToBase64Url(write2.dataBytes));
                // testing `from` and `to` range
                const lastDayOf2023 = createDateString(new Date(2023, 12, 31));
                const recordsQuery3 = yield TestDataGenerator.generateRecordsQuery({
                    author: alice,
                    filter: { dateCreated: { from: lastDayOf2022, to: lastDayOf2023 } },
                    dateSort: DateSort.CreatedAscending
                });
                const reply3 = yield dwn.processMessage(alice.did, recordsQuery3.message);
                expect((_k = reply3.entries) === null || _k === void 0 ? void 0 : _k.length).to.equal(1);
                expect(reply3.entries[0].encodedData).to.equal(Encoder.bytesToBase64Url(write3.dataBytes));
                // testing edge case where value equals `from` and `to`
                const recordsQuery4 = yield TestDataGenerator.generateRecordsQuery({
                    author: alice,
                    filter: { dateCreated: { from: firstDayOf2022, to: firstDayOf2023 } },
                    dateSort: DateSort.CreatedAscending
                });
                const reply4 = yield dwn.processMessage(alice.did, recordsQuery4.message);
                expect((_l = reply4.entries) === null || _l === void 0 ? void 0 : _l.length).to.equal(1);
                expect(reply4.entries[0].encodedData).to.equal(Encoder.bytesToBase64Url(write2.dataBytes));
            }));
            it('should be able use range and exact match queries at the same time', () => __awaiter(this, void 0, void 0, function* () {
                var _m;
                // scenario: 3 records authored by alice, created on first of 2021, 2022, and 2023 respectively, only the first 2 records share the same schema
                const firstDayOf2021 = createDateString(new Date(2021, 1, 1));
                const firstDayOf2022 = createDateString(new Date(2022, 1, 1));
                const firstDayOf2023 = createDateString(new Date(2023, 1, 1));
                const alice = yield DidKeyResolver.generate();
                const schema = '2021And2022Schema';
                const write1 = yield TestDataGenerator.generateRecordsWrite({
                    author: alice, dateCreated: firstDayOf2021, messageTimestamp: firstDayOf2021, schema
                });
                const write2 = yield TestDataGenerator.generateRecordsWrite({
                    author: alice, dateCreated: firstDayOf2022, messageTimestamp: firstDayOf2022, schema
                });
                const write3 = yield TestDataGenerator.generateRecordsWrite({
                    author: alice, dateCreated: firstDayOf2023, messageTimestamp: firstDayOf2023
                });
                // insert data
                const writeReply1 = yield dwn.processMessage(alice.did, write1.message, write1.dataStream);
                const writeReply2 = yield dwn.processMessage(alice.did, write2.message, write2.dataStream);
                const writeReply3 = yield dwn.processMessage(alice.did, write3.message, write3.dataStream);
                expect(writeReply1.status.code).to.equal(202);
                expect(writeReply2.status.code).to.equal(202);
                expect(writeReply3.status.code).to.equal(202);
                // testing range criterion with another exact match
                const lastDayOf2021 = createDateString(new Date(2021, 12, 31));
                const lastDayOf2023 = createDateString(new Date(2023, 12, 31));
                const recordsQuery5 = yield TestDataGenerator.generateRecordsQuery({
                    author: alice,
                    filter: {
                        schema,
                        dateCreated: { from: lastDayOf2021, to: lastDayOf2023 } // by itself selects the last 2 records
                    },
                    dateSort: DateSort.CreatedAscending
                });
                const reply = yield dwn.processMessage(alice.did, recordsQuery5.message);
                expect((_m = reply.entries) === null || _m === void 0 ? void 0 : _m.length).to.equal(1);
                expect(reply.entries[0].encodedData).to.equal(Encoder.bytesToBase64Url(write2.dataBytes));
            }));
            it('should not include `authorization` in returned records', () => __awaiter(this, void 0, void 0, function* () {
                var _o;
                const alice = yield TestDataGenerator.generatePersona();
                const { message, dataStream } = yield TestDataGenerator.generateRecordsWrite({ author: alice });
                // setting up a stub method resolver
                const mockResolution = TestDataGenerator.createDidResolutionResult(alice);
                sinon.stub(didResolver, 'resolve').resolves(mockResolution);
                const writeReply = yield dwn.processMessage(alice.did, message, dataStream);
                expect(writeReply.status.code).to.equal(202);
                const queryData = yield TestDataGenerator.generateRecordsQuery({
                    author: alice,
                    filter: { schema: message.descriptor.schema }
                });
                const queryReply = yield dwn.processMessage(alice.did, queryData.message);
                expect(queryReply.status.code).to.equal(200);
                expect((_o = queryReply.entries) === null || _o === void 0 ? void 0 : _o.length).to.equal(1);
                expect(queryReply.entries[0].authorization).to.equal(undefined);
            }));
            it('should include `attestation` in returned records', () => __awaiter(this, void 0, void 0, function* () {
                // scenario: alice and bob attest to a message alice authored
                var _p, _q, _r;
                const alice = yield DidKeyResolver.generate();
                const { message, dataStream } = yield TestDataGenerator.generateRecordsWrite({ author: alice, attesters: [alice] });
                const writeReply = yield dwn.processMessage(alice.did, message, dataStream);
                expect(writeReply.status.code).to.equal(202);
                const queryData = yield TestDataGenerator.generateRecordsQuery({
                    author: alice,
                    filter: { schema: message.descriptor.schema }
                });
                const queryReply = yield dwn.processMessage(alice.did, queryData.message);
                expect(queryReply.status.code).to.equal(200);
                expect((_p = queryReply.entries) === null || _p === void 0 ? void 0 : _p.length).to.equal(1);
                const recordsWriteMessage = queryReply.entries[0];
                expect((_r = (_q = recordsWriteMessage.attestation) === null || _q === void 0 ? void 0 : _q.signatures) === null || _r === void 0 ? void 0 : _r.length).to.equal(1);
            }));
            it('should omit records that are not published if `dateSort` sorts on `datePublished`', () => __awaiter(this, void 0, void 0, function* () {
                var _s, _t;
                // insert three messages into DB, two with matching protocol
                const alice = yield TestDataGenerator.generatePersona();
                const schema = 'aSchema';
                const publishedWriteData = yield TestDataGenerator.generateRecordsWrite({
                    author: alice, schema, published: true
                });
                const unpublishedWriteData = yield TestDataGenerator.generateRecordsWrite({
                    author: alice, schema
                });
                // setting up a stub method resolver
                const mockResolution = TestDataGenerator.createDidResolutionResult(alice);
                ;
                sinon.stub(didResolver, 'resolve').resolves(mockResolution);
                // insert data
                const publishedWriteReply = yield dwn.processMessage(alice.did, publishedWriteData.message, publishedWriteData.dataStream);
                const unpublishedWriteReply = yield dwn.processMessage(alice.did, unpublishedWriteData.message, unpublishedWriteData.dataStream);
                expect(publishedWriteReply.status.code).to.equal(202);
                expect(unpublishedWriteReply.status.code).to.equal(202);
                // test published date ascending sort does not include any records that is not published
                const publishedAscendingQueryData = yield TestDataGenerator.generateRecordsQuery({
                    author: alice,
                    dateSort: DateSort.PublishedAscending,
                    filter: { schema }
                });
                const publishedAscendingQueryReply = yield dwn.handleRecordsQuery(alice.did, publishedAscendingQueryData.message);
                expect((_s = publishedAscendingQueryReply.entries) === null || _s === void 0 ? void 0 : _s.length).to.equal(1);
                expect(publishedAscendingQueryReply.entries[0].descriptor['datePublished']).to.equal(publishedWriteData.message.descriptor.datePublished);
                // test published date scending sort does not include any records that is not published
                const publishedDescendingQueryData = yield TestDataGenerator.generateRecordsQuery({
                    author: alice,
                    dateSort: DateSort.PublishedDescending,
                    filter: { schema }
                });
                const publishedDescendingQueryReply = yield dwn.handleRecordsQuery(alice.did, publishedDescendingQueryData.message);
                expect((_t = publishedDescendingQueryReply.entries) === null || _t === void 0 ? void 0 : _t.length).to.equal(1);
                expect(publishedDescendingQueryReply.entries[0].descriptor['datePublished']).to.equal(publishedWriteData.message.descriptor.datePublished);
            }));
            it('should sort records if `dateSort` is specified', () => __awaiter(this, void 0, void 0, function* () {
                var _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5;
                // insert three messages into DB, two with matching protocol
                const alice = yield TestDataGenerator.generatePersona();
                const schema = 'aSchema';
                const published = true;
                const write1Data = yield TestDataGenerator.generateRecordsWrite({ author: alice, schema, published });
                const write2Data = yield TestDataGenerator.generateRecordsWrite({ author: alice, schema, published });
                const write3Data = yield TestDataGenerator.generateRecordsWrite({ author: alice, schema, published });
                // setting up a stub method resolver
                const mockResolution = TestDataGenerator.createDidResolutionResult(alice);
                ;
                sinon.stub(didResolver, 'resolve').resolves(mockResolution);
                // insert data, intentionally out of order
                const writeReply2 = yield dwn.processMessage(alice.did, write2Data.message, write2Data.dataStream);
                const writeReply1 = yield dwn.processMessage(alice.did, write1Data.message, write1Data.dataStream);
                const writeReply3 = yield dwn.processMessage(alice.did, write3Data.message, write3Data.dataStream);
                expect(writeReply1.status.code).to.equal(202);
                expect(writeReply2.status.code).to.equal(202);
                expect(writeReply3.status.code).to.equal(202);
                // createdAscending test
                const createdAscendingQueryData = yield TestDataGenerator.generateRecordsQuery({
                    author: alice,
                    dateSort: DateSort.CreatedAscending,
                    filter: { schema }
                });
                const createdAscendingQueryReply = yield dwn.handleRecordsQuery(alice.did, createdAscendingQueryData.message);
                expect((_u = createdAscendingQueryReply.entries) === null || _u === void 0 ? void 0 : _u[0].descriptor['dateCreated']).to.equal(write1Data.message.descriptor.dateCreated);
                expect((_v = createdAscendingQueryReply.entries) === null || _v === void 0 ? void 0 : _v[1].descriptor['dateCreated']).to.equal(write2Data.message.descriptor.dateCreated);
                expect((_w = createdAscendingQueryReply.entries) === null || _w === void 0 ? void 0 : _w[2].descriptor['dateCreated']).to.equal(write3Data.message.descriptor.dateCreated);
                // createdDescending test
                const createdDescendingQueryData = yield TestDataGenerator.generateRecordsQuery({
                    author: alice,
                    dateSort: DateSort.CreatedDescending,
                    filter: { schema }
                });
                const createdDescendingQueryReply = yield dwn.handleRecordsQuery(alice.did, createdDescendingQueryData.message);
                expect((_x = createdDescendingQueryReply.entries) === null || _x === void 0 ? void 0 : _x[0].descriptor['dateCreated']).to.equal(write3Data.message.descriptor.dateCreated);
                expect((_y = createdDescendingQueryReply.entries) === null || _y === void 0 ? void 0 : _y[1].descriptor['dateCreated']).to.equal(write2Data.message.descriptor.dateCreated);
                expect((_z = createdDescendingQueryReply.entries) === null || _z === void 0 ? void 0 : _z[2].descriptor['dateCreated']).to.equal(write1Data.message.descriptor.dateCreated);
                // publishedAscending test
                const publishedAscendingQueryData = yield TestDataGenerator.generateRecordsQuery({
                    author: alice,
                    dateSort: DateSort.PublishedAscending,
                    filter: { schema }
                });
                const publishedAscendingQueryReply = yield dwn.handleRecordsQuery(alice.did, publishedAscendingQueryData.message);
                expect((_0 = publishedAscendingQueryReply.entries) === null || _0 === void 0 ? void 0 : _0[0].descriptor['datePublished']).to.equal(write1Data.message.descriptor.datePublished);
                expect((_1 = publishedAscendingQueryReply.entries) === null || _1 === void 0 ? void 0 : _1[1].descriptor['datePublished']).to.equal(write2Data.message.descriptor.datePublished);
                expect((_2 = publishedAscendingQueryReply.entries) === null || _2 === void 0 ? void 0 : _2[2].descriptor['datePublished']).to.equal(write3Data.message.descriptor.datePublished);
                // publishedDescending test
                const publishedDescendingQueryData = yield TestDataGenerator.generateRecordsQuery({
                    author: alice,
                    dateSort: DateSort.PublishedDescending,
                    filter: { schema }
                });
                const publishedDescendingQueryReply = yield dwn.handleRecordsQuery(alice.did, publishedDescendingQueryData.message);
                expect((_3 = publishedDescendingQueryReply.entries) === null || _3 === void 0 ? void 0 : _3[0].descriptor['datePublished']).to.equal(write3Data.message.descriptor.datePublished);
                expect((_4 = publishedDescendingQueryReply.entries) === null || _4 === void 0 ? void 0 : _4[1].descriptor['datePublished']).to.equal(write2Data.message.descriptor.datePublished);
                expect((_5 = publishedDescendingQueryReply.entries) === null || _5 === void 0 ? void 0 : _5[2].descriptor['datePublished']).to.equal(write1Data.message.descriptor.datePublished);
            }));
            it('should allow an anonymous unauthenticated query to return published records', () => __awaiter(this, void 0, void 0, function* () {
                var _6;
                // write 2 records into Alice's DB:
                // 1st is unpublished
                // 2nd is published
                const alice = yield DidKeyResolver.generate();
                const record1Data = yield TestDataGenerator.generateRecordsWrite({ author: alice, schema: 'https://schema1', published: false });
                const record2Data = yield TestDataGenerator.generateRecordsWrite({ author: alice, schema: 'https://schema2', published: true });
                const recordsWrite1Reply = yield dwn.processMessage(alice.did, record1Data.message, record1Data.dataStream);
                expect(recordsWrite1Reply.status.code).to.equal(202);
                const recordsWrite2Reply = yield dwn.processMessage(alice.did, record2Data.message, record2Data.dataStream);
                expect(recordsWrite2Reply.status.code).to.equal(202);
                // test correctness for anonymous query
                const anonymousQueryMessageData = yield TestDataGenerator.generateRecordsQuery({
                    anonymous: true,
                    filter: { dateCreated: { from: '2000-01-01T10:20:30.123456Z' } }
                });
                // sanity check
                expect(anonymousQueryMessageData.message.authorization).to.not.exist;
                const replyToQuery = yield dwn.processMessage(alice.did, anonymousQueryMessageData.message);
                expect(replyToQuery.status.code).to.equal(200);
                expect((_6 = replyToQuery.entries) === null || _6 === void 0 ? void 0 : _6.length).to.equal(1);
                expect(replyToQuery.entries[0].descriptor.schema).to.equal('https://schema2');
            }));
            it('should only return published records and unpublished records that is meant for author', () => __awaiter(this, void 0, void 0, function* () {
                var _7, _8, _9, _10, _11;
                // write 4 records into Alice's DB:
                // 1st is unpublished authored by Alice
                // 2nd is also unpublished authored by Alice, but is meant for (has recipient as) Bob
                // 3rd is also unpublished but is authored by Bob
                // 4th is published
                const alice = yield DidKeyResolver.generate();
                const bob = yield DidKeyResolver.generate();
                const schema = 'schema1';
                const record1Data = yield TestDataGenerator.generateRecordsWrite({ author: alice, schema, data: Encoder.stringToBytes('1') });
                const record2Data = yield TestDataGenerator.generateRecordsWrite({ author: alice, schema, protocol: 'protocol', protocolPath: 'path', recipient: bob.did, data: Encoder.stringToBytes('2') });
                const record3Data = yield TestDataGenerator.generateRecordsWrite({ author: bob, schema, protocol: 'protocol', protocolPath: 'path', recipient: alice.did, data: Encoder.stringToBytes('3') });
                const record4Data = yield TestDataGenerator.generateRecordsWrite({ author: alice, schema, data: Encoder.stringToBytes('4'), published: true });
                // directly inserting data to datastore so that we don't have to setup to grant Bob permission to write to Alice's DWN
                const recordsWriteHandler = new RecordsWriteHandler(didResolver, messageStore, dataStore, eventLog);
                const additionalIndexes1 = yield constructRecordsWriteIndexes(record1Data.recordsWrite, true);
                yield recordsWriteHandler.putData(alice.did, record1Data.message, record1Data.dataStream);
                yield messageStore.put(alice.did, record1Data.message, additionalIndexes1);
                yield eventLog.append(alice.did, yield Message.getCid(record1Data.message));
                const additionalIndexes2 = yield constructRecordsWriteIndexes(record2Data.recordsWrite, true);
                yield recordsWriteHandler.putData(alice.did, record2Data.message, record2Data.dataStream);
                yield messageStore.put(alice.did, record2Data.message, additionalIndexes2);
                yield eventLog.append(alice.did, yield Message.getCid(record2Data.message));
                const additionalIndexes3 = yield constructRecordsWriteIndexes(record3Data.recordsWrite, true);
                yield recordsWriteHandler.putData(alice.did, record3Data.message, record3Data.dataStream);
                yield messageStore.put(alice.did, record3Data.message, additionalIndexes3);
                yield eventLog.append(alice.did, yield Message.getCid(record3Data.message));
                const additionalIndexes4 = yield constructRecordsWriteIndexes(record4Data.recordsWrite, true);
                yield recordsWriteHandler.putData(alice.did, record4Data.message, record4Data.dataStream);
                yield messageStore.put(alice.did, record4Data.message, additionalIndexes4);
                yield eventLog.append(alice.did, yield Message.getCid(record4Data.message));
                // test correctness for Bob's query
                const bobQueryMessageData = yield TestDataGenerator.generateRecordsQuery({
                    author: bob,
                    filter: { schema }
                });
                const replyToBob = yield dwn.processMessage(alice.did, bobQueryMessageData.message);
                expect(replyToBob.status.code).to.equal(200);
                expect((_7 = replyToBob.entries) === null || _7 === void 0 ? void 0 : _7.length).to.equal(3); // expect 3 records
                const privateRecordsForBob = (_8 = replyToBob.entries) === null || _8 === void 0 ? void 0 : _8.filter(message => message.encodedData === Encoder.stringToBase64Url('2'));
                const privateRecordsFromBob = (_9 = replyToBob.entries) === null || _9 === void 0 ? void 0 : _9.filter(message => message.encodedData === Encoder.stringToBase64Url('3'));
                const publicRecords = (_10 = replyToBob.entries) === null || _10 === void 0 ? void 0 : _10.filter(message => message.encodedData === Encoder.stringToBase64Url('4'));
                expect(privateRecordsForBob.length).to.equal(1);
                expect(privateRecordsFromBob.length).to.equal(1);
                expect(publicRecords.length).to.equal(1);
                // test correctness for Alice's query
                const aliceQueryMessageData = yield TestDataGenerator.generateRecordsQuery({
                    author: alice,
                    filter: { schema }
                });
                const replyToAliceQuery = yield dwn.processMessage(alice.did, aliceQueryMessageData.message);
                expect(replyToAliceQuery.status.code).to.equal(200);
                expect((_11 = replyToAliceQuery.entries) === null || _11 === void 0 ? void 0 : _11.length).to.equal(4); // expect all 4 records
            }));
            it('should treat records with `published` explicitly set to `false` as unpublished', () => __awaiter(this, void 0, void 0, function* () {
                var _12, _13;
                const alice = yield DidKeyResolver.generate();
                const bob = yield DidKeyResolver.generate();
                const schema = 'schema1';
                const unpublishedRecordsWrite = yield TestDataGenerator.generateRecordsWrite({ author: alice, schema, data: Encoder.stringToBytes('1'), published: false } // explicitly setting `published` to `false`
                );
                const result1 = yield dwn.processMessage(alice.did, unpublishedRecordsWrite.message, unpublishedRecordsWrite.dataStream);
                expect(result1.status.code).to.equal(202);
                // alice should be able to see the unpublished record
                const queryByAlice = yield TestDataGenerator.generateRecordsQuery({
                    author: alice,
                    filter: { schema }
                });
                const replyToAliceQuery = yield dwn.processMessage(alice.did, queryByAlice.message);
                expect(replyToAliceQuery.status.code).to.equal(200);
                expect((_12 = replyToAliceQuery.entries) === null || _12 === void 0 ? void 0 : _12.length).to.equal(1);
                // actual test: bob should not be able to see unpublished record
                const queryByBob = yield TestDataGenerator.generateRecordsQuery({
                    author: bob,
                    filter: { schema }
                });
                const replyToBobQuery = yield dwn.processMessage(alice.did, queryByBob.message);
                expect(replyToBobQuery.status.code).to.equal(200);
                expect((_13 = replyToBobQuery.entries) === null || _13 === void 0 ? void 0 : _13.length).to.equal(0);
            }));
            it('should allow DWN owner to use `recipient` as a filter in queries', () => __awaiter(this, void 0, void 0, function* () {
                const alice = yield DidKeyResolver.generate();
                const bob = yield DidKeyResolver.generate();
                const bobQueryMessageData = yield TestDataGenerator.generateRecordsQuery({
                    author: alice,
                    filter: { recipient: bob.did } // alice as the DWN owner querying bob's records
                });
                const replyToBobQuery = yield dwn.processMessage(alice.did, bobQueryMessageData.message);
                expect(replyToBobQuery.status.code).to.equal(200);
            }));
            it('should not fetch entries across tenants', () => __awaiter(this, void 0, void 0, function* () {
                var _14;
                // insert three messages into DB, two with matching schema
                const alice = yield DidKeyResolver.generate();
                const bob = yield DidKeyResolver.generate();
                const schema = 'myAwesomeSchema';
                const recordsWriteMessage1Data = yield TestDataGenerator.generateRecordsWrite({ author: alice, schema });
                const recordsWriteMessage2Data = yield TestDataGenerator.generateRecordsWrite({ author: bob, schema });
                const aliceQueryMessageData = yield TestDataGenerator.generateRecordsQuery({
                    author: alice,
                    filter: { schema }
                });
                // insert data into 2 different tenants
                yield dwn.processMessage(alice.did, recordsWriteMessage1Data.message, recordsWriteMessage1Data.dataStream);
                yield dwn.processMessage(bob.did, recordsWriteMessage2Data.message, recordsWriteMessage2Data.dataStream);
                const reply = yield dwn.processMessage(alice.did, aliceQueryMessageData.message);
                expect(reply.status.code).to.equal(200);
                expect((_14 = reply.entries) === null || _14 === void 0 ? void 0 : _14.length).to.equal(1);
            }));
            it('should return 400 if protocol is not normalized', () => __awaiter(this, void 0, void 0, function* () {
                const alice = yield DidKeyResolver.generate();
                // query for non-normalized protocol
                const recordsQuery = yield TestDataGenerator.generateRecordsQuery({
                    author: alice,
                    filter: { protocol: 'example.com/' },
                });
                // overwrite protocol because #create auto-normalizes protocol
                recordsQuery.message.descriptor.filter.protocol = 'example.com/';
                // Re-create auth because we altered the descriptor after signing
                recordsQuery.message.authorization = yield Message.signAsAuthorization(recordsQuery.message.descriptor, Jws.createSignatureInput(alice));
                // Send records write message
                const reply = yield dwn.processMessage(alice.did, recordsQuery.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(this, void 0, void 0, function* () {
                const alice = yield DidKeyResolver.generate();
                // query for non-normalized schema
                const recordsQuery = yield TestDataGenerator.generateRecordsQuery({
                    author: alice,
                    filter: { schema: 'example.com/' },
                });
                // overwrite schema because #create auto-normalizes schema
                recordsQuery.message.descriptor.filter.schema = 'example.com/';
                // Re-create auth because we altered the descriptor after signing
                recordsQuery.message.authorization = yield Message.signAsAuthorization(recordsQuery.message.descriptor, Jws.createSignatureInput(alice));
                // Send records write message
                const reply = yield dwn.processMessage(alice.did, recordsQuery.message);
                expect(reply.status.code).to.equal(400);
                expect(reply.status.detail).to.contain(DwnErrorCode.UrlSchemaNotNormalized);
            }));
            describe('encryption scenarios', () => {
                it('should only be able to decrypt record with a correct derived private key - `protocols` derivation scheme', () => __awaiter(this, void 0, void 0, function* () {
                    // scenario, Bob writes into Alice's DWN an encrypted "email", alice is able to decrypt it
                    // creating Alice and Bob persona and setting up a stub DID resolver
                    const alice = yield TestDataGenerator.generatePersona();
                    const bob = yield TestDataGenerator.generatePersona();
                    TestStubGenerator.stubDidResolver(didResolver, [alice, bob]);
                    // configure protocol
                    const protocolDefinition = emailProtocolDefinition;
                    const protocol = protocolDefinition.protocol;
                    const protocolsConfig = yield TestDataGenerator.generateProtocolsConfigure({
                        author: alice,
                        protocolDefinition
                    });
                    const protocolsConfigureReply = yield dwn.processMessage(alice.did, protocolsConfig.message, protocolsConfig.dataStream);
                    expect(protocolsConfigureReply.status.code).to.equal(202);
                    // encrypt bob's message
                    const bobMessageBytes = Encoder.stringToBytes('message from bob');
                    const bobMessageStream = DataStream.fromBytes(bobMessageBytes);
                    const dataEncryptionInitializationVector = TestDataGenerator.randomBytes(16);
                    const dataEncryptionKey = TestDataGenerator.randomBytes(32);
                    const bobMessageEncryptedStream = yield Encryption.aes256CtrEncrypt(dataEncryptionKey, dataEncryptionInitializationVector, bobMessageStream);
                    const bobMessageEncryptedBytes = yield DataStream.toBytes(bobMessageEncryptedStream);
                    // generate a `RecordsWrite` message from bob allowed by anyone
                    const encryptionInput = {
                        initializationVector: dataEncryptionInitializationVector,
                        key: dataEncryptionKey,
                        keyEncryptionInputs: [{
                                publicKeyId: alice.keyId,
                                publicKey: alice.keyPair.publicJwk,
                                derivationScheme: KeyDerivationScheme.Protocols
                            }]
                    };
                    const schema = protocolDefinition.types.email.schema;
                    const { message, dataStream } = yield TestDataGenerator.generateRecordsWrite({
                        author: bob,
                        protocol,
                        protocolPath: 'email',
                        schema,
                        dataFormat: protocolDefinition.types.email.dataFormats[0],
                        data: bobMessageEncryptedBytes,
                        encryptionInput
                    });
                    const bobWriteReply = yield dwn.processMessage(alice.did, message, dataStream);
                    expect(bobWriteReply.status.code).to.equal(202);
                    const recordsQuery = yield RecordsQuery.create({
                        filter: { schema },
                        authorizationSignatureInput: Jws.createSignatureInput(alice)
                    });
                    const queryReply = yield dwn.processMessage(alice.did, recordsQuery.message);
                    expect(queryReply.status.code).to.equal(200);
                    const unsignedRecordsWrite = queryReply.entries[0];
                    // test able to decrypt the message using the root key
                    const rootPrivateKey = {
                        rootKeyId: alice.keyId,
                        derivationScheme: KeyDerivationScheme.Protocols,
                        derivedPrivateKey: alice.keyPair.privateJwk
                    };
                    const cipherStream = DataStream.fromBytes(Encoder.base64UrlToBytes(unsignedRecordsWrite.encodedData));
                    const plaintextDataStream = yield Records.decrypt(unsignedRecordsWrite, rootPrivateKey, cipherStream);
                    const plaintextBytes = yield DataStream.toBytes(plaintextDataStream);
                    expect(ArrayUtility.byteArraysEqual(plaintextBytes, bobMessageBytes)).to.be.true;
                    // test able to decrypt the message using a derived key
                    const derivationPath = [KeyDerivationScheme.Protocols]; // the first path segment of `protocols` derivation scheme
                    const derivedPrivateKey = yield HdKey.derivePrivateKey(rootPrivateKey, derivationPath);
                    const cipherStream2 = DataStream.fromBytes(Encoder.base64UrlToBytes(unsignedRecordsWrite.encodedData));
                    const plaintextDataStream2 = yield Records.decrypt(unsignedRecordsWrite, derivedPrivateKey, cipherStream2);
                    const plaintextBytes2 = yield DataStream.toBytes(plaintextDataStream2);
                    expect(ArrayUtility.byteArraysEqual(plaintextBytes2, bobMessageBytes)).to.be.true;
                    // test able to decrypt the message using a key derived from a derived key
                    const protocolsUriDerivationPathSegment = [message.descriptor.protocol]; // the 2nd path segment of `protocols` derivation scheme
                    const derivedPrivateKey2 = yield HdKey.derivePrivateKey(derivedPrivateKey, protocolsUriDerivationPathSegment);
                    const cipherStream3 = DataStream.fromBytes(Encoder.base64UrlToBytes(unsignedRecordsWrite.encodedData));
                    const plaintextDataStream3 = yield Records.decrypt(unsignedRecordsWrite, derivedPrivateKey2, cipherStream3);
                    const plaintextBytes3 = yield DataStream.toBytes(plaintextDataStream3);
                    expect(ArrayUtility.byteArraysEqual(plaintextBytes3, bobMessageBytes)).to.be.true;
                    // test unable to decrypt the message if derived key has an unexpected path
                    const invalidDerivationPath = [KeyDerivationScheme.Protocols, protocol, 'invalidContextId'];
                    const inValidDescendantPrivateKey = yield HdKey.derivePrivateKey(rootPrivateKey, invalidDerivationPath);
                    yield expect(Records.decrypt(unsignedRecordsWrite, inValidDescendantPrivateKey, cipherStream)).to.be.rejectedWith(DwnErrorCode.RecordsInvalidAncestorKeyDerivationSegment);
                }));
            });
        });
        it('should return 401 if signature check fails', () => __awaiter(this, void 0, void 0, function* () {
            const { author, message } = yield TestDataGenerator.generateRecordsQuery();
            const tenant = author.did;
            // setting up a stub did resolver & message store
            // intentionally not supplying the public key so a different public key is generated to simulate invalid signature
            const mismatchingPersona = yield TestDataGenerator.generatePersona({ did: author.did, keyId: author.keyId });
            const didResolver = TestStubGenerator.createDidResolverStub(mismatchingPersona);
            const messageStore = stubInterface();
            const dataStore = stubInterface();
            const recordsQueryHandler = new RecordsQueryHandler(didResolver, messageStore, dataStore);
            const reply = yield recordsQueryHandler.handle({ tenant, message });
            expect(reply.status.code).to.equal(401);
        }));
        it('should return 400 if fail parsing the message', () => __awaiter(this, void 0, void 0, function* () {
            const { author, message } = yield TestDataGenerator.generateRecordsQuery();
            const tenant = author.did;
            // setting up a stub method resolver & message store
            const didResolver = TestStubGenerator.createDidResolverStub(author);
            const messageStore = stubInterface();
            const dataStore = stubInterface();
            const recordsQueryHandler = new RecordsQueryHandler(didResolver, messageStore, dataStore);
            // stub the `parse()` function to throw an error
            sinon.stub(RecordsQuery, 'parse').throws('anyError');
            const reply = yield recordsQueryHandler.handle({ tenant, message });
            expect(reply.status.code).to.equal(400);
        }));
    });
}
//# sourceMappingURL=records-query.spec.js.map