<commit_before>'use strict';

/*
 * Import and Export tests for the /p/whateverPadId/import and /p/whateverPadId/export endpoints.
 */

const assert = require('assert').strict;
const common = require('../../common');
const fs = require('fs');
const settings = require('../../../../node/utils/Settings');
const superagent = require('superagent');
const padManager = require('../../../../node/db/PadManager');
const plugins = require('../../../../static/js/pluginfw/plugin_defs');

const padText = fs.readFileSync(`${__dirname}/test.txt`);
const etherpadDoc = fs.readFileSync(`${__dirname}/test.etherpad`);
const wordDoc = fs.readFileSync(`${__dirname}/test.doc`);
const wordXDoc = fs.readFileSync(`${__dirname}/test.docx`);
const odtDoc = fs.readFileSync(`${__dirname}/test.odt`);
const pdfDoc = fs.readFileSync(`${__dirname}/test.pdf`);

let agent;
const apiKey = common.apiKey;
const apiVersion = 1;
const testPadId = makeid();
const testPadIdEnc = encodeURIComponent(testPadId);

describe(__filename, function () {
  this.timeout(45000);
  before(async function () { agent = await common.init(); });

  describe('Connectivity', function () {
    it('can connect', async function () {
      await agent.get('/api/')
          .expect(200)
          .expect('Content-Type', /json/);
    });
  });

  describe('API Versioning', function () {
    it('finds the version tag', async function () {
      await agent.get('/api/')
          .expect(200)
          .expect((res) => assert(res.body.currentVersion));
    });
  });

  /*
  Tests
  -----

  Test.
    / Create a pad
    / Set pad contents
    / Try export pad in various formats
    / Get pad contents and ensure it matches imported contents

  Test.
    / Try to export a pad that doesn't exist // Expect failure

  Test.
    / Try to import an unsupported file to a pad that exists

  -- TODO: Test.
    Try to import to a file and abort it half way through

  Test.
    Try to import to files of varying size.

  Example Curl command for testing import URI:
    curl -s -v --form file=@/home/jose/test.txt http://127.0.0.1:9001/p/foo/import
  */

  describe('Imports and Exports', function () {
    const backups = {};

    beforeEach(async function () {
      backups.hooks = {};
      for (const hookName of ['preAuthorize', 'authenticate', 'authorize']) {
        backups.hooks[hookName] = plugins.hooks[hookName];
        plugins.hooks[hookName] = [];
      }
      // Note: This is a shallow copy.
      backups.settings = Object.assign({}, settings);
      settings.requireAuthentication = false;
      settings.requireAuthorization = false;
      settings.users = {user: {password: '<PASSWORD>'}};
    });

    afterEach(async function () {
      Object.assign(plugins.hooks, backups.hooks);
      // Note: This does not unset settings that were added.
      Object.assign(settings, backups.settings);
    });

    it('creates a new Pad, imports content to it, checks that content', async function () {
      await agent.get(`${endPoint('createPad')}&padID=${testPadId}`)
          .expect(200)
          .expect('Content-Type', /json/)
          .expect((res) => assert.equal(res.body.code, 0));
      await agent.post(`/p/${testPadId}/import`)
          .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
          .expect(200);
      await agent.get(`${endPoint('getText')}&padID=${testPadId}`)
          .expect(200)
          .expect((res) => assert.equal(res.body.data.text, padText.toString()));
    });

    describe('export from read-only pad ID', function () {
      let readOnlyId;

      // This ought to be before(), but it must run after the top-level beforeEach() above.
      beforeEach(async function () {
        if (readOnlyId != null) return;
        await agent.post(`/p/${testPadId}/import`)
            .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
            .expect(200);
        const res = await agent.get(`${endPoint('getReadOnlyID')}&padID=${testPadId}`)
            .expect(200)
            .expect('Content-Type', /json/)
            .expect((res) => assert.equal(res.body.code, 0));
        readOnlyId = res.body.data.readOnlyID;
      });

      for (const authn of [false, true]) {
        describe(`requireAuthentication = ${authn}`, function () {
          // This ought to be before(), but it must run after the top-level beforeEach() above.
          beforeEach(async function () {
            settings.requireAuthentication = authn;
          });

          for (const exportType of ['html', 'txt', 'etherpad']) {
            describe(`export to ${exportType}`, function () {
              let text;

              // This ought to be before(), but it must run after the top-level beforeEach() above.
              beforeEach(async function () {
                if (text != null) return;
                let req = agent.get(`/p/${readOnlyId}/export/${exportType}`);
                if (authn) req = req.auth('user', 'user-password');
                const res = await req
                    .expect(200)
                    .buffer(true).parse(superagent.parse.text);
                text = res.text;
              });

              it('export OK', async function () {
                assert.match(text, /This is the/);
              });

              it('writable pad ID is not leaked', async function () {
                assert(!text.includes(testPadId));
              });
            });
          }
        });
      }
    });

    describe('Import/Export tests requiring AbiWord/LibreOffice', function () {
      before(async function () {
        if ((!settings.abiword || settings.abiword.indexOf('/') === -1) &&
            (!settings.soffice || settings.soffice.indexOf('/') === -1)) {
          this.skip();
        }
      });

      // For some reason word import does not work in testing..
      // TODO: fix support for .doc files..
      it('Tries to import .doc that uses soffice or abiword', async function () {
        await agent.post(`/p/${testPadId}/import`)
            .attach('file', wordDoc, {filename: '/test.doc', contentType: 'application/msword'})<commit_msg>tests: Check import of export of read-only pad ID<commit_after>'use strict';

/*
 * Import and Export tests for the /p/whateverPadId/import and /p/whateverPadId/export endpoints.
 */

const assert = require('assert').strict;
const common = require('../../common');
const fs = require('fs');
const settings = require('../../../../node/utils/Settings');
const superagent = require('superagent');
const padManager = require('../../../../node/db/PadManager');
const plugins = require('../../../../static/js/pluginfw/plugin_defs');

const padText = fs.readFileSync(`${__dirname}/test.txt`);
const etherpadDoc = fs.readFileSync(`${__dirname}/test.etherpad`);
const wordDoc = fs.readFileSync(`${__dirname}/test.doc`);
const wordXDoc = fs.readFileSync(`${__dirname}/test.docx`);
const odtDoc = fs.readFileSync(`${__dirname}/test.odt`);
const pdfDoc = fs.readFileSync(`${__dirname}/test.pdf`);

let agent;
const apiKey = common.apiKey;
const apiVersion = 1;
const testPadId = makeid();
const testPadIdEnc = encodeURIComponent(testPadId);

describe(__filename, function () {
  this.timeout(45000);
  before(async function () { agent = await common.init(); });

  describe('Connectivity', function () {
    it('can connect', async function () {
      await agent.get('/api/')
          .expect(200)
          .expect('Content-Type', /json/);
    });
  });

  describe('API Versioning', function () {
    it('finds the version tag', async function () {
      await agent.get('/api/')
          .expect(200)
          .expect((res) => assert(res.body.currentVersion));
    });
  });

  /*
  Tests
  -----

  Test.
    / Create a pad
    / Set pad contents
    / Try export pad in various formats
    / Get pad contents and ensure it matches imported contents

  Test.
    / Try to export a pad that doesn't exist // Expect failure

  Test.
    / Try to import an unsupported file to a pad that exists

  -- TODO: Test.
    Try to import to a file and abort it half way through

  Test.
    Try to import to files of varying size.

  Example Curl command for testing import URI:
    curl -s -v --form file=@/home/jose/test.txt http://127.0.0.1:9001/p/foo/import
  */

  describe('Imports and Exports', function () {
    const backups = {};

    beforeEach(async function () {
      backups.hooks = {};
      for (const hookName of ['preAuthorize', 'authenticate', 'authorize']) {
        backups.hooks[hookName] = plugins.hooks[hookName];
        plugins.hooks[hookName] = [];
      }
      // Note: This is a shallow copy.
      backups.settings = Object.assign({}, settings);
      settings.requireAuthentication = false;
      settings.requireAuthorization = false;
      settings.users = {user: {password: '<PASSWORD>'}};
    });

    afterEach(async function () {
      Object.assign(plugins.hooks, backups.hooks);
      // Note: This does not unset settings that were added.
      Object.assign(settings, backups.settings);
    });

    it('creates a new Pad, imports content to it, checks that content', async function () {
      await agent.get(`${endPoint('createPad')}&padID=${testPadId}`)
          .expect(200)
          .expect('Content-Type', /json/)
          .expect((res) => assert.equal(res.body.code, 0));
      await agent.post(`/p/${testPadId}/import`)
          .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
          .expect(200);
      await agent.get(`${endPoint('getText')}&padID=${testPadId}`)
          .expect(200)
          .expect((res) => assert.equal(res.body.data.text, padText.toString()));
    });

    describe('export from read-only pad ID', function () {
      let readOnlyId;

      // This ought to be before(), but it must run after the top-level beforeEach() above.
      beforeEach(async function () {
        if (readOnlyId != null) return;
        await agent.post(`/p/${testPadId}/import`)
            .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
            .expect(200);
        const res = await agent.get(`${endPoint('getReadOnlyID')}&padID=${testPadId}`)
            .expect(200)
            .expect('Content-Type', /json/)
            .expect((res) => assert.equal(res.body.code, 0));
        readOnlyId = res.body.data.readOnlyID;
      });

      for (const authn of [false, true]) {
        describe(`requireAuthentication = ${authn}`, function () {
          // This ought to be before(), but it must run after the top-level beforeEach() above.
          beforeEach(async function () {
            settings.requireAuthentication = authn;
          });

          for (const exportType of ['html', 'txt', 'etherpad']) {
            describe(`export to ${exportType}`, function () {
              let text;

              // This ought to be before(), but it must run after the top-level beforeEach() above.
              beforeEach(async function () {
                if (text != null) return;
                let req = agent.get(`/p/${readOnlyId}/export/${exportType}`);
                if (authn) req = req.auth('user', '<PASSWORD>-password');
                const res = await req
                    .expect(200)
                    .buffer(true).parse(superagent.parse.text);
                text = res.text;
              });

              it('export OK', async function () {
                assert.match(text, /This is the/);
              });

              it('writable pad ID is not leaked', async function () {
                assert(!text.includes(testPadId));
              });

              it('re-import to read-only pad ID gives 403 forbidden', async function () {
                let req = agent.post(`/p/${readOnlyId}/import`)
                    .attach('file', Buffer.from(text), {
                      filename: `/test.${exportType}`,
                      contentType: 'text/plain',
                    });
                if (authn) req = req.auth('user', '<PASSWORD>-password');
                await req.expect(403);
              });

              it('re-import to read-write pad ID gives 200 OK', async function () {
                // The new pad ID must differ from testPadId because Etherpad refuses to import
                // .etherpad files on top of a pad that already has edits.
                let req = agent.post(`/p/${testPadId}_import/import`)
                    .attach('file', Buffer.from(text), {
                      filename: `/test.${exportType}`,
                      contentType: 'text/plain',
                    });
                if (authn) req = req.auth('user', '<PASSWORD>');
                await req.expect(200);
              });
            });
          }
        });
      }
    });

    describe('Import/Export tests requiring AbiWord/LibreOffice', function () {
      before(async function () {
        if ((!settings.abiword || settings.abiword.indexOf('/') === -1) &&
            (!settings.soffice || settings.soffice.indexOf('/') === -1)) {
          this.skip();
        }
      });

      // For some reason word import does not work in testing..
      // TODO: fix support for .doc files..
      it('Tries to import .doc that uses soffice or abiword', async function () {
        await agent.post(`/p/${testPadId}/import`)
            .attach('file', wordDoc, {filename: '/test.doc', contentType: 'application/msword'})
{'fraction_non_alphanumeric': 0.15596671124981423, 'fraction_numerical': 0.00743052459503641, 'mean_word_length': 2.262787878787879, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 16, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, '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': '1455430', 'n_tokens_mistral': 3882, 'n_tokens_neox': 3784, 'n_words': 1001}