test(core): api integration tests
MODIFY
packages/core/src/http.listener.spec.ts
packages/core/src/http.listener.spec.ts
-import { map, mapTo, tap } from 'rxjs/operators';
+import { throwError } from 'rxjs';
+import { mapTo, switchMap } from 'rxjs/operators';
import * as request from 'supertest';
-import { combineRoutes } from './effects/effects.combiner';
import { Effect } from './effects/effects.interface';
-import { HttpRequest } from './http.interface';
import { httpListener } from './http.listener';
-import { matchPath, use } from './operators';
+import { matchPath } from './operators';
+import { HttpError } from './util/error.util';
describe('Http listener', () => {
-
it('reacts to attached effect', async () => {
- const effect$: Effect = req$ => req$.pipe(
- matchPath('/test'),
- mapTo({ status: 200 })
- );
+ const effect$: Effect = req$ =>
+ req$.pipe(matchPath('/test'), mapTo({ status: 200 }));
const app = httpListener({ effects: [effect$] });
@@ -22,9 +19,10 @@ describe('Http listener', () => {
});
it('reacts to throwed exception', async () => {
- const effect$: Effect = req$ => req$.pipe(
+ const effect$: Effect = req$ =>
+ req$.pipe(
matchPath('/test'),
- tap(() => { throw new Error(); }),
+ switchMap(() => throwError(new HttpError('test', 500))),
mapTo({ status: 200 })
);
@@ -34,73 +32,4 @@ describe('Http listener', () => {
.get('/test')
.expect(500);
});
-
- it('integrates with "use" operator', async () => {
- const middleware$: Effect<HttpRequest> = req$ => req$
- .pipe(tap(req => req.test = 'test'));
-
- const effect$: Effect = req$ => req$.pipe(
- matchPath('/test'),
- use(middleware$),
- map(req => ({ status: 200, body: { test: req.test } })),
- );
-
- const app = httpListener({ effects: [effect$] });
-
- return request(app)
- .get('/test')
- .expect(200, { test: 'test' });
- });
-
- it('integrates with "matchPath" operator', async () => {
- const effect$1: Effect = req$ => req$.pipe(
- matchPath('/test/:id/foo'),
- map(req => req.params.id),
- map(id => ({ status: 200, body: { id } })),
- );
-
- const effect$2: Effect = req$ => req$.pipe(
- matchPath('/test/:id/foo/:id/test'),
- map(req => req.params),
- map(params => ({ status: 200, body: { id: params.id } })),
- );
-
- const effect$3: Effect = req$ => req$.pipe(
- matchPath('/test/:id/bar/:type/test'),
- map(req => req.params),
- map(params => ({ status: 200, body: { id: params.id, type: params.type } })),
- );
-
- const app = httpListener({ effects: [effect$1, effect$2, effect$3] });
-
- return Promise.all([
- request(app)
- .get('/test/2/foo')
- .expect(200, { id: '2' }),
-
- request(app)
- .get('/test/2/foo/marble/test')
- .expect(200, { id: 'marble' }),
-
- request(app)
- .get('/test/2/bar/marble/test')
- .expect(200, { id: '2', type: 'marble' }),
- ]);
- });
-
- it('integrates with "combineRoutes"', async () => {
- const effect$: Effect = req$ => req$.pipe(
- matchPath('/user/:id'),
- map(req => req.params),
- map(params => ({ status: 200, body: { id: params.id, version: params.version } })),
- );
-
- const api$ = combineRoutes('/api/:version', [ effect$ ]);
- const app = httpListener({ effects: [api$] });
-
- return request(app)
- .get('/api/v1/user/1')
- .expect(200, { version: 'v1', id: '1' });
- });
-
});

ADD
packages/core/src/specs/api.integration.spec.ts
+import { of } from 'rxjs';
+import { filter, map, mapTo, switchMap } from 'rxjs/operators';
+import * as request from 'supertest';
+import { combineRoutes } from '../effects/effects.combiner';
+import { Effect } from '../effects/effects.interface';
+import { HttpRequest } from '../http.interface';
+import { httpListener } from '../http.listener';
+import { bodyParser$ } from '../middlewares/bodyParser.middleware';
+import { matchPath, matchType, use } from '../operators';
+
+const MOCKED_USER_LIST = [{ id: 1 }, { id: 2 }];
+
+const authorize$: Effect<HttpRequest> = request$ =>
+ request$.pipe(filter(req => req.headers.authorization === 'Bearer test'));
+
+const root$: Effect = req$ =>
+ req$.pipe(matchPath('/'), matchType('GET'), mapTo({ status: 200 }));
+
+const getUserList$: Effect = request$ =>
+ request$.pipe(
+ matchPath('/'),
+ matchType('GET'),
+ switchMap(() => of(MOCKED_USER_LIST)),
+ map(users => ({ body: users }))
+ );
+
+const getUserSingle$: Effect = request$ =>
+ request$.pipe(
+ matchPath('/:id'),
+ matchType('GET'),
+ map(req => req.params),
+ map(({ id, version }) => ({ body: { id, version } }))
+ );
+
+const postUser$: Effect = request$ =>
+ request$.pipe(
+ matchPath('/'),
+ matchType('POST'),
+ use(authorize$),
+ map(req => req.body),
+ map(response => ({ body: response }))
+ );
+
+const user$ = combineRoutes('/user', [getUserList$, getUserSingle$, postUser$]);
+
+const api$ = combineRoutes('/api/:version', [root$, user$]);
+
+const app = httpListener({
+ middlewares: [bodyParser$],
+ effects: [api$]
+});
+
+describe('API integration', () => {
+ it('returns 404 when route not found: /', async () =>
+ request(app)
+ .post('/')
+ .expect(404));
+
+ it('returns only status 200: /api/v1', async () =>
+ request(app)
+ .get('/api/v1')
+ .expect(200));
+
+ it('returns object: /api/v1/user', async () =>
+ request(app)
+ .get('/api/v1/user')
+ .expect(200, MOCKED_USER_LIST));
+
+ it('intercepts :id and :version parameter from url: /api/v1/user/:id', async () =>
+ request(app)
+ .get('/api/v1/user/123')
+ .expect(200, { id: 123, version: 'v1' }));
+
+ it('parses body and returns echo for secured route: /api/v1/user', async () =>
+ request(app)
+ .post('/api/v1/user')
+ .set('Authorization', 'Bearer test')
+ .send({ test: 'test' })
+ .expect(200, { test: 'test' }));
+});

test(core): api integration tests