"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const chai_1 = require("chai");
const fs = require("fs");
const os_1 = require("os");
const path = require("path");
const sinon = require("sinon");
const errors_1 = require("../src/server/errors");
const profile_1 = require("../src/server/profile");
const util_1 = require("../src/server/util");
const _setup_1 = require("./_setup");
describe('profile', () => {
    let file;
    let profile;
    let requester;
    let mockOAuth;
    const mockPrompter = {
        prompt: (code) => chai_1.expect(code).to.equal('ABC123'),
        isCancelled: () => false,
    };
    beforeEach(() => {
        mockOAuth = { getCode: sinon.stub(), refresh: sinon.stub() };
        requester = new _setup_1.MockRequester();
        mockOAuth.getCode.resolves({
            code: 'ABC123',
            waitForAccept: () => __awaiter(this, void 0, void 0, function* () { return _setup_1.createValidOAuthTokens(); }),
        });
        requester.json.resolves({
            json: () => ({
                id: 1,
                username: 'connor',
                channel: {
                    id: 2,
                },
            }),
        });
        file = path.resolve(os_1.tmpdir(), 'miix-test-profile.yaml');
        profile = new profile_1.Profile(file, mockOAuth, requester);
    });
    afterEach(() => {
        try {
            fs.unlinkSync(file);
        }
        catch (e) {
            /* ignored */
        }
    });
    function expectProfileFile() {
        return chai_1.expect(fs.readFileSync(file, 'utf-8'));
    }
    describe('granting', () => {
        it("creates and grants a file if it doesn't exist", () => __awaiter(this, void 0, void 0, function* () {
            const tokens = yield profile.grant(mockPrompter);
            expectProfileFile().to.contain(`accessToken: ${tokens.data.accessToken}`);
            chai_1.expect(yield profile.user()).to.containSubset({
                id: 1,
                username: 'connor',
            });
        }));
        it('throws when getting tokens if the tokens are not set', () => __awaiter(this, void 0, void 0, function* () {
            yield chai_1.expect(profile.tokens()).to.eventually.rejectedWith(errors_1.NoAuthenticationError);
        }));
        it('loads existing tokens', () => __awaiter(this, void 0, void 0, function* () {
            const originalTokens = yield profile.grant(mockPrompter);
            const savedTokens = yield new profile_1.Profile(file, null).tokens();
            chai_1.expect(savedTokens).to.deep.equal(originalTokens);
        }));
        it('retries asking for expired tokens until notifier cancels', () => __awaiter(this, void 0, void 0, function* () {
            let n = 0;
            let cancelled = false;
            const notifier = {
                isCancelled: () => cancelled,
                prompt: sinon.stub(),
            };
            mockOAuth.getCode.resolves({
                code: 'ABC123',
                waitForAccept: () => __awaiter(this, void 0, void 0, function* () {
                    if (n++ === 2) {
                        cancelled = true;
                    }
                    throw new errors_1.ShortCodeExpireError();
                }),
            });
            yield chai_1.expect(profile.grant(notifier)).to.eventually.be.rejectedWith(profile_1.GrantCancelledError);
            chai_1.expect(notifier.prompt).to.have.been.calledThrice;
        }));
        it('refreshes expired tokens', () => __awaiter(this, void 0, void 0, function* () {
            mockOAuth.getCode.resolves({
                code: 'ABC123',
                waitForAccept: () => __awaiter(this, void 0, void 0, function* () { return _setup_1.createExpiredOAuthTokens(); }),
            });
            yield profile.grant(mockPrompter);
            mockOAuth.refresh.resolves(_setup_1.createValidOAuthTokens());
            const newTokens = yield new profile_1.Profile(file, mockOAuth, requester).tokens();
            chai_1.expect(newTokens.data.accessToken).to.equal('access_token'); // not expired_access_token
            const savedTokens = yield new profile_1.Profile(file, null).tokens();
            chai_1.expect(savedTokens).to.deep.equal(newTokens);
        }));
    });
    describe('logout', () => {
        beforeEach(() => __awaiter(this, void 0, void 0, function* () { return profile.grant(mockPrompter); }));
        it('logs out of the current host', () => __awaiter(this, void 0, void 0, function* () {
            yield profile.logout();
            chai_1.expect(yield profile.hasAuthenticated()).to.be.false;
            expectProfileFile().to.not.contain(util_1.api());
        }));
        it('is a noop if already logged out', () => __awaiter(this, void 0, void 0, function* () {
            yield profile.logout();
            yield profile.logout(); // should not throw or anything
        }));
        it('does not affect other hosts', () => __awaiter(this, void 0, void 0, function* () {
            const exampleHost = 'https://example.com';
            profile.host = exampleHost;
            yield profile.grant(mockPrompter);
            expectProfileFile().to.contain(exampleHost);
            yield profile.logout();
            expectProfileFile().to.not.contain(exampleHost);
            expectProfileFile().to.contain(util_1.api());
        }));
    });
});
