Add more integration tests #31
@ -158,6 +158,7 @@ Team.findAll({order: ['name']})
|
||||
```js
|
||||
// returns the teams ordered by the name column, descending
|
||||
// GET /teams?order[0]=name&order[0]=DESC
|
||||
// GET /teams?order=name%20DESC
|
||||
|
||||
// results in a Sequelize query:
|
||||
Team.findAll({order: [['name', 'DESC']]})
|
||||
|
@ -4,6 +4,7 @@ import setup from '../test/integration-setup.js';
|
||||
|
||||
const STATUS_OK = 200;
|
||||
const STATUS_NOT_FOUND = 404;
|
||||
const STATUS_BAD_REQUEST = 400;
|
||||
|
||||
setup(test);
|
||||
|
||||
@ -112,3 +113,65 @@ test('not found /notamodel', async (t) => {
|
||||
const { statusCode } = await server.inject({ url, method });
|
||||
t.is(statusCode, STATUS_NOT_FOUND);
|
||||
});
|
||||
|
||||
test('destroyScope /players/returnsOne', async (t) => {
|
||||
const { server, instances, sequelize: { models: { Player } } } = t.context;
|
||||
const { player1, player2 } = instances;
|
||||
// this doesn't exist in our fixtures
|
||||
const url = '/players/returnsOne';
|
||||
const method = 'DELETE';
|
||||
|
||||
const presentPlayers = await Player.findAll();
|
||||
const playerIds = presentPlayers.map(({ id }) => id);
|
||||
t.truthy(playerIds.includes(player1.id));
|
||||
t.truthy(playerIds.includes(player2.id));
|
||||
|
||||
const { result, statusCode } = await server.inject({ url, method });
|
||||
t.is(statusCode, STATUS_OK);
|
||||
t.is(result.id, player1.id);
|
||||
|
||||
const nonDeletedPlayers = await Player.findAll();
|
||||
t.is(nonDeletedPlayers.length, presentPlayers.length - 1);
|
||||
});
|
||||
|
||||
test('destroyScope /players/returnsNone', async (t) => {
|
||||
const { server, instances, sequelize: { models: { Player } } } = t.context;
|
||||
const { player1, player2 } = instances;
|
||||
// this doesn't exist in our fixtures
|
||||
const url = '/players/returnsNone';
|
||||
const method = 'DELETE';
|
||||
|
||||
const presentPlayers = await Player.findAll();
|
||||
const playerIds = presentPlayers.map(({ id }) => id);
|
||||
t.truthy(playerIds.includes(player1.id));
|
||||
t.truthy(playerIds.includes(player2.id));
|
||||
|
||||
const { statusCode } = await server.inject({ url, method });
|
||||
t.is(statusCode, STATUS_NOT_FOUND);
|
||||
|
||||
const nonDeletedPlayers = await Player.findAll();
|
||||
const nonDeletedPlayerIds = nonDeletedPlayers.map(({ id }) => id);
|
||||
t.truthy(nonDeletedPlayerIds.includes(player1.id));
|
||||
t.truthy(nonDeletedPlayerIds.includes(player2.id));
|
||||
});
|
||||
|
||||
test('destroyScope invalid scope /players/invalid', async (t) => {
|
||||
const { server, instances, sequelize: { models: { Player } } } = t.context;
|
||||
const { player1, player2 } = instances;
|
||||
// this doesn't exist in our fixtures
|
||||
const url = '/players/invalid';
|
||||
const method = 'DELETE';
|
||||
|
||||
const presentPlayers = await Player.findAll();
|
||||
const playerIds = presentPlayers.map(({ id }) => id);
|
||||
t.truthy(playerIds.includes(player1.id));
|
||||
t.truthy(playerIds.includes(player2.id));
|
||||
|
||||
const { statusCode } = await server.inject({ url, method });
|
||||
t.is(statusCode, STATUS_BAD_REQUEST);
|
||||
|
||||
const nonDeletedPlayers = await Player.findAll();
|
||||
const nonDeletedPlayerIds = nonDeletedPlayers.map(({ id }) => id);
|
||||
t.truthy(nonDeletedPlayerIds.includes(player1.id));
|
||||
t.truthy(nonDeletedPlayerIds.includes(player2.id));
|
||||
});
|
||||
|
83
src/crud-list-order.integration.test.js
Normal file
83
src/crud-list-order.integration.test.js
Normal file
@ -0,0 +1,83 @@
|
||||
import test from 'ava';
|
||||
import 'sinon-bluebird';
|
||||
import setup from '../test/integration-setup.js';
|
||||
|
||||
const STATUS_OK = 200;
|
||||
const STATUS_BAD_QUERY = 502;
|
||||
|
||||
setup(test);
|
||||
|
||||
test('/players?order=name', async (t) => {
|
||||
const { server, instances } = t.context;
|
||||
const { player1, player2 } = instances;
|
||||
const url = '/players?order=name';
|
||||
const method = 'GET';
|
||||
|
||||
const { result, statusCode } = await server.inject({ url, method });
|
||||
t.is(statusCode, STATUS_OK);
|
||||
// this is the order we'd expect the names to be in
|
||||
t.is(result[0].name, player1.name);
|
||||
t.is(result[1].name, player2.name);
|
||||
});
|
||||
|
||||
test('/players?order=name%20ASC', async (t) => {
|
||||
const { server, instances } = t.context;
|
||||
const { player1, player2 } = instances;
|
||||
const url = '/players?order=name%20ASC';
|
||||
const method = 'GET';
|
||||
|
||||
const { result, statusCode } = await server.inject({ url, method });
|
||||
t.is(statusCode, STATUS_OK);
|
||||
// this is the order we'd expect the names to be in
|
||||
t.is(result[0].name, player1.name);
|
||||
t.is(result[1].name, player2.name);
|
||||
});
|
||||
|
||||
test('/players?order=name%20DESC', async (t) => {
|
||||
const { server, instances } = t.context;
|
||||
const { player1, player2 } = instances;
|
||||
const url = '/players?order=name%20DESC';
|
||||
const method = 'GET';
|
||||
|
||||
const { result, statusCode } = await server.inject({ url, method });
|
||||
t.is(statusCode, STATUS_OK);
|
||||
// this is the order we'd expect the names to be in
|
||||
t.is(result[0].name, player2.name);
|
||||
t.is(result[1].name, player1.name);
|
||||
});
|
||||
|
||||
test('/players?order[]=name', async (t) => {
|
||||
const { server, instances } = t.context;
|
||||
const { player1, player2 } = instances;
|
||||
const url = '/players?order[]=name';
|
||||
const method = 'GET';
|
||||
|
||||
const { result, statusCode } = await server.inject({ url, method });
|
||||
t.is(statusCode, STATUS_OK);
|
||||
// this is the order we'd expect the names to be in
|
||||
t.is(result[0].name, player1.name);
|
||||
t.is(result[1].name, player2.name);
|
||||
});
|
||||
|
||||
test('/players?order[0]=name&order[0]=DESC', async (t) => {
|
||||
const { server, instances } = t.context;
|
||||
const { player1, player2 } = instances;
|
||||
const url = '/players?order[0]=name&order[0]=DESC';
|
||||
const method = 'GET';
|
||||
|
||||
const { result, statusCode } = await server.inject({ url, method });
|
||||
t.is(statusCode, STATUS_OK);
|
||||
// this is the order we'd expect the names to be in
|
||||
t.is(result[0].name, player2.name);
|
||||
t.is(result[1].name, player1.name);
|
||||
});
|
||||
|
||||
test('invalid column /players?order[0]=invalid', async (t) => {
|
||||
const { server } = t.context;
|
||||
const url = '/players?order[]=invalid';
|
||||
const method = 'GET';
|
||||
|
||||
const { statusCode, result } = await server.inject({ url, method });
|
||||
t.is(statusCode, STATUS_BAD_QUERY);
|
||||
t.truthy(result.message.includes('invalid'));
|
||||
});
|
40
src/crud-scope.integration.test.js
Normal file
40
src/crud-scope.integration.test.js
Normal file
@ -0,0 +1,40 @@
|
||||
import test from 'ava';
|
||||
import 'sinon-bluebird';
|
||||
import setup from '../test/integration-setup.js';
|
||||
|
||||
const STATUS_OK = 200;
|
||||
const STATUS_NOT_FOUND = 404;
|
||||
const STATUS_BAD_REQUEST = 400;
|
||||
|
||||
setup(test);
|
||||
|
||||
test('/players/returnsOne', async (t) => {
|
||||
const { server, instances } = t.context;
|
||||
const { player1 } = instances;
|
||||
const url = '/players/returnsOne';
|
||||
const method = 'GET';
|
||||
|
||||
const { result, statusCode } = await server.inject({ url, method });
|
||||
t.is(statusCode, STATUS_OK);
|
||||
t.is(result.length, 1);
|
||||
t.truthy(result[0].id, player1.id);
|
||||
});
|
||||
|
||||
test('/players/returnsNone', async (t) => {
|
||||
const { server } = t.context;
|
||||
const url = '/players/returnsNone';
|
||||
const method = 'GET';
|
||||
|
||||
const { statusCode } = await server.inject({ url, method });
|
||||
t.is(statusCode, STATUS_NOT_FOUND);
|
||||
});
|
||||
|
||||
test('invalid scope /players/invalid', async (t) => {
|
||||
const { server } = t.context;
|
||||
// this doesn't exist in our fixtures
|
||||
const url = '/players/invalid';
|
||||
const method = 'GET';
|
||||
|
||||
const { statusCode } = await server.inject({ url, method });
|
||||
t.is(statusCode, STATUS_BAD_REQUEST);
|
||||
});
|
@ -214,6 +214,8 @@ export const scope = ({ server, model, prefix = '/', config }) => {
|
||||
include, where, limit, offset, order,
|
||||
});
|
||||
|
||||
if (!list.length) return void reply(notFound('Nothing found.'));
|
||||
|
||||
reply(list.map((item) => item.toJSON()));
|
||||
},
|
||||
config,
|
||||
@ -309,6 +311,8 @@ export const destroyScope = ({ server, model, prefix = '/', config }) => {
|
||||
|
||||
const list = await model.scope(request.params.scope).findAll({ include, where });
|
||||
|
||||
if (!list.length) return void reply(notFound('Nothing found.'));
|
||||
|
||||
await Promise.all(list.map(instance => instance.destroy()));
|
||||
|
||||
const listAsJSON = list.map((item) => item.toJSON());
|
||||
|
@ -225,7 +225,7 @@ test('crud#list handler with order', async (t) => {
|
||||
|
||||
t.deepEqual(
|
||||
findAllArgs.order,
|
||||
[request.query.order],
|
||||
[[request.query.order]],
|
||||
'queries with the order as an array b/c that\'s what sequelize wants'
|
||||
);
|
||||
});
|
||||
|
@ -144,7 +144,7 @@ export default ({
|
||||
.keys({
|
||||
limit: joi.number().min(0).integer(),
|
||||
offset: joi.number().min(0).integer(),
|
||||
order: joi.array(),
|
||||
order: [joi.array(), joi.string()],
|
||||
}),
|
||||
get(methodConfig, 'validate.query')
|
||||
);
|
||||
|
@ -70,7 +70,7 @@ export const parseOrder = (request) => {
|
||||
|
||||
// transform to an array so sequelize will escape the input for us and
|
||||
// maintain security. See http://docs.sequelizejs.com/en/latest/docs/querying/#ordering
|
||||
if (isString(order)) return order.split(' ');
|
||||
if (isString(order)) return [order.split(' ')];
|
||||
|
||||
for (const key of Object.keys(order)) {
|
||||
try {
|
||||
@ -80,7 +80,7 @@ export const parseOrder = (request) => {
|
||||
}
|
||||
}
|
||||
|
||||
return order;
|
||||
return [order];
|
||||
};
|
||||
|
||||
export const getMethod = (model, association, plural = true, method = 'get') => {
|
||||
|
@ -57,7 +57,7 @@ test('parseOrder returns order when a string', (t) => {
|
||||
|
||||
t.deepEqual(
|
||||
parseOrder(request)
|
||||
, [order]
|
||||
, [[order]]
|
||||
);
|
||||
});
|
||||
|
||||
@ -69,7 +69,7 @@ test('parseOrder returns order when json', (t) => {
|
||||
|
||||
t.deepEqual(
|
||||
parseOrder(request)
|
||||
, order
|
||||
, [order]
|
||||
);
|
||||
});
|
||||
|
||||
|
13
test/fixtures/models/player.js
vendored
13
test/fixtures/models/player.js
vendored
@ -7,6 +7,7 @@ export default (sequelize, DataTypes) => {
|
||||
},
|
||||
name: DataTypes.STRING,
|
||||
teamId: DataTypes.INTEGER,
|
||||
active: DataTypes.BOOLEAN,
|
||||
}, {
|
||||
classMethods: {
|
||||
associate: (models) => {
|
||||
@ -15,5 +16,17 @@ export default (sequelize, DataTypes) => {
|
||||
});
|
||||
},
|
||||
},
|
||||
scopes: {
|
||||
returnsOne: {
|
||||
where: {
|
||||
active: true,
|
||||
},
|
||||
},
|
||||
returnsNone: {
|
||||
where: {
|
||||
name: 'notaname',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
@ -58,7 +58,9 @@ export default (test) => {
|
||||
const { Player, Team, City } = t.context.sequelize.models;
|
||||
const city1 = await City.create({ name: 'Healdsburg' });
|
||||
const team1 = await Team.create({ name: 'Baseballs', cityId: city1.id });
|
||||
const player1 = await Player.create({ name: 'Pinot', teamId: team1.id });
|
||||
const player1 = await Player.create({
|
||||
name: 'Pinot', teamId: team1.id, active: true,
|
||||
});
|
||||
const player2 = await Player.create({ name: 'Syrah', teamId: team1.id });
|
||||
t.context.instances = { city1, team1, player1, player2 };
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user