<commit_before>require('mocha');
const fs = require('fs');
const path = require('path');
const { Agent } = require('https');
const { assert } = require('chai');
const { WebClient } = require('./WebClient');
const { ErrorCode } = require('./errors');
const { LogLevel } = require('./logger');
const { addAppMetadata } = require('./instrument');
const { rapidRetryPolicy } = require('./retry-policies');
const { CaptureConsole } = require('@aoberoi/capture-console');
const nock = require('nock');
const Busboy = require('busboy');
const sinon = require('sinon');

const token = 'xoxb-faketoken';

describe('WebClient', function () {

  describe('constructor()', function () {
    it('should build a default client given a token', function () {
      const client = new WebClient(token);
      assert.instanceOf(client, WebClient);
      assert.equal(client.token, token);
      assert.equal(client.slackApiUrl, 'https://slack.com/api/')
    });

    it('should build a client without a token', function () {
      const client = new WebClient();
      assert.instanceOf(client, WebClient);
    });
  });

  describe('has an option to change the log output severity', function () {
    beforeEach(function () {
      this.capture = new CaptureConsole();
      this.capture.startCapture();
    });
    it('outputs a debug log on initialization', function () {
      const debuggingClient = new WebClient(token, { logLevel: LogLevel.DEBUG });
      const output = this.capture.getCapturedText();
      assert.isNotEmpty(output); // should have at least 1 log line, but not asserting since that is an implementation detail
    });
    afterEach(function () {
      this.capture.stopCapture();
    });
  });

  describe('has a logger option', function () {
    beforeEach(function () {
      this.capture = new CaptureConsole();
      this.capture.startCapture();
      this.logger = {
        debug: sinon.spy(),
        info: sinon.spy(),
        warn: sinon.spy(),
        error: sinon.spy(),
        setLevel: sinon.spy(),
        setName: sinon.spy(),
      };
    });
    it('sends logs to a logger and not to stdout', function () {
      const debuggingClient = new WebClient(token, { logLevel: LogLevel.DEBUG, logger: this.logger });
      assert.isTrue(this.logger.debug.called);
      const capturedOutput = this.capture.getCapturedText();
      assert.isEmpty(capturedOutput);
    });
    it('never modifies the original logger', function () {
      new WebClient(token, { logger: this.logger });
      // Calling #setName of the given logger is destructive
      assert.isFalse(this.logger.setName.called);
    });
    afterEach(function () {
      this.capture.stopCapture();
    });
  });

  describe('apiCall()', function () {
    beforeEach(function () {
      this.client = new WebClient(token, { retryConfig: rapidRetryPolicy });
    });

    describe('when making a successful call', function () {
      beforeEach(function () {
        this.scope = nock('https://slack.com')
          .post(/api/)
          .reply(200, { ok: true,
            response_metadata: {
              warnings: ['testWarning1', 'testWarning2']
            }
          });
      });

      it('should return results in a Promise', function () {
        const r = this.client.apiCall('method');
        return r.then(result => {
          assert(result.ok);
          this.scope.done();
        });
      });

      it('should send warnings to logs', function() {
        const logger = {
          debug: sinon.spy(),
          info: sinon.spy(),
          warn: sinon.spy(),
          error: sinon.spy(),
          setLevel: sinon.spy(),
          setName: sinon.spy(),
        };
        const warnClient = new WebClient(token, { logLevel: LogLevel.WARN, logger });
        return warnClient.apiCall('method')
          .then(() => {
            assert.isTrue(logger.warn.calledTwice);
          });
      });
    });

    describe('with OAuth scopes in the response headers', function () {
      it('should expose a scopes and acceptedScopes properties on the result', function () {
        const scope = nock('https://slack.com')<commit_msg>added test for issue #945<commit_after>require('mocha');
const fs = require('fs');
const path = require('path');
const { Agent } = require('https');
const { assert } = require('chai');
const { WebClient } = require('./WebClient');
const { ErrorCode } = require('./errors');
const { LogLevel } = require('./logger');
const { addAppMetadata } = require('./instrument');
const { rapidRetryPolicy } = require('./retry-policies');
const { CaptureConsole } = require('@aoberoi/capture-console');
const nock = require('nock');
const Busboy = require('busboy');
const sinon = require('sinon');

const token = 'xoxb-faketoken';

describe('WebClient', function () {

  describe('constructor()', function () {
    it('should build a default client given a token', function () {
      const client = new WebClient(token);
      assert.instanceOf(client, WebClient);
      assert.equal(client.token, token);
      assert.equal(client.slackApiUrl, 'https://slack.com/api/')
    });

    it('should build a client without a token', function () {
      const client = new WebClient();
      assert.instanceOf(client, WebClient);
    });
  });

  describe('has an option to change the log output severity', function () {
    beforeEach(function () {
      this.capture = new CaptureConsole();
      this.capture.startCapture();
    });
    it('outputs a debug log on initialization', function () {
      const debuggingClient = new WebClient(token, { logLevel: LogLevel.DEBUG });
      const output = this.capture.getCapturedText();
      assert.isNotEmpty(output); // should have at least 1 log line, but not asserting since that is an implementation detail
    });
    afterEach(function () {
      this.capture.stopCapture();
    });
  });

  describe('has a logger option', function () {
    beforeEach(function () {
      this.capture = new CaptureConsole();
      this.capture.startCapture();
      this.logger = {
        debug: sinon.spy(),
        info: sinon.spy(),
        warn: sinon.spy(),
        error: sinon.spy(),
        setLevel: sinon.spy(),
        setName: sinon.spy(),
      };
    });
    it('sends logs to a logger and not to stdout', function () {
      const debuggingClient = new WebClient(token, { logLevel: LogLevel.DEBUG, logger: this.logger });
      assert.isTrue(this.logger.debug.called);
      const capturedOutput = this.capture.getCapturedText();
      assert.isEmpty(capturedOutput);
    });
    it('never modifies the original logger', function () {
      new WebClient(token, { logger: this.logger });
      // Calling #setName of the given logger is destructive
      assert.isFalse(this.logger.setName.called);
    });
    afterEach(function () {
      this.capture.stopCapture();
    });
  });

  describe('apiCall()', function () {
    beforeEach(function () {
      this.client = new WebClient(token, { retryConfig: rapidRetryPolicy });
    });

    describe('when making a successful call', function () {
      beforeEach(function () {
        this.scope = nock('https://slack.com')
          .post(/api/)
          .reply(200, { ok: true,
            response_metadata: {
              warnings: ['testWarning1', 'testWarning2'],
              messages: [
                "[ERROR] unsupported type: sections [json-pointer:/blocks/0/type]", 
                "[WARN] A Content-Type HTTP header was presented but did not declare a charset, such as a 'utf-8'"
              ]
            }
          });
      });

      it('should return results in a Promise', function () {
        const r = this.client.apiCall('method');
        return r.then(result => {
          assert(result.ok);
          this.scope.done();
        });
      });

      it('should send warnings to logs from both response_metadata.warnings & response_metadata.messages', function() {
        const logger = {
          debug: sinon.spy(),
          info: sinon.spy(),
          warn: sinon.spy(),
          error: sinon.spy(),
          setLevel: sinon.spy(),
          setName: sinon.spy(),
        };
        const warnClient = new WebClient(token, { logLevel: LogLevel.WARN, logger });
        return warnClient.apiCall('method')
          .then(() => {
            assert.isTrue(logger.warn.calledThrice);
          });
      });

      it('should send response_metadata.messages errors to logs', function() {
        const logger = {
          debug: sinon.spy(),
          info: sinon.spy(),
          warn: sinon.spy(),
          error: sinon.spy(),
          setLevel: sinon.spy(),
          setName: sinon.spy(),
        };
        const errorClient = new WebClient(token, { logLevel: LogLevel.ERROR, logger });
        return errorClient.apiCall('method')
          .then(() => {
            assert.isTrue(logger.error.calledOnce);
          });
      });
    });

    describe('with OAuth scopes in the response headers', function () {
      it('should expose a scopes and acceptedScopes properties on the result', function () {
        const scope = nock('https://slack.com')
{'fraction_non_alphanumeric': 0.1472715318869165, 'fraction_numerical': 0.0018628095551172475, 'mean_word_length': 2.5975561687031927, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 8, 'https://': 6, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6631390', 'n_tokens_mistral': 2567, 'n_tokens_neox': 2528, 'n_words': 640}