1 |
|
2 | const assert = require('assert');
|
3 | const fs = require('fs');
|
4 | const rimraf = require('rimraf');
|
5 |
|
6 | const AUTHENTICATION_TOKEN = 'ec25fc7b-6ee2-4bda-b57c-6c9867b30ff4';
|
7 | const AJAX_AUTHORIZATION_HEADERS = {
|
8 | 'Content-Type': 'application/json', 'Authorization': `Token ${AUTHENTICATION_TOKEN}`
|
9 | };
|
10 |
|
11 | process.setMaxListeners(0);
|
12 |
|
13 | describe('MemServer.Server Parameters and Query Parameters', function() {
|
14 | before(function() {
|
15 | fs.mkdirSync(`./memserver`);
|
16 | fs.mkdirSync(`./memserver/models`);
|
17 | fs.writeFileSync(`${process.cwd()}/memserver/models/user.js`, `
|
18 | import Model from '${process.cwd()}/lib/model';
|
19 |
|
20 | export default Model({
|
21 | findFromHeaderToken(headers) {
|
22 | const authorizationHeader = headers.Authorization;
|
23 | const token = authorizationHeader ? authorizationHeader.slice(6) : false;
|
24 |
|
25 | return this.findBy({ authentication_token: token }) || false;
|
26 | }
|
27 | });
|
28 | `);
|
29 | fs.writeFileSync(`${process.cwd()}/memserver/models/photo.js`, `
|
30 | import Model from '${process.cwd()}/lib/model';
|
31 |
|
32 | export default Model({
|
33 | defaultAttributes: {
|
34 | is_public: true,
|
35 | name() {
|
36 | return 'Some default name';
|
37 | }
|
38 | }
|
39 | });
|
40 | `);
|
41 | fs.writeFileSync(`${process.cwd()}/memserver/models/photo-comment.js`, `
|
42 | import Model from '${process.cwd()}/lib/model';
|
43 |
|
44 | export default Model({
|
45 | defaultAttributes: {
|
46 | inserted_at() {
|
47 | return '2017-10-25T20:54:04.447Z';
|
48 | },
|
49 | is_important: true
|
50 | }
|
51 | });
|
52 | `);
|
53 | fs.mkdirSync(`./memserver/fixtures`);
|
54 | fs.writeFileSync(`${process.cwd()}/memserver/fixtures/users.js`, `export default [
|
55 | {
|
56 | id: 1,
|
57 | email: 'contact@izelnakri.com',
|
58 | username: 'izelnakri',
|
59 | authentication_token: '${AUTHENTICATION_TOKEN}'
|
60 | }
|
61 | ];`);
|
62 | fs.writeFileSync(`${process.cwd()}/memserver/fixtures/photos.js`, `export default [
|
63 | {
|
64 | id: 1,
|
65 | name: 'Ski trip',
|
66 | href: 'ski-trip.jpeg',
|
67 | is_public: false,
|
68 | user_id: 1
|
69 | },
|
70 | {
|
71 | id: 2,
|
72 | name: 'Family photo',
|
73 | href: 'family-photo.jpeg',
|
74 | is_public: true,
|
75 | user_id: 1
|
76 | },
|
77 | {
|
78 | id: 3,
|
79 | name: 'Selfie',
|
80 | href: 'selfie.jpeg',
|
81 | is_public: false,
|
82 | user_id: 1
|
83 | }
|
84 | ];`);
|
85 | fs.writeFileSync(`${process.cwd()}/memserver/fixtures/photo-comments.js`, `export default [
|
86 | {
|
87 | uuid: '499ec646-493f-4eea-b92e-e383d94182f4',
|
88 | content: 'What a nice photo!',
|
89 | photo_id: 1,
|
90 | user_id: 1
|
91 | },
|
92 | {
|
93 | uuid: '77653ad3-47e4-4ec2-b49f-57ea36a627e7',
|
94 | content: 'I agree',
|
95 | photo_id: 1,
|
96 | user_id: 2
|
97 | },
|
98 | {
|
99 | uuid: 'd351963d-e725-4092-a37c-1ca1823b57d3',
|
100 | content: 'I was kidding',
|
101 | photo_id: 1,
|
102 | user_id: 1
|
103 | },
|
104 | {
|
105 | uuid: '374c7f4a-85d6-429a-bf2a-0719525f5f29',
|
106 | content: 'Interesting indeed',
|
107 | photo_id: 2,
|
108 | user_id: 1
|
109 | }
|
110 | ];`);
|
111 | });
|
112 |
|
113 | beforeEach(function() {
|
114 | Object.keys(require.cache).forEach((key) => delete require.cache[key]);
|
115 | });
|
116 |
|
117 | after(function(done) {
|
118 | if (fs.existsSync(`${process.cwd()}/memserver`)) {
|
119 | rimraf.sync(`${process.cwd()}/memserver`);
|
120 | }
|
121 |
|
122 | done();
|
123 | });
|
124 |
|
125 | describe('server can process custom queryParams and responses', function() {
|
126 | before(function(){
|
127 | fs.writeFileSync(`${process.cwd()}/memserver/server.js`, `
|
128 | import Response from '../lib/response';
|
129 |
|
130 | export default function({ User, Photo }) {
|
131 | this.post('/photos', ({ headers, params, queryParams }) => {
|
132 | const user = User.findFromHeaderToken(headers);
|
133 |
|
134 | if (!user || !queryParams.is_admin) {
|
135 | return Response(401, { error: 'Unauthorized' });
|
136 | }
|
137 |
|
138 | const photo = Photo.insert(Object.assign({}, params.photo, { user_id: user.id }));
|
139 |
|
140 | return { photo: Photo.serializer(photo) };
|
141 | });
|
142 |
|
143 | this.get('/photos', ({ headers, queryParams }) => {
|
144 | const user = User.findFromHeaderToken(headers);
|
145 |
|
146 | if (!user) {
|
147 | return Response(404, { error: 'Not found' });
|
148 | }
|
149 |
|
150 | const photos = Photo.findAll(Object.assign({}, { user_id: user.id }, queryParams));
|
151 |
|
152 | if (!photos || photos.length === 0) {
|
153 | return Response(404, { error: 'Not found' });
|
154 | }
|
155 |
|
156 | return { photos: Photo.serializer(photos) };
|
157 | });
|
158 |
|
159 | this.get('/photos/:id', ({ headers, params, queryParams }) => {
|
160 | const user = User.findFromHeaderToken(headers);
|
161 |
|
162 | if (!user) {
|
163 | return Response(401, { error: 'Unauthorized' });
|
164 | } else if (queryParams.nonce === 123123123) {
|
165 | const photo = Photo.findBy({ id: params.id, user_id: user.id });
|
166 |
|
167 | return photo ? { photo: Photo.serializer(photo) } : Response(404, { error: 'Not found' });
|
168 | }
|
169 |
|
170 | return Response(404, { error: 'Not found' });
|
171 | });
|
172 |
|
173 | this.put('/photos/:id', ({ headers, params, queryParams }) => {
|
174 | const user = User.findFromHeaderToken(headers);
|
175 | const validRequest = user && queryParams.nonce === 123123123 &&
|
176 | Photo.findBy({ id: params.id, user_id: user.id });
|
177 |
|
178 | if (validRequest) {
|
179 | return { photo: Photo.serializer(Photo.update(params.photo)) };
|
180 | }
|
181 |
|
182 | return Response(500, { error: 'Unexpected error occured' });
|
183 | });
|
184 |
|
185 | this.delete('/photos/:id', ({ headers, params, queryParams }) => {
|
186 | const user = User.findFromHeaderToken(headers);
|
187 |
|
188 | if (!(queryParams.nonce === 123123123)) {
|
189 | return Response(500, { error: 'Invalid nonce to delete a photo' });
|
190 | } else if (!user && !Photo.findBy({ id: params.id, user_id: user.id })) {
|
191 | return Response(404, { error: 'Not found' });
|
192 | }
|
193 |
|
194 | Photo.delete({ id: params.id }); // NOTE: what to do with this response
|
195 | });
|
196 | }
|
197 | `);
|
198 | });
|
199 |
|
200 | it('POST /resources work with custom headers, queryParams and responses', async function() {
|
201 | this.timeout(10000);
|
202 |
|
203 | const MemServer = require('../../lib/index.js');
|
204 | const { Photo } = MemServer.Models;
|
205 |
|
206 | MemServer.start();
|
207 | window.$ = require('jquery');
|
208 |
|
209 | assert.equal(Photo.count(), 3);
|
210 |
|
211 | await window.$.ajax({
|
212 | type: 'POST', url: '/photos', headers: { 'Content-Type': 'application/json' }
|
213 | }).catch((jqXHR) => {
|
214 | assert.equal(jqXHR.status, 401);
|
215 | assert.deepEqual(jqXHR.responseJSON, { error: 'Unauthorized' });
|
216 | });
|
217 |
|
218 | await window.$.ajax({
|
219 | type: 'POST', url: '/photos', headers: AJAX_AUTHORIZATION_HEADERS
|
220 | }).catch((jqXHR) => {
|
221 | assert.equal(jqXHR.status, 401);
|
222 | assert.deepEqual(jqXHR.responseJSON, { error: 'Unauthorized' });
|
223 | });
|
224 |
|
225 | await window.$.ajax({
|
226 | type: 'POST', url: '/photos?is_admin=true', headers: AJAX_AUTHORIZATION_HEADERS
|
227 | }).then((data, textStatus, jqXHR) => {
|
228 | assert.equal(jqXHR.status, 201);
|
229 | assert.deepEqual(data, { photo: Photo.serializer(Photo.find(4)) });
|
230 | assert.equal(Photo.count(), 4);
|
231 | });
|
232 | });
|
233 |
|
234 | it('GET /resources works with custom headers, queryParams and responses', async function() {
|
235 | const MemServer = require('../../lib/index.js');
|
236 | const { Photo } = MemServer.Models;
|
237 |
|
238 | MemServer.start();
|
239 | window.$ = require('jquery');
|
240 |
|
241 | await window.$.ajax({
|
242 | type: 'GET', url: '/photos', headers: { 'Content-Type': 'application/json' }
|
243 | }).catch((jqXHR) => {
|
244 | assert.equal(jqXHR.status, 404);
|
245 | assert.deepEqual(jqXHR.responseJSON, { error: 'Not found' });
|
246 | });
|
247 |
|
248 | await window.$.ajax({
|
249 | type: 'GET', url: '/photos?is_public=false', headers: AJAX_AUTHORIZATION_HEADERS
|
250 | }).then((data, textStatus, jqXHR) => {
|
251 | assert.equal(jqXHR.status, 200);
|
252 | assert.deepEqual(data, { photos: Photo.serializer(Photo.findAll({ is_public: false })) });
|
253 | });
|
254 |
|
255 | await window.$.ajax({
|
256 | type: 'GET', url: '/photos?href=family-photo.jpeg', headers: AJAX_AUTHORIZATION_HEADERS
|
257 | }).then((data, textStatus, jqXHR) => {
|
258 | assert.equal(jqXHR.status, 200);
|
259 | assert.deepEqual(data, { photos: Photo.serializer(Photo.findAll({ href: 'family-photo.jpeg' })) });
|
260 | });
|
261 | });
|
262 |
|
263 | it('GET /resources/:id works with custom headers, queryParams and responses', async function() {
|
264 | const MemServer = require('../../lib/index.js');
|
265 | const { Photo } = MemServer.Models;
|
266 |
|
267 | MemServer.start();
|
268 | window.$ = require('jquery');
|
269 |
|
270 | await window.$.ajax({
|
271 | type: 'GET', url: '/photos/1', headers: AJAX_AUTHORIZATION_HEADERS
|
272 | }).catch((jqXHR) => {
|
273 | assert.equal(jqXHR.status, 404);
|
274 | assert.deepEqual(jqXHR.responseJSON, { error: 'Not found' });
|
275 | });
|
276 |
|
277 | await window.$.ajax({
|
278 | type: 'GET', url: '/photos/1?nonce=123123123', headers: AJAX_AUTHORIZATION_HEADERS
|
279 | }).then((data, textStatus, jqXHR) => {
|
280 | assert.equal(jqXHR.status, 200);
|
281 | assert.deepEqual(data, { photo: Photo.serializer(Photo.find(1)) });
|
282 | });
|
283 | });
|
284 |
|
285 | it('PUT /resources/:id works with custom headers, queryParams and responses', async function() {
|
286 | const MemServer = require('../../lib/index.js');
|
287 | const { Photo } = MemServer.Models;
|
288 |
|
289 | MemServer.start();
|
290 | window.$ = require('jquery');
|
291 |
|
292 | await window.$.ajax({
|
293 | type: 'PUT', url: '/photos/1', headers: AJAX_AUTHORIZATION_HEADERS,
|
294 | data: JSON.stringify({ photo: { id: 1, name: 'Life' } })
|
295 | }).catch((jqXHR) => {
|
296 | assert.equal(jqXHR.status, 500);
|
297 | assert.deepEqual(jqXHR.responseJSON, { error: 'Unexpected error occured' });
|
298 | });
|
299 |
|
300 | await window.$.ajax({
|
301 | type: 'PUT', url: '/photos/1?nonce=123123123', headers: AJAX_AUTHORIZATION_HEADERS,
|
302 | data: JSON.stringify({ photo: { id: 1, name: 'Life' } })
|
303 | }).then((data, textStatus, jqXHR) => {
|
304 | assert.equal(jqXHR.status, 200);
|
305 | assert.deepEqual(data, { photo: Photo.serializer(Photo.find(1)) });
|
306 | });
|
307 | });
|
308 |
|
309 | it('DELETE /resources/:id works with custom headers, queryParams and responses', async function() {
|
310 | const MemServer = require('../../lib/index.js');
|
311 | const { Photo } = MemServer.Models;
|
312 |
|
313 | MemServer.start();
|
314 | window.$ = require('jquery');
|
315 |
|
316 | await window.$.ajax({
|
317 | type: 'DELETE', url: '/photos/1', headers: AJAX_AUTHORIZATION_HEADERS
|
318 | }).catch((jqXHR) => {
|
319 | assert.equal(jqXHR.status, 500);
|
320 | assert.deepEqual(jqXHR.responseJSON, { error: 'Invalid nonce to delete a photo' });
|
321 | });
|
322 |
|
323 | await window.$.ajax({
|
324 | type: 'DELETE', url: '/photos/1?nonce=123123123', headers: AJAX_AUTHORIZATION_HEADERS
|
325 | }).then((data, textStatus, jqXHR) => {
|
326 | assert.equal(jqXHR.status, 204);
|
327 | });
|
328 | });
|
329 | });
|
330 |
|
331 | describe('some edge cases', function() {
|
332 | before(function() {
|
333 | fs.writeFileSync(`${process.cwd()}/memserver/models/ethereum-account.js`, `
|
334 | import Model from '${process.cwd()}/lib/model';
|
335 |
|
336 | export default Model({
|
337 | });
|
338 | `);
|
339 | fs.writeFileSync(`${process.cwd()}/memserver/fixtures/ethereum-accounts.js`, `export default [
|
340 | {
|
341 | id: 1,
|
342 | address: '0x7be8315acfef37816c9ad4dc5e82195f2a52934c5d0c74883f9978675e26d600'
|
343 | }
|
344 | ];`);
|
345 |
|
346 | fs.writeFileSync(`${process.cwd()}/memserver/server.js`, `
|
347 | import Response from '../lib/response';
|
348 |
|
349 | export default function({ EthereumAccount, Photo, PhotoComment }) {
|
350 | this.get('/ethereum-accounts', ({ queryParams }) => {
|
351 | const ethereumAccounts = EthereumAccount.findAll({ address: queryParams.address });
|
352 |
|
353 | return { ethereum_accounts: EthereumAccount.serializer(ethereumAccounts) };
|
354 | });
|
355 |
|
356 | this.get('/ethereum-accounts/:address', ({ params }) => {
|
357 | const ethereumAccount = EthereumAccount.findBy({ address: params.address });
|
358 |
|
359 | return { ethereum_account: EthereumAccount.serializer(ethereumAccount) };
|
360 | });
|
361 |
|
362 | this.get('/photos', ({ queryParams }) => {
|
363 | const photos = Photo.find(queryParams.ids || []);
|
364 |
|
365 | if (!photos || photos.length === 0) {
|
366 | return Response(404, { error: 'Not found' });
|
367 | }
|
368 |
|
369 | return { photos: Photo.serializer(photos) };
|
370 | });
|
371 |
|
372 | this.get('/photo-comments/:uuid', ({ params }) => {
|
373 | const photoComment = PhotoComment.findBy({ uuid: params.uuid });
|
374 |
|
375 | return { photo_comment: PhotoComment.serializer(photoComment) };
|
376 | });
|
377 |
|
378 | this.get('/photo-comments', ({ queryParams }) => {
|
379 | const photoComments = PhotoComment.findAll({ uuid: queryParams.uuid });
|
380 |
|
381 | return { photo_comments: PhotoComment.serializer(photoComments) };
|
382 | });
|
383 | }
|
384 | `);
|
385 | });
|
386 |
|
387 | it('works for coalasceFindRequests routes', async function() {
|
388 | const MemServer = require('../../lib/index.js');
|
389 | const { Photo } = MemServer.Models;
|
390 |
|
391 | MemServer.start();
|
392 | window.$ = require('jquery');
|
393 |
|
394 | await window.$.ajax({
|
395 | type: 'GET', url: '/photos', headers: { 'Content-Type': 'application/json' }
|
396 | }).catch((jqXHR) => {
|
397 | assert.equal(jqXHR.status, 404);
|
398 | assert.deepEqual(jqXHR.responseJSON, { error: 'Not found' });
|
399 | });
|
400 |
|
401 | await window.$.ajax({
|
402 | type: 'GET', url: '/photos?ids[]=1&ids[]=2', headers: { 'Content-Type': 'application/json' }
|
403 | }).then((data, textStatus, jqXHR) => {
|
404 | assert.equal(jqXHR.status, 200);
|
405 | assert.deepEqual(jqXHR.responseJSON, { photos: Photo.serializer(Photo.find([1, 2])) });
|
406 | });
|
407 |
|
408 | await window.$.ajax({
|
409 | type: 'GET', url: '/photos?ids[]=2&ids[]=3', headers: { 'Content-Type': 'application/json' }
|
410 | }).then((data, textStatus, jqXHR) => {
|
411 | assert.equal(jqXHR.status, 200);
|
412 | assert.deepEqual(jqXHR.responseJSON, { photos: Photo.serializer(Photo.find([2, 3])) });
|
413 | });
|
414 | });
|
415 |
|
416 | it('converts empty strings to null in request and formats query params', async function() {
|
417 | const MemServer = require('../../lib/index.js');
|
418 | const { Photo } = MemServer.Models;
|
419 |
|
420 | MemServer.start();
|
421 | window.$ = require('jquery');
|
422 |
|
423 | MemServer.Server.post('/photos', ({ params, queryParams }) => {
|
424 | assert.deepEqual(params, { name: null, title: 'Cool' });
|
425 | assert.deepEqual(queryParams, { is_important: true, filter: 32 });
|
426 |
|
427 | return { photo: Photo.serializer(Photo.insert(params)) };
|
428 | });
|
429 |
|
430 | await window.$.ajax({
|
431 | type: 'POST', url: '/photos?is_important=true&filter=32', data: { name: '', title: 'Cool' }
|
432 | }).then((data, textStatus, jqXHR) => {
|
433 | assert.equal(jqXHR.status, 201);
|
434 | assert.equal(Photo.count(), 4);
|
435 | });
|
436 | });
|
437 |
|
438 | it('casts uuids correctly as params', async function() {
|
439 | const MemServer = require('../../lib/index.js');
|
440 | const { PhotoComment } = MemServer.Models;
|
441 |
|
442 | MemServer.start();
|
443 | window.$ = require('jquery');
|
444 |
|
445 | const targetComment = PhotoComment.findBy({ uuid: '499ec646-493f-4eea-b92e-e383d94182f4' });
|
446 |
|
447 | await window.$.ajax({
|
448 | type: 'GET', url: '/photo-comments/499ec646-493f-4eea-b92e-e383d94182f4'
|
449 | }).then((data, textStatus, jqXHR) => {
|
450 | assert.equal(jqXHR.status, 200);
|
451 | assert.deepEqual(data, { photo_comment: PhotoComment.serializer(targetComment) });
|
452 | });
|
453 | });
|
454 |
|
455 | it('casts uuids correctly as queryParams', async function() {
|
456 | const MemServer = require('../../lib/index.js');
|
457 | const { PhotoComment } = MemServer.Models;
|
458 |
|
459 | MemServer.start();
|
460 | window.$ = require('jquery');
|
461 |
|
462 | const targetComments = PhotoComment.findAll({ uuid: '499ec646-493f-4eea-b92e-e383d94182f4' });
|
463 |
|
464 | await window.$.ajax({
|
465 | type: 'GET', url: '/photo-comments?uuid=499ec646-493f-4eea-b92e-e383d94182f4'
|
466 | }).then((data, textStatus, jqXHR) => {
|
467 | assert.equal(jqXHR.status, 200);
|
468 | assert.deepEqual(data, { photo_comments: PhotoComment.serializer(targetComments) });
|
469 | });
|
470 | });
|
471 |
|
472 | it('casts ethereum addresses correctly as string request.params', async function() {
|
473 | this.timeout(10000);
|
474 |
|
475 | const MemServer = require('../../lib/index.js');
|
476 | const { EthereumAccount } = MemServer.Models;
|
477 |
|
478 | MemServer.start();
|
479 | window.$ = require('jquery');
|
480 |
|
481 | const targetAccount = EthereumAccount.findBy({
|
482 | address: '0x7be8315acfef37816c9ad4dc5e82195f2a52934c5d0c74883f9978675e26d600'
|
483 | });
|
484 |
|
485 | await window.$.ajax({
|
486 | type: 'GET',
|
487 | url: '/ethereum-accounts/0x7be8315acfef37816c9ad4dc5e82195f2a52934c5d0c74883f9978675e26d600'
|
488 | }).then((data, textStatus, jqXHR) => {
|
489 | assert.equal(jqXHR.status, 200);
|
490 | assert.deepEqual(data, { ethereum_account: EthereumAccount.serializer(targetAccount) });
|
491 | });
|
492 | });
|
493 |
|
494 | it('casts ethereum addresses correctly as string request.queryParams', async function() {
|
495 | this.timeout(10000);
|
496 |
|
497 | const MemServer = require('../../lib/index.js');
|
498 | const { EthereumAccount } = MemServer.Models;
|
499 |
|
500 | MemServer.start();
|
501 | window.$ = require('jquery');
|
502 |
|
503 | const targetAccounts = EthereumAccount.findAll({
|
504 | address: '0x7be8315acfef37816c9ad4dc5e82195f2a52934c5d0c74883f9978675e26d600'
|
505 | });
|
506 |
|
507 | await window.$.ajax({
|
508 | type: 'GET',
|
509 | url: '/ethereum-accounts?address=0x7be8315acfef37816c9ad4dc5e82195f2a52934c5d0c74883f9978675e26d600'
|
510 | }).then((data, textStatus, jqXHR) => {
|
511 | assert.equal(jqXHR.status, 200);
|
512 | assert.deepEqual(data, { ethereum_accounts: EthereumAccount.serializer(targetAccounts) });
|
513 | });
|
514 |
|
515 | });
|
516 | });
|
517 | });
|