Skip to content

Commit

Permalink
feat(test): testing circles
Browse files Browse the repository at this point in the history
  • Loading branch information
serge1peshcoff committed Feb 8, 2020
1 parent f9d7022 commit 421547f
Show file tree
Hide file tree
Showing 9 changed files with 507 additions and 4 deletions.
20 changes: 16 additions & 4 deletions middlewares/circles.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const { Circle } = require('../models');
const errors = require('../lib/errors');
const helpers = require('../lib/helpers');
const { Sequelize, sequelize } = require('../lib/sequelize');

exports.listAllCircles = async (req, res) => {
const circle = await Circle.findAll({});
Expand Down Expand Up @@ -49,15 +51,25 @@ exports.deleteCircle = async (req, res) => {
};

exports.setParentCircle = async (req, res) => {
if (!helpers.isNumber(req.body.parent_circle)) {
return errors.makeBadRequestError(res, 'The circle ID is not valid.');
}

// TODO: check permissions
const parentCircle = await Circle.findByPk(req.body.id);
const parentCircle = await Circle.findByPk(req.body.parent_circle);
if (!parentCircle) {
return errors.makeNotFoundError(res, 'No parent circle found.');
}

await req.currentCircle.update({ parent_circle_id: parentCircle.id });

// TODO: check if there are any loops in the chain.
try {
// need to check after the update if there are any loops
await sequelize.transaction(async (t) => {
await req.currentCircle.update({ parent_circle_id: parentCircle.id }, { transaction: t });
await Circle.throwIfAnyLoops(t);
});
} catch (err) {
return errors.makeValidationError(res, err);
}

return res.json({
success: true,
Expand Down
37 changes: 37 additions & 0 deletions models/Circle.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,41 @@ const Circle = sequelize.define('circle', {
updatedAt: 'updated_at',
});

// Checking if there isn't a loop inside circles (so no circle1 -> circle2 -> circle1).
Circle.throwIfAnyLoops = async function throwIfAnyLoops(transaction) {
// Firstly, loading all bound circles.
const circles = await Circle.findAll({ transaction });
const boundCircles = circles.filter(circle => circle.parent_circle_id !== null);

// Secondly, creating a map of circles visited.
let visibleCircles = {};

// Then, defining a function that we'll call recursively.
const markCircleAsVisible = (circle) => {
// means that we'be been there before
if (visibleCircles[circle.id]) {
throw new Error(`There's a loop in circles, check ID ${circle.id}`);
}

// now we haven't been here before, let's mark this circle as visited
// and move on to its parent circle
visibleCircles[circle.id] = true;
if (!circle.parent_circle_id) {
return;
}

const parentCircle = circles.find(c => c.id === circle.parent_circle_id);
markCircleAsVisible(parentCircle);
}

// Lastly, call this function for all circles.
for (const circle of boundCircles) {
markCircleAsVisible(circle);

// Only counting loops within one chain.
visibleCircles = {};
}
};


module.exports = Circle;
57 changes: 57 additions & 0 deletions test/api/circles-creating.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
const { startServer, stopServer } = require('../../lib/server.js');
const { request } = require('../scripts/helpers');
const generator = require('../scripts/generator');

describe('Circles creating', () => {
beforeAll(async () => {
await startServer();
});

afterAll(async () => {
await stopServer();
});

afterEach(async () => {
await generator.clearAll();
});

test('should fail if there are validation errors', async () => {
const user = await generator.createUser({ username: 'test', mail_confirmed_at: new Date() });
const token = await generator.createAccessToken({}, user);

const circle = generator.generateCircle({ name: '' });

const res = await request({
uri: '/circles/',
method: 'POST',
headers: { 'X-Auth-Token': token.value },
body: circle
});

expect(res.statusCode).toEqual(422);
expect(res.body.success).toEqual(false);
expect(res.body).not.toHaveProperty('data');
expect(res.body).toHaveProperty('errors');
expect(res.body.errors).toHaveProperty('name');
});

test('should succeed if everything is okay', async () => {
const user = await generator.createUser({ username: 'test', mail_confirmed_at: new Date() });
const token = await generator.createAccessToken({}, user);

const circle = generator.generateCircle();

const res = await request({
uri: '/circles/',
method: 'POST',
headers: { 'X-Auth-Token': token.value },
body: circle
});

expect(res.statusCode).toEqual(200);
expect(res.body.success).toEqual(true);
expect(res.body).not.toHaveProperty('errors');
expect(res.body).toHaveProperty('data');
expect(res.body.data.name).toEqual(circle.name);
});
});
55 changes: 55 additions & 0 deletions test/api/circles-deleting.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
const { startServer, stopServer } = require('../../lib/server.js');
const { request } = require('../scripts/helpers');
const generator = require('../scripts/generator');
const { Circle } = require('../../models');

describe('Circles deleting', () => {
beforeAll(async () => {
await startServer();
});

afterAll(async () => {
await stopServer();
});

afterEach(async () => {
await generator.clearAll();
});

test('should return 404 if the circle is not found', async () => {
const user = await generator.createUser();
const token = await generator.createAccessToken({}, user);

const res = await request({
uri: '/circles/1337',
method: 'DELETE',
headers: { 'X-Auth-Token': token.value }
});

expect(res.statusCode).toEqual(404);
expect(res.body.success).toEqual(false);
expect(res.body).not.toHaveProperty('data');
expect(res.body).toHaveProperty('message');
});

test('should succeed if everything is okay', async () => {
const user = await generator.createUser();
const token = await generator.createAccessToken({}, user);

const circle = await generator.createCircle();

const res = await request({
uri: '/circles/' + circle.id,
method: 'DELETE',
headers: { 'X-Auth-Token': token.value }
});

expect(res.statusCode).toEqual(200);
expect(res.body.success).toEqual(true);
expect(res.body).not.toHaveProperty('errors');
expect(res.body).toHaveProperty('message');

const circleFromDb = await Circle.findByPk(circle.id);
expect(circleFromDb).toEqual(null);
});
});
68 changes: 68 additions & 0 deletions test/api/circles-details.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const { startServer, stopServer } = require('../../lib/server.js');
const { request } = require('../scripts/helpers');
const generator = require('../scripts/generator');

describe('Circle details', () => {
beforeAll(async () => {
await startServer();
});

afterAll(async () => {
await stopServer();
});

afterEach(async () => {
await generator.clearAll();
});

test('should return 404 if the circle is not found', async () => {
const user = await generator.createUser();
const token = await generator.createAccessToken({}, user);

const res = await request({
uri: '/circles/1337',
method: 'GET',
headers: { 'X-Auth-Token': token.value }
});

expect(res.statusCode).toEqual(404);
expect(res.body.success).toEqual(false);
expect(res.body).not.toHaveProperty('data');
expect(res.body).toHaveProperty('message');
});

test('should return 400 if id us not a number', async () => {
const user = await generator.createUser();
const token = await generator.createAccessToken({}, user);

const res = await request({
uri: '/circles/xxx',
method: 'GET',
headers: { 'X-Auth-Token': token.value }
});

expect(res.statusCode).toEqual(400);
expect(res.body.success).toEqual(false);
expect(res.body).toHaveProperty('message');
expect(res.body).not.toHaveProperty('data');
});

test('should find the circle by id', async () => {
const user = await generator.createUser();
const token = await generator.createAccessToken({}, user);

const circle = await generator.createCircle();

const res = await request({
uri: '/circles/' + circle.id,
method: 'GET',
headers: { 'X-Auth-Token': token.value }
});

expect(res.statusCode).toEqual(200);
expect(res.body.success).toEqual(true);
expect(res.body).toHaveProperty('data');
expect(res.body).not.toHaveProperty('errors');
expect(res.body.data.id).toEqual(circle.id);
});
});
74 changes: 74 additions & 0 deletions test/api/circles-editing.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
const { startServer, stopServer } = require('../../lib/server.js');
const { request } = require('../scripts/helpers');
const generator = require('../scripts/generator');

describe('Circle details', () => {
beforeAll(async () => {
await startServer();
});

afterAll(async () => {
await stopServer();
});

afterEach(async () => {
await generator.clearAll();
});

test('should return 404 if the circle is not found', async () => {
const user = await generator.createUser();
const token = await generator.createAccessToken({}, user);

const res = await request({
uri: '/circles/1337',
method: 'PUT',
headers: { 'X-Auth-Token': token.value },
body: { name: 'New name' }
});

expect(res.statusCode).toEqual(404);
expect(res.body.success).toEqual(false);
expect(res.body).not.toHaveProperty('data');
expect(res.body).toHaveProperty('message');
});

test('should fail if there are validation errors', async () => {
const user = await generator.createUser();
const token = await generator.createAccessToken({}, user);

const circle = await generator.createCircle();

const res = await request({
uri: '/circles/' + circle.id,
method: 'PUT',
headers: { 'X-Auth-Token': token.value },
body: { name: '' }
});

expect(res.statusCode).toEqual(422);
expect(res.body.success).toEqual(false);
expect(res.body).not.toHaveProperty('data');
expect(res.body).toHaveProperty('errors');
expect(res.body.errors).toHaveProperty('name');
});

test('should succeed if everything is okay', async () => {
const user = await generator.createUser();
const token = await generator.createAccessToken({}, user);

const circle = await generator.createCircle();

const res = await request({
uri: '/circles/' + circle.id,
method: 'PUT',
headers: { 'X-Auth-Token': token.value },
body: { name: 'New name' }
});

expect(res.statusCode).toEqual(200);
expect(res.body.success).toEqual(true);
expect(res.body).not.toHaveProperty('errors');
expect(res.body).toHaveProperty('data');
expect(res.body.data.name).toEqual('New name');
});
});
39 changes: 39 additions & 0 deletions test/api/circles-listing.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const { startServer, stopServer } = require('../../lib/server.js');
const { request } = require('../scripts/helpers');
const generator = require('../scripts/generator');

describe('Circles list', () => {
beforeAll(async () => {
await startServer();
});

afterAll(async () => {
await stopServer();
});

afterEach(async () => {
await generator.clearAll();
});


test('should succeed when everything is okay', async () => {
const user = await generator.createUser({ password: 'test', mail_confirmed_at: new Date() });
const token = await generator.createAccessToken({}, user);

const circle = await generator.createCircle();

const res = await request({
uri: '/circles',
method: 'GET',
headers: { 'X-Auth-Token': token.value }
});

expect(res.statusCode).toEqual(200);
expect(res.body.success).toEqual(true);
expect(res.body).toHaveProperty('data');
expect(res.body).not.toHaveProperty('errors');

expect(res.body.data.length).toEqual(1);
expect(res.body.data[0].id).toEqual(circle.id);
});
});
Loading

0 comments on commit 421547f

Please sign in to comment.