"use strict";
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());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable import/first */
// Some hackery to get WebSocket in the global namespace on nodejs
// @todo: Find a nicer way to do this...
;
global.WebSocket = require('isomorphic-ws');
const chai_1 = require("chai");
const index_1 = require("./index");
const models_1 = require("./models");
const client = new index_1.Client();
const personSchema = {
    $id: 'https://example.com/person.schema.json',
    $schema: 'http://json-schema.org/draft-07/schema#',
    title: 'Person',
    type: 'object',
    required: ['ID'],
    properties: {
        ID: {
            type: 'string',
            description: "The instance's id.",
        },
        firstName: {
            type: 'string',
            description: "The person's first name.",
        },
        lastName: {
            type: 'string',
            description: "The person's last name.",
        },
        age: {
            description: 'Age in years which must be equal to or greater than zero.',
            type: 'integer',
            minimum: 0,
        },
    },
};
const createPerson = () => {
    return {
        ID: '',
        firstName: 'Adam',
        lastName: 'Doe',
        age: 21,
    };
};
describe('Client', function () {
    const threadId = index_1.ThreadID.fromRandom();
    const dbID = threadId.bytes();
    let dbKey;
    let dbAddr;
    describe('.newDB', () => {
        it('response should succeed', () => __awaiter(this, void 0, void 0, function* () {
            yield client.newDB(dbID);
        }));
    });
    describe('.newCollection', () => {
        it('response should be defined and be an empty object', () => __awaiter(this, void 0, void 0, function* () {
            const register = yield client.newCollection(dbID, 'Person', personSchema);
            chai_1.expect(register).to.be.undefined;
        }));
    });
    describe('.getDBInfo', () => {
        it('response should be defined and be an array of strings', () => __awaiter(this, void 0, void 0, function* () {
            const invites = yield client.getDBInfo(dbID);
            chai_1.expect(invites).to.not.be.undefined;
            chai_1.expect(invites[0].address).to.not.be.undefined;
            chai_1.expect(invites[0].key).to.not.be.undefined;
            dbKey = invites[0].key;
            dbAddr = invites[0].address;
            chai_1.expect(invites).to.not.be.empty;
        }));
    });
    describe.skip('.newDBFromAddr', () => {
        it('response should be defined and be an empty object', () => __awaiter(this, void 0, void 0, function* () {
            const start = yield client.newDBFromAddr(dbKey, dbAddr, [{ name: 'Person', schema: personSchema }]);
            chai_1.expect(start).to.be.undefined;
        }));
    });
    describe('.create', () => {
        it('response should contain a JSON parsable instancesList', () => __awaiter(this, void 0, void 0, function* () {
            const entities = yield client.create(dbID, 'Person', [createPerson()]);
            chai_1.expect(entities.length).to.equal(1);
        }));
    });
    describe('.save', () => {
        it('response should be defined and be an empty object', () => __awaiter(this, void 0, void 0, function* () {
            const person = createPerson();
            const entities = yield client.create(dbID, 'Person', [person]);
            chai_1.expect(entities.length).to.equal(1);
            person.ID = entities[0];
            person.age = 30;
            const save = yield client.save(dbID, 'Person', [person]);
            chai_1.expect(save).to.be.undefined;
        }));
    });
    describe('.delete', () => {
        it('response should be defined and be an empty object', () => __awaiter(this, void 0, void 0, function* () {
            const entities = yield client.create(dbID, 'Person', [createPerson()]);
            chai_1.expect(entities.length).to.equal(1);
            const personID = entities[0];
            const deleted = yield client.delete(dbID, 'Person', [personID]);
            chai_1.expect(deleted).to.be.undefined;
        }));
    });
    describe('.has', () => {
        it('response be an object with property "exists" equal to true', () => __awaiter(this, void 0, void 0, function* () {
            const entities = yield client.create(dbID, 'Person', [createPerson()]);
            // Here we 'test' a different approach where we didn't use generics above to create the instance...
            chai_1.expect(entities.length).to.equal(1);
            const personID = entities[0];
            const has = yield client.has(dbID, 'Person', [personID]);
            chai_1.expect(has).to.be.true;
        }));
    });
    describe('.find', () => {
        it('response should contain the same entity based on query', () => __awaiter(this, void 0, void 0, function* () {
            const frank = createPerson();
            frank.firstName = 'Frank';
            const entities = yield client.create(dbID, 'Person', [frank]);
            chai_1.expect(entities.length).to.equal(1);
            const personID = entities[0];
            const q = {
                ands: [
                    {
                        fieldPath: 'firstName',
                        operation: models_1.ComparisonJSON.Eq,
                        value: { string: frank.firstName },
                    },
                ],
            };
            const find = yield client.find(dbID, 'Person', q);
            chai_1.expect(find).to.not.be.undefined;
            const found = find.instancesList;
            chai_1.expect(found).to.have.length(1);
            const foundPerson = found.pop();
            chai_1.expect(foundPerson).to.not.be.undefined;
            chai_1.expect(foundPerson).to.have.property('firstName', 'Frank');
            chai_1.expect(foundPerson).to.have.property('lastName', 'Doe');
            chai_1.expect(foundPerson).to.have.property('age', 21);
            chai_1.expect(foundPerson).to.have.property('ID');
            chai_1.expect(foundPerson['ID']).to.equal(personID);
        }));
    });
    describe('.findById', () => {
        it('response should contain a JSON parsable instance property', () => __awaiter(this, void 0, void 0, function* () {
            const entities = yield client.create(dbID, 'Person', [createPerson()]);
            const personID = entities.pop();
            const find = yield client.findByID(dbID, 'Person', personID);
            chai_1.expect(find).to.not.be.undefined;
            chai_1.expect(find).to.haveOwnProperty('instance');
            const instance = find.instance;
            chai_1.expect(instance).to.not.be.undefined;
            chai_1.expect(instance).to.have.property('firstName', 'Adam');
            chai_1.expect(instance).to.have.property('lastName', 'Doe');
            chai_1.expect(instance).to.have.property('age', 21);
            chai_1.expect(instance).to.have.property('ID');
        }));
    });
    describe('.readTransaction', () => {
        let existingPersonID;
        let transaction;
        before(() => __awaiter(this, void 0, void 0, function* () {
            const entities = yield client.create(dbID, 'Person', [createPerson()]);
            existingPersonID = entities.pop();
            transaction = client.readTransaction(dbID, 'Person');
        }));
        it('should start a transaction', () => __awaiter(this, void 0, void 0, function* () {
            chai_1.expect(transaction).to.not.be.undefined;
            yield transaction.start();
        }));
        it('should able to check for an existing instance', () => __awaiter(this, void 0, void 0, function* () {
            const has = yield transaction.has([existingPersonID]);
            chai_1.expect(has).to.be.true;
        }));
        it('should be able to find an existing instance', () => __awaiter(this, void 0, void 0, function* () {
            const find = yield transaction.findByID(existingPersonID);
            chai_1.expect(find).to.not.be.undefined;
            chai_1.expect(find).to.haveOwnProperty('instance');
            const instance = find.instance;
            chai_1.expect(instance).to.not.be.undefined;
            chai_1.expect(instance).to.have.property('firstName', 'Adam');
            chai_1.expect(instance).to.have.property('lastName', 'Doe');
            chai_1.expect(instance).to.have.property('age', 21);
            chai_1.expect(instance).to.have.property('ID');
            chai_1.expect(instance['ID']).to.deep.equal(existingPersonID);
        }));
        it('should be able to close/end an transaction', () => __awaiter(this, void 0, void 0, function* () {
            yield transaction.end();
        }));
    });
    describe('.writeTransaction', () => {
        const person = createPerson();
        let existingPersonID;
        let transaction;
        before(() => __awaiter(this, void 0, void 0, function* () {
            const entities = yield client.create(dbID, 'Person', [person]);
            existingPersonID = entities.length ? entities[0] : '';
            person['ID'] = existingPersonID;
            transaction = client.writeTransaction(dbID, 'Person');
        }));
        it('should start a transaction', () => __awaiter(this, void 0, void 0, function* () {
            chai_1.expect(transaction).to.not.be.undefined;
            yield transaction.start();
        }));
        it('should be able to create an instance', () => __awaiter(this, void 0, void 0, function* () {
            const newPerson = createPerson();
            const entities = yield transaction.create([newPerson]);
            chai_1.expect(entities).to.not.be.undefined;
            chai_1.expect(entities.length).to.equal(1);
        }));
        it('should able to check for an existing instance', () => __awaiter(this, void 0, void 0, function* () {
            const has = yield transaction.has([existingPersonID]);
            chai_1.expect(has).to.be.true;
        }));
        it('should be able to find an existing instance', () => __awaiter(this, void 0, void 0, function* () {
            const find = yield transaction.findByID(existingPersonID);
            chai_1.expect(find).to.not.be.undefined;
            chai_1.expect(find).to.haveOwnProperty('instance');
            const instance = find.instance;
            chai_1.expect(instance).to.not.be.undefined;
            chai_1.expect(instance).to.have.property('firstName', 'Adam');
            chai_1.expect(instance).to.have.property('lastName', 'Doe');
            chai_1.expect(instance).to.have.property('age', 21);
            chai_1.expect(instance).to.have.property('ID');
            chai_1.expect(instance['ID']).to.deep.equal(existingPersonID);
        }));
        it('should be able to save an existing instance', () => __awaiter(this, void 0, void 0, function* () {
            person.age = 99;
            const saved = yield transaction.save([person]);
            chai_1.expect(saved).to.be.undefined;
            const deleted = yield transaction.delete([person.ID]);
            chai_1.expect(deleted).to.be.undefined;
        }));
        it('should be able to close/end an transaction', () => __awaiter(this, void 0, void 0, function* () {
            yield transaction.end();
        }));
    });
    describe('.listen', () => {
        const listener = { events: 0 };
        const person = createPerson();
        let existingPersonID;
        before(() => __awaiter(this, void 0, void 0, function* () {
            const entities = yield client.create(dbID, 'Person', [person]);
            existingPersonID = entities.length ? entities[0] : '';
            person['ID'] = existingPersonID;
        }));
        it('should stream responses.', (done) => {
            const callback = (reply, err) => {
                if (err) {
                    throw err;
                }
                const instance = reply === null || reply === void 0 ? void 0 : reply.instance;
                chai_1.expect(instance).to.not.be.undefined;
                chai_1.expect(instance).to.have.property('age');
                chai_1.expect(instance === null || instance === void 0 ? void 0 : instance.age).to.be.greaterThan(21);
                listener.events += 1;
                if (listener.events == 2) {
                    done();
                }
                if (listener.events > 1 && listener.close) {
                    listener.close();
                }
            };
            listener.close = client.listen(dbID, [
                {
                    instanceID: existingPersonID,
                },
            ], callback);
            person.age = 30;
            client.save(dbID, 'Person', [person]);
            person.age = 40;
            client.save(dbID, 'Person', [person]);
        }).timeout(25000); // Make sure our test doesn't timeout
    });
    describe('Query', () => {
        before(() => __awaiter(this, void 0, void 0, function* () {
            const people = [...Array(8)].map((_, i) => {
                const person = createPerson();
                person.age = 60 + i;
                return person;
            });
            yield client.create(dbID, 'Person', people);
        }));
        it('Should return a full list of entities matching the given query', () => __awaiter(this, void 0, void 0, function* () {
            const q = new models_1.Where('age').ge(60).and('age').lt(66).or(new models_1.Where('age').eq(67));
            const find = yield client.find(dbID, 'Person', q);
            chai_1.expect(find).to.not.be.undefined;
            const found = find.instancesList;
            chai_1.expect(found).to.have.length(7);
        }));
    });
});
