FoseFx/twitch-chatbot-boilerplate-core

View on GitHub
__tests__/core/server/auth.test.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { Request, Response } from 'express';
import * as nock from 'nock';
import {
  _this as auth,
  getBasicProfileInfo,
} from '../../../src/core/server/auth';
import * as setup from '../../../src/core/setup';
import {
  StartServerOptions,
  TokenResponse,
  AuthData,
  BasicProfile,
} from '../../../src/core/server/server.types';

describe('auth', () => {
  afterEach(() => {
    nock.cleanAll();
  });
  it('should generate an OAuthUrl', () => {
    const opts = { clientId: 'someClientIdLol' } as StartServerOptions;
    const scopes = ['scope:one', 'something_else', 'anda:third_one'];
    const callbackURI = 'https://test.com/cb';
    const expectation =
      'https://id.twitch.tv/oauth2/authorize' +
      '?client_id=someClientIdLol' +
      '&redirect_uri=https://test.com/cb' +
      '&response_type=code' +
      '&scope=scope:one something_else anda:third_one' +
      '&claims={"id_token":{"email":null},"userinfo":{}}' +
      '&force_verify=true';
    expect(auth.getOAuthUrl(opts, scopes, callbackURI)).toEqual(expectation);
  });

  describe('obtainAccessToken', () => {
    const opts = {
      clientId: 'someCI',
      clientSecret: 'someSecret',
    } as StartServerOptions;

    const respObj = { test: 'test' };
    const code = 'test';
    const cbURL = 'https://test.com/cb';
    let networkMock;

    beforeEach(() => {
      networkMock = nock('https://id.twitch.tv').post('/oauth2/token').query({
        client_id: opts.clientId,
        client_secret: opts.clientSecret,
        code,
        grant_type: 'authorization_code',
        redirect_uri: cbURL,
      });
    });

    it('should request token', async () => {
      networkMock.reply(200, respObj);
      expect(await auth.obtainAccessToken(opts, code, cbURL)).toEqual(respObj);
    });

    it('should handle errors by loggging the json response and returning an error', () => {
      networkMock.reply(400, respObj);

      return auth.obtainAccessToken(opts, code, cbURL).catch((err) => {
        expect(err).toEqual(
          new Error(
            'An error has occurred while reaching out to the TwitchAPI: {"test":"test"}',
          ),
        );
      });
    });

    it('should handle errors by failing to log non-json response and returning an error', () => {
      const logSpy = jest.spyOn(console, 'log').mockReset();

      networkMock.reply(400, 'lololol');

      return auth.obtainAccessToken(opts, code, cbURL).catch((err) => {
        expect(err).toEqual(
          new Error(
            'An error has occurred while reaching out to the TwitchAPI',
          ),
        );
        expect(logSpy).not.toHaveBeenCalled();
      });
    });
  });

  describe('setupCallback', () => {
    const opts = {} as StartServerOptions;

    it('should return error when obtainAccessCode fails', async () => {
      jest.spyOn(setup, 'getOTP').mockReturnValue('some');
      jest
        .spyOn(auth, 'obtainAccessToken')
        .mockRejectedValue(new Error('Idk, you send bs or sth'));

      const req = ({
        cookies: { token: 'some' },
        query: { code: 'somecode' },
      } as unknown) as Request;

      const res = ({
        status: jest.fn(),
        render: jest.fn(),
      } as unknown) as Response;

      const next = jest.fn();

      await auth.setupCallback(opts)(req, res, next);

      expect(res.render).not.toHaveBeenCalled();
      expect(next).toHaveBeenCalledWith(new Error('Idk, you send bs or sth'));
    });

    it('should finishSetup() and return ok, when ok', async () => {
      jest.spyOn(setup, 'getOTP').mockReturnValue('some');
      jest
        .spyOn(auth, 'obtainAccessToken')
        .mockResolvedValue({} as TokenResponse);

      const fSspy = jest
        .spyOn(setup, 'finishSetup')
        .mockResolvedValue(undefined);

      const req = ({
        cookies: { token: 'some' },
        query: { code: 'somecode' },
      } as unknown) as Request;

      const res = ({
        status: jest.fn(),
        render: jest.fn(),
      } as unknown) as Response;

      await auth.setupCallback(opts)(req, res, null);

      expect(res.render).toHaveBeenCalledWith('ok');
      expect(fSspy).toHaveBeenCalled();
    });
  });

  describe('refreshAccessToken', () => {
    const opts = ({
      clientId: 'clientId',
      clientSecret: 'clientSecret',
    } as unknown) as StartServerOptions;

    const authData = ({
      refresh_token: 'some refreshTöken',
    } as unknown) as AuthData;

    let networkMock;

    beforeEach(() => {
      networkMock = nock('https://id.twitch.tv').post('/oauth2/token').query({
        grant_type: 'refresh_token',
        refresh_token: authData.refresh_token,
        client_id: opts.clientId,
        client_secret: opts.clientSecret,
      });
    });

    it('should return error when request fails', () => {
      const writeSpy = jest
        .spyOn(setup, 'writeToDisk')
        .mockImplementation(() => ({}));

      const respObj = { status: 400, message: 'Some Error idk' };

      networkMock.reply(400, respObj);

      return auth.refreshAccessToken(opts, authData, true).catch((e) => {
        expect(e).toEqual(
          new Error(
            'Refresh request was rejected: 400: ' + JSON.stringify(respObj),
          ),
        );
        expect(writeSpy).not.toHaveBeenCalled();
      });
    });
    it('should return new authData and writeToDisk', () => {
      const writeSpy = jest
        .spyOn(setup, 'writeToDisk')
        .mockImplementation(() => ({}));

      const respObj = {
        access_token: 'someAT',
        refresh_token: 'someRT',
        expires_in: 6969,
      } as TokenResponse;

      networkMock.reply(200, respObj);

      return auth
        .refreshAccessToken(opts, authData, true)
        .then((data: AuthData) => {
          expect(data).toEqual(respObj);
          expect(writeSpy).toHaveBeenCalled();
        });
    });
  });

  describe('getBasicProfileInfo', () => {
    it('should return BasicProfile of user', () => {
      expect.assertions(1);
      const user = { id: 'test', login: 'testtest' } as BasicProfile;
      nock('https://api.twitch.tv')
        .get('/helix/users')
        .reply(200, { data: [user] });

      return getBasicProfileInfo(
        ({} as unknown) as StartServerOptions,
        {} as AuthData,
      ).then((resp) => {
        expect(resp).toEqual(user);
      });
    });
  });
});