huridocs/uwazi

View on GitHub
app/api/files/specs/jsRoutes.spec.js

Summary

Maintainability
A
0 mins
Test Coverage
/*eslint-disable max-lines*/
import db from 'api/utils/testing_db';
import entities from 'api/entities';
import { settingsModel } from 'api/settings/settingsModel';
import { search } from 'api/search';
import request from 'supertest';
import express from 'express';

import mailer from 'api/utils/mailer';
// eslint-disable-next-line node/no-restricted-import
import fs from 'fs/promises';
import { allowedPublicTemplate, fixtures, templateId } from './fixtures';
import instrumentRoutes from '../../utils/instrumentRoutes';
import uploadRoutes from '../jsRoutes.js';
import { legacyLogger } from '../../log';
import { createDirIfNotExists, deleteFiles } from '../filesystem';

const mockExport = jest.fn();
jest.mock('api/csv/csvExporter', () =>
  jest.fn().mockImplementation(() => ({ export: mockExport }))
);

// eslint-disable-next-line max-statements
describe('upload routes', () => {
  let routes;
  let req;
  let file;
  const directory = `${__dirname}/uploads/upload_routes`;

  const deleteAllFiles = async cb => {
    const dontDeleteFiles = [
      'import.zip',
      'eng.pdf',
      'invalid_document.txt',
      'spn.pdf',
      'importcsv.csv',
      'f2082bf51b6ef839690485d7153e847a.pdf',
    ];
    const filesToDelete = (await fs.readdir(directory)).filter(filename =>
      dontDeleteFiles.includes(filename)
    );
    await deleteFiles(filesToDelete);
    cb();
  };

  beforeEach(async () => {
    await createDirIfNotExists(directory);
    await deleteAllFiles(async () => {
      jest.spyOn(search, 'delete').mockImplementation(async () => Promise.resolve());
      jest.spyOn(search, 'indexEntities').mockImplementation(async () => Promise.resolve());
      routes = instrumentRoutes(uploadRoutes);
      file = {
        fieldname: 'file',
        originalname: 'gadgets-01.pdf',
        encoding: '7bit',
        mimetype: 'application/octet-stream',
        destination: `${__dirname}/uploads/`,
        filename: 'f2082bf51b6ef839690485d7153e847a.pdf',
        path: `${__dirname}/uploads/f2082bf51b6ef839690485d7153e847a.pdf`,
        size: 171411271,
      };
      req = {
        language: 'es',
        user: 'admin',
        headers: {},
        body: { document: 'sharedId1' },
        files: [file],
      };
    });
    await db.setupFixturesAndContext(fixtures);
    jest.spyOn(legacyLogger, 'error'); //just to avoid annoying console outpu.mockImplementation(() => {});
  });

  describe('api/public', () => {
    beforeEach(async () => {
      await deleteAllFiles(async () => {
        jest.spyOn(Date, 'now').mockReturnValue(1000);
        jest.spyOn(mailer, 'send').mockImplementation(() => {});
        const buffer = await fs.readFile(`${__dirname}/12345.test.pdf`);
        file = {
          fieldname: 'file',
          originalname: 'gadgets-01.pdf',
          encoding: '7bit',
          mimetype: 'application/octet-stream',
          buffer,
        };

        const attachment = {
          fieldname: 'attachment0',
          originalname: 'attachment-01.pdf',
          encoding: '7bit',
          mimetype: 'application/octet-stream',
          buffer,
        };
        req = {
          language: 'es',
          headers: {},
          body: {
            entity: { title: 'public submit', template: templateId.toString() },
          },
          files: [file, attachment],
          io: {},
        };
      });
    });

    it('should not create entity if settings has no allowedPublicTemplates option', async () => {
      const [settingsObject] = await settingsModel.get();
      delete settingsObject.allowedPublicTemplates;
      await settingsModel.db.replaceOne({}, settingsObject);
      try {
        await routes.post('/api/public', req);
        fail('should return error');
      } catch (e) {
        expect(e.message).toMatch(/unauthorized public template/i);
        expect(e.code).toBe(403);
      }
      const res = await entities.get({ title: 'public submit' });
      expect(res.length).toBe(0);
    });

    it('should not create entity if template is not whitelisted in allowedPublicTemplates setting', async () => {
      req.body.entity = {
        title: 'public submit',
        template: 'unauthorized_template_id',
      };
      try {
        await routes.post('/api/public', req);
        fail('should return error');
      } catch (e) {
        expect(e.message).toMatch(/unauthorized public template/i);
        expect(e.code).toBe(403);
      }
      const res = await entities.get({ title: 'public submit' });
      expect(res.length).toBe(0);
    });

    it('should not allow entity updates (sending entities with _id)', async () => {
      req.body.entity = {
        _id: 'an id',
        title: 'public submit',
        template: allowedPublicTemplate.toString(),
      };
      try {
        await routes.post('/api/public', req);
        fail('should return error');
      } catch (e) {
        expect(e.message).toMatch(/unauthorized _id property/i);
        expect(e.code).toBe(403);
      }
    });
  });

  describe('/remotepublic', () => {
    let app;
    let remoteApp;
    let remoteServer;
    beforeEach(() => {
      app = express();
      uploadRoutes(app);
    });

    afterEach(async () => {
      await remoteServer.close();
    });

    it('should remove the tenant and cookie from headers', done => {
      remoteApp = express();
      remoteApp.post('/api/public', (_req, res) => {
        res.json(_req.headers);
      });
      remoteServer = remoteApp.listen(54321, async () => {
        const response = await request(app)
          .post('/api/remotepublic')
          .send({ title: 'Title' })
          .set(
            'cookie',
            'locale=en; SL_G_WPT_TO=en; connect.sid=s%3AnK04AiZIYyWOjO_p.kFF17AeJhqKr207n95pV8'
          )
          .set('tenant', 'tenant')
          .expect(200);

        const headersOnRemote = JSON.parse(response.text);
        expect(headersOnRemote.tenant).toBeUndefined();
        expect(headersOnRemote.cookie).toBeUndefined();
        done();
      });
    });
  });

  afterAll(async () => {
    await deleteAllFiles(() => {});
    await db.disconnect();
  });
});