"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());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint-disable import/first */
// Some hackery to get WebSocket in the global namespace on nodejs
;
global.WebSocket = require('isomorphic-ws');
const libp2p_crypto_1 = require("libp2p-crypto");
const chai_1 = require("chai");
const peer_id_1 = __importDefault(require("peer-id"));
const threads_core_1 = require("@textile/threads-core");
const context_1 = require("@textile/context");
const threads_encoding_1 = require("@textile/threads-encoding");
const _1 = require(".");
const proxyAddr1 = 'http://127.0.0.1:6007';
const proxyAddr2 = 'http://127.0.0.1:6207';
const ed25519 = libp2p_crypto_1.keys.supportedKeys.ed25519;
function createThread(client) {
    return __awaiter(this, void 0, void 0, function* () {
        const id = threads_core_1.ThreadID.fromRandom(threads_core_1.ThreadID.Variant.Raw, 32);
        const threadKey = threads_core_1.ThreadKey.fromRandom();
        return client.createThread(id, { threadKey });
    });
}
function threadAddr(hostAddr, hostID, info) {
    const pa = new threads_core_1.Multiaddr(`/p2p/${hostID.toB58String()}`);
    const ta = new threads_core_1.Multiaddr(`/thread/${info.id.toString()}`);
    return hostAddr.encapsulate(pa.encapsulate(ta));
}
describe('Network Client...', () => {
    let client;
    let token;
    let identity;
    before(() => __awaiter(void 0, void 0, void 0, function* () {
        client = new _1.Client(new context_1.Context(proxyAddr1));
        identity = yield threads_core_1.Libp2pCryptoIdentity.fromRandom();
        token = yield client.getToken(identity);
    }));
    describe('Basic...', () => {
        it('should return a remote host peer id', () => __awaiter(void 0, void 0, void 0, function* () {
            const id = yield client.getHostID();
            chai_1.expect(id).to.be.instanceOf(peer_id_1.default);
            chai_1.expect(peer_id_1.default.isPeerId(id)).to.be.true;
        }));
        it('should create a remote thread', () => __awaiter(void 0, void 0, void 0, function* () {
            const id = threads_core_1.ThreadID.fromRandom(threads_core_1.ThreadID.Variant.Raw, 32);
            const threadKey = threads_core_1.ThreadKey.fromRandom();
            const info = yield client.createThread(id, { threadKey });
            chai_1.expect(info.id.toString()).to.equal(id.toString());
            chai_1.expect(info.key).to.not.be.undefined;
        })).timeout(5000);
        it('should add a remote thread', () => __awaiter(void 0, void 0, void 0, function* () {
            const hostID = yield client.getHostID();
            const info1 = yield createThread(client);
            const hostAddr = new threads_core_1.Multiaddr('/dns4/threads1/tcp/4006');
            const addr = threadAddr(hostAddr, hostID, info1);
            const client2 = new _1.Client(new context_1.Context(proxyAddr2));
            // Create temporary identity
            const identity = yield threads_core_1.Libp2pCryptoIdentity.fromRandom();
            const token2 = yield client2.getToken(identity);
            try {
                const info2 = yield client2.addThread(addr, { threadKey: info1.key });
                chai_1.expect(info2.id.toString()).to.equal(info1.id.toString());
            }
            catch (err) {
                throw new Error(`unexpected error: ${err}`);
            }
        }));
        it('should add and then get a remote thread', () => __awaiter(void 0, void 0, void 0, function* () {
            const info1 = yield createThread(client);
            const info2 = yield client.getThread(info1.id);
            chai_1.expect(info2.id.toString()).to.equal(info1.id.toString());
        }));
        it('should pull a thread for records', () => __awaiter(void 0, void 0, void 0, function* () {
            const info = yield createThread(client);
            try {
                yield client.pullThread(info.id);
            }
            catch (err) {
                throw new Error(`unexpected error: ${err}`);
            }
        }));
        it('should delete an existing thread', () => __awaiter(void 0, void 0, void 0, function* () {
            const info = yield createThread(client);
            try {
                yield client.deleteThread(info.id);
            }
            catch (err) {
                throw new Error(`unexpected error: ${err}`);
            }
        }));
        it('should add a replicator to a thread', () => __awaiter(void 0, void 0, void 0, function* () {
            const client2 = new _1.Client(new context_1.Context(proxyAddr2));
            const hostID2 = yield client2.getHostID();
            const hostAddr2 = new threads_core_1.Multiaddr(`/dns4/threads2/tcp/4006`);
            const info1 = yield createThread(client);
            const peerAddr = hostAddr2.encapsulate(new threads_core_1.Multiaddr(`/p2p/${hostID2}`));
            const pid = yield client.addReplicator(info1.id, peerAddr);
            chai_1.expect(pid.toB58String()).to.equal(hostID2.toB58String());
        })).timeout(5000);
        it('should create a new record', () => __awaiter(void 0, void 0, void 0, function* () {
            const info = yield createThread(client);
            const body = { foo: 'bar', baz: Buffer.from('howdy') };
            const rec = yield client.createRecord(info.id, body);
            chai_1.expect(rec === null || rec === void 0 ? void 0 : rec.threadID.toString()).to.equal(info.id.toString());
            chai_1.expect(rec === null || rec === void 0 ? void 0 : rec.logID).to.not.be.undefined;
            if (rec === null || rec === void 0 ? void 0 : rec.record) {
                const block = rec.record.block;
                chai_1.expect(block).to.have.ownProperty('body');
            }
            else {
                throw new Error('expected record to be defined');
            }
        }));
        it('should be able to add a pre-formed record', () => __awaiter(void 0, void 0, void 0, function* () {
            // Create a thread, keeping read key and log private key on the client
            const id = threads_core_1.ThreadID.fromRandom(threads_core_1.ThreadID.Variant.Raw, 32);
            const threadKey = threads_core_1.ThreadKey.fromRandom(false);
            const logSk = yield ed25519.generateKeyPair();
            const logPk = logSk.public;
            const info = yield client.createThread(id, { threadKey, logKey: logPk });
            const body = { foo: 'bar', baz: Buffer.from('howdy') };
            const readKey = libp2p_crypto_1.randomBytes(32);
            const block = threads_core_1.Block.encoder(body, 'dag-cbor');
            const event = yield threads_encoding_1.createEvent(block, readKey);
            const record = yield threads_encoding_1.createRecord(event, {
                privKey: logSk,
                servKey: threadKey.service,
                pubKey: identity.public,
            });
            const cid1 = yield record.value.cid();
            const logID = yield peer_id_1.default.createFromPubKey(logPk.bytes);
            yield client.addRecord(info.id, logID, record);
            const record2 = yield client.getRecord(info.id, cid1);
            if (!record2) {
                throw new Error('expected record to be defined');
            }
            const cid2 = yield record2.value.cid();
            chai_1.expect(cid1.toString()).to.equal(cid2.toString());
            const b1 = yield record.block.value.cid();
            const b2 = yield record2.block.value.cid();
            chai_1.expect(b1.toString()).to.equal(b2.toString());
        }));
        it('should be able to retrieve a remote record', () => __awaiter(void 0, void 0, void 0, function* () {
            var _a;
            const info = yield createThread(client);
            const body = { foo: 'bar', baz: Buffer.from('howdy') };
            const rec1 = yield client.createRecord(info.id, body);
            const cid1 = yield ((_a = rec1 === null || rec1 === void 0 ? void 0 : rec1.record) === null || _a === void 0 ? void 0 : _a.value.cid());
            if (!cid1)
                throw new Error('expected valid record');
            const rec2 = yield client.getRecord(info.id, cid1);
            const cid2 = yield rec2.value.cid();
            chai_1.expect(cid1.toString()).to.equal(cid2.toString());
        }));
        describe('subscribe', () => {
            let client2;
            let info;
            let token2;
            before(function () {
                return __awaiter(this, void 0, void 0, function* () {
                    this.timeout(5000);
                    client2 = new _1.Client(new context_1.Context(proxyAddr2));
                    const hostID2 = yield client2.getHostID();
                    const hostAddr2 = new threads_core_1.Multiaddr(`/dns4/threads2/tcp/4006`);
                    const peerAddr = hostAddr2.encapsulate(new threads_core_1.Multiaddr(`/p2p/${hostID2}`));
                    info = yield createThread(client);
                    yield client.addReplicator(info.id, peerAddr);
                    // Create temporary identity
                    const identity = yield threads_core_1.Libp2pCryptoIdentity.fromRandom();
                    token2 = yield client2.getToken(identity);
                });
            });
            it('should handle updates and close cleanly', (done) => {
                let count = 0;
                const res = client2.subscribe((rec, err) => {
                    chai_1.expect(rec).to.not.be.undefined;
                    if (rec)
                        count += 1;
                    if (err)
                        throw new Error(`unexpected error: ${err.toString()}`);
                    if (count >= 2) {
                        res.close();
                        done();
                    }
                }, [info.id]);
                client.createRecord(info.id, { foo: 'bar1' }).then(() => {
                    client.createRecord(info.id, { foo: 'bar2' });
                });
            }).timeout(7000);
        });
    });
});
//# sourceMappingURL=index.spec.js.map