tests(middleware-joi): move to specific folder
ADD
packages/middleware-joi/__tests__/integration.spec.ts
+import { validator$, Joi } from '../src';
+
+describe('Joi middleware - Integration', () => {
+ it('not implemented');
+});

ADD
packages/middleware-joi/__tests__/unit/body.spec.ts
+import { validator$, Joi } from '../../src';
+import { of } from 'rxjs';
+import { HttpRequest, HttpResponse, bodyParser$ } from '@marblejs/core';
+const MockReq = require('mock-req');
+
+describe('Joi middleware - Body', () => {
+ it('should throws an error if dont pass a required field', done => {
+ expect.assertions(2);
+
+ const request = new MockReq({
+ method: 'POST'
+ });
+
+ const req$ = of(request as HttpRequest);
+ const res = {} as HttpResponse;
+ const schema = {
+ body: Joi.object({
+ token: Joi.string()
+ .token()
+ .required()
+ })
+ };
+
+ const http$ = bodyParser$(req$, res, {});
+ const valid$ = validator$(schema)(http$, res, {});
+
+ valid$.subscribe(
+ () => {
+ fail('Exceptions should be thrown');
+ done();
+ },
+ error => {
+ expect(error).toBeDefined();
+ expect(error.message).toBe(
+ 'child "token" fails because ["token" is required]'
+ );
+ done();
+ }
+ );
+
+ request.write({});
+ request.end();
+ });
+
+ it('should throws an error if pass a unknown field', done => {
+ expect.assertions(2);
+
+ const request = new MockReq({
+ method: 'POST'
+ });
+
+ const req$ = of(request as HttpRequest);
+ const res = {} as HttpResponse;
+ const schema = {
+ body: Joi.object({
+ token: Joi.string()
+ .token()
+ .required()
+ })
+ };
+
+ const http$ = bodyParser$(req$, res, {});
+ const valid$ = validator$(schema)(http$, res, {});
+
+ valid$.subscribe(
+ () => {
+ fail('Exceptions should be thrown');
+ done();
+ },
+ error => {
+ expect(error).toBeDefined();
+ expect(error.message).toBe('"id" is not allowed');
+ done();
+ }
+ );
+
+ request.write({ token: '181782881DB38D84', id: 5 });
+ request.end();
+ });
+
+ it('should validates body with a unknown field', done => {
+ expect.assertions(2);
+
+ const request = new MockReq({
+ method: 'POST'
+ });
+
+ const req$ = of(request as HttpRequest);
+ const res = {} as HttpResponse;
+ const schema = {
+ body: Joi.object({
+ token: Joi.string()
+ .token()
+ .required()
+ })
+ };
+
+ const http$ = bodyParser$(req$, res, {});
+ const valid$ = validator$(schema, { allowUnknown: true })(http$, res, {});
+
+ valid$.subscribe(data => {
+ expect(data).toBeTruthy();
+ expect(data.body).toEqual({ token: '181782881DB38D84', id: 5 });
+ done();
+ });
+
+ request.write({ token: '181782881DB38D84', id: 5 });
+ request.end();
+ });
+});

ADD
packages/middleware-joi/__tests__/unit/header.spec.ts
+import { validator$, Joi } from '../../src';
+import { of } from 'rxjs';
+import { HttpRequest, HttpResponse } from '@marblejs/core';
+const MockReq = require('mock-req');
+
+describe('Joi middleware - Header', () => {
+ it('should throws an error if dont pass a required field', done => {
+ expect.assertions(2);
+
+ const request = new MockReq({
+ method: 'GET',
+ headers: {}
+ });
+
+ const req$ = of(request as HttpRequest);
+ const res = {} as HttpResponse;
+ const schema = {
+ headers: Joi.object({
+ token: Joi.string()
+ .token()
+ .required()
+ })
+ };
+ const http$ = validator$(schema)(req$, res, {});
+
+ http$.subscribe(
+ () => {
+ fail('Exceptions should be thrown');
+ done();
+ },
+ error => {
+ expect(error).toBeDefined();
+ expect(error.message).toBe(
+ 'child "token" fails because ["token" is required]'
+ );
+ done();
+ }
+ );
+ });
+
+ it('should throws an error if pass a unknown field', done => {
+ expect.assertions(2);
+
+ const request = new MockReq({
+ method: 'GET',
+ headers: {
+ end: 1
+ }
+ });
+
+ const req$ = of(request as HttpRequest);
+ const res = {} as HttpResponse;
+ const schema = {
+ headers: Joi.object({
+ start: Joi.date()
+ })
+ };
+ const http$ = validator$(schema)(req$, res, {});
+
+ http$.subscribe(
+ () => {
+ fail('Exceptions should be thrown');
+ done();
+ },
+ error => {
+ expect(error).toBeDefined();
+ expect(error.message).toBe('"end" is not allowed');
+ done();
+ }
+ );
+ });
+
+ it('should validates header with unknown field', done => {
+ expect.assertions(2);
+
+ const request = new MockReq({
+ method: 'GET',
+ headers: {
+ end: 1,
+ start: '2018'
+ }
+ });
+
+ const req$ = of(request as HttpRequest);
+ const res = {} as HttpResponse;
+ const schema = {
+ headers: Joi.object({
+ start: Joi.number().integer()
+ })
+ };
+ const http$ = validator$(schema, { stripUnknown: true })(req$, res, {});
+
+ http$.subscribe(data => {
+ expect(data).toBeTruthy();
+ expect(data.headers).toEqual({ start: 2018 });
+ done();
+ });
+ });
+
+ it('should validates header with two fields', done => {
+ expect.assertions(1);
+
+ const request = new MockReq({
+ method: 'GET',
+ headers: {
+ token: '181782881DB38D84',
+ id: 5
+ }
+ });
+
+ const req$ = of(request as HttpRequest);
+ const res = {} as HttpResponse;
+ const schema = {
+ headers: Joi.object({
+ token: Joi.string()
+ .token()
+ .required(),
+ id: Joi.number()
+ .integer()
+ .min(1)
+ })
+ };
+ const http$ = validator$(schema)(req$, res, {});
+
+ http$.subscribe(data => {
+ expect(data).toBeTruthy();
+ done();
+ });
+ });
+});

ADD
packages/middleware-joi/__tests__/unit/params.spec.ts
+import { validator$, Joi } from '../../src';
+import { of } from 'rxjs';
+import { HttpRequest, HttpResponse, RouteParameters } from '@marblejs/core';
+
+const reqMatched = (
+ url: string,
+ query = '',
+ matchers: string[] = [],
+ params: RouteParameters = {}
+) =>
+ (({
+ url: !!query ? `${url}?${query}` : url,
+ matchers,
+ params
+ } as any) as HttpRequest);
+
+describe('Joi middleware - Params', () => {
+ it('should throws an error if dont pass a required field', done => {
+ expect.assertions(2);
+
+ const req$ = of(reqMatched('/test', null, [], {}));
+ const res = {} as HttpResponse;
+ const schema = {
+ params: Joi.object({
+ id: Joi.string()
+ .token()
+ .required()
+ })
+ };
+ const http$ = validator$(schema)(req$, res, {});
+
+ http$.subscribe(
+ () => {
+ fail('Exceptions should be thrown');
+ done();
+ },
+ error => {
+ expect(error).toBeDefined();
+ expect(error.message).toBe(
+ 'child "id" fails because ["id" is required]'
+ );
+ done();
+ }
+ );
+ });
+
+ it('should throws an error if pass a invalid field', done => {
+ expect.assertions(2);
+
+ const req$ = of(reqMatched('/test/@@@', null, [], { id: '@@@' }));
+ const res = {} as HttpResponse;
+ const schema = {
+ params: Joi.object({
+ id: Joi.string().token()
+ })
+ };
+ const http$ = validator$(schema)(req$, res, {});
+
+ http$.subscribe(
+ () => {
+ fail('Exceptions should be thrown');
+ done();
+ },
+ error => {
+ expect(error).toBeDefined();
+ expect(error.message).toBe(
+ 'child "id" fails because ["id" must only contain alpha-numeric and underscore characters]'
+ );
+ done();
+ }
+ );
+ });
+
+ it('should validates params with a default value', done => {
+ expect.assertions(2);
+
+ const req$ = of(reqMatched('/test', null, [], {}));
+ const res = {} as HttpResponse;
+ const schema = {
+ params: Joi.object({
+ id: Joi.string()
+ .token()
+ .default('181782881DB38D84')
+ })
+ };
+ const http$ = validator$(schema)(req$, res, {});
+
+ http$.subscribe(data => {
+ expect(data).toBeDefined();
+ expect(data.params).toEqual({ id: '181782881DB38D84' });
+ done();
+ });
+ });
+});

ADD
packages/middleware-joi/__tests__/unit/query.spec.ts
+import { validator$, Joi } from '../../src';
+import { of } from 'rxjs';
+import { HttpRequest, HttpResponse, RouteParameters } from '@marblejs/core';
+
+const reqMatched = (
+ url: string,
+ query = '',
+ matchers: string[] = [],
+ params: RouteParameters = {}
+) =>
+ (({
+ url: !!query ? `${url}?${query}` : url,
+ matchers,
+ params
+ } as any) as HttpRequest);
+
+describe('Joi middleware - Query', () => {
+ it('should throws an error if dont pass a required field', done => {
+ expect.assertions(2);
+
+ const req$ = of(reqMatched('/test', null, [], {}));
+ const res = {} as HttpResponse;
+ const schema = {
+ query: Joi.object({
+ id: Joi.string()
+ .token()
+ .required()
+ })
+ };
+ const http$ = validator$(schema)(req$, res, {});
+
+ http$.subscribe(
+ () => {
+ fail('Exceptions should be thrown');
+ done();
+ },
+ error => {
+ expect(error).toBeDefined();
+ expect(error.message).toBe(
+ 'child "id" fails because ["id" is required]'
+ );
+ done();
+ }
+ );
+ });
+
+ // FIXME: fix after merge PR
+ it.skip('should throws an error if pass a invalid field', done => {
+ expect.assertions(2);
+
+ const req$ = of(reqMatched('/test', 'id=@@@', [], {}));
+ const res = {} as HttpResponse;
+ const schema = {
+ query: Joi.object({
+ id: Joi.string().token()
+ })
+ };
+ const http$ = validator$(schema)(req$, res, {});
+
+ http$.subscribe(
+ () => {
+ fail('Exceptions should be thrown');
+ done();
+ },
+ error => {
+ expect(error).toBeDefined();
+ expect(error.message).toBe(
+ 'child "id" fails because ["id" must only contain alpha-numeric and underscore characters]'
+ );
+ done();
+ }
+ );
+ });
+
+ it.skip('should validates query with a valid value', done => {
+ expect.assertions(2);
+
+ const req$ = of(reqMatched('/test', 'id=181782881DB38D84', [], {}));
+ const res = {} as HttpResponse;
+ const schema = {
+ query: Joi.object({
+ id: Joi.string().token()
+ })
+ };
+ const http$ = validator$(schema)(req$, res, {});
+
+ http$.subscribe(data => {
+ expect(data).toBeDefined();
+ expect(data.query).toEqual({ id: '181782881DB38D84' });
+ done();
+ });
+ });
+});

ADD
packages/middleware-joi/__tests__/unit/schema.spec.ts
+import { validator$, Joi } from '../../src';
+
+describe('Joi middleware - Schema', () => {
+ it('should throws an error if using an invalid schema', done => {
+ const schema = { headers: 'foo' };
+ const http$ = validator$(schema)(null, null, {});
+ http$.subscribe(
+ () => {
+ fail('Exceptions should be thrown');
+ done();
+ },
+ error => {
+ expect(error).toBeDefined();
+ done();
+ }
+ );
+ });
+
+ it('should throws an error if using an empty schema', done => {
+ const http$ = validator$({})(null, null, {});
+ http$.subscribe(
+ () => {
+ fail('Exceptions should be thrown');
+ done();
+ },
+ error => {
+ expect(error).toBeDefined();
+ done();
+ }
+ );
+ });
+});

MODIFY
packages/middleware-joi/src/index.ts
packages/middleware-joi/src/index.ts
-export * from './joi.middleware';
+import * as Joi from 'joi';
+import { Schema, SchemaValidator } from './schema';
+import { Effect, HttpRequest, HttpError, HttpStatus } from '@marblejs/core';
+import { from, of, throwError } from 'rxjs';
+import {
+ mergeMap,
+ flatMap,
+ catchError,
+ mapTo,
+ switchMap,
+ toArray,
+ map
+} from 'rxjs/operators';
+
+const validateSource = (req: HttpRequest, rules: Map<string, any>, options) =>
+ from(rules.keys()).pipe(
+ mergeMap(rule =>
+ of(req[rule]).pipe(
+ flatMap(item =>
+ from(Joi.validate(item || {}, rules.get(rule), options))
+ ),
+ catchError((err: Error) =>
+ throwError(new HttpError(err.message, HttpStatus.BAD_REQUEST))
+ ),
+ map(result => (req[rule] = result))
+ )
+ ),
+ toArray(),
+ mapTo(req)
+ );
+
+const validator$ = (
+ schema: Schema,
+ options: Joi.ValidationOptions = {}
+): Effect<HttpRequest> => request$ => {
+ const result = Joi.validate(schema, SchemaValidator);
+ const rules = Object.keys(schema).reduce(
+ (acc, value) => acc.set(value, Joi.compile(schema[value])),
+ new Map()
+ );
+
+ if (result && result.error) {
+ return throwError(result.error);
+ }
+
+ return request$.pipe(switchMap(req => validateSource(req, rules, options)));
+};
+
+export { Joi, validator$ };

DELETE
packages/middleware-joi/src/joi.middleware.spec.ts
-import { of } from 'rxjs';
-import { HttpRequest, HttpResponse, HttpError, bodyParser$ } from '@marblejs/core';
-import { Marbles } from '@marblejs/core/dist/util/marbles.spec-util';
-import { validator$, Joi } from './joi.middleware';
-import { take } from 'rxjs/operators';
-
-const MockReq = require('mock-req');
-
-describe('Joi middleware', () => {
-
- beforeEach(() => {
- spyOn(console, 'log').and.stub();
- spyOn(console, 'error').and.stub();
- });
-
- it('validator$ throws an error if using an invalid schema', done => {
- expect.assertions(1);
-
- const request = new MockReq({
- method: 'GET',
- });
-
- const req$ = of(request as HttpRequest);
- const res = {} as HttpResponse;
- const schema = { headers: 'foo' };
- const http$ = validator$(schema)(req$, res, {});
-
- http$.subscribe(() => {
- fail('Exceptions should be thrown');
- done();
- },
- error => {
- expect(error).toBeDefined();
- done();
- });
- });
-
- it('validator$ throws an error if pass a invalid header', done => {
- expect.assertions(1);
-
- const request = new MockReq({
- method: 'GET',
- headers: {
- token: '@@@'
- }
- });
-
- const req$ = of(request as HttpRequest);
- const res = {} as HttpResponse;
- const schema = { headers: Joi.object({
- token: Joi.string().token().required(),
- }) };
- const http$ = validator$(schema)(req$, res, {});
-
- http$.subscribe(() => {
- fail('Exceptions should be thrown');
- done();
- },
- error => {
- expect(error).toBeDefined();
- done();
- });
- });
-
- it('validator$ validates header', done => {
- expect.assertions(1);
-
- const request = new MockReq({
- method: 'GET',
- headers: {
- token: '181782881DB38D84',
- }
- });
-
- const req$ = of(request as HttpRequest);
- const res = {} as HttpResponse;
- const schema = { headers: Joi.object({
- token: Joi.string().token().required(),
- }) };
- const http$ = validator$(schema)(req$, res, {});
-
- http$.subscribe(data => {
- expect(data).toBeTruthy();
- done();
- });
- });
-
- it('validator$ validates header and body', done => {
- expect.assertions(2);
-
- const request = new MockReq({
- method: 'POST',
- headers: {
- token: '181782881DB38D84',
- }
- });
-
- const req$ = of(request as HttpRequest);
- const res = {} as HttpResponse;
- const schema = {
- headers: Joi.object({
- token: Joi.string().token().required(),
- }),
- body: Joi.object({
- id: Joi.number().min(1).max(4),
- role: Joi.string().default('admin'),
- }),
- };
- const http$ = bodyParser$(req$, res, {});
- const valid$ = validator$(schema)(http$, res, {});
-
- valid$.subscribe(data => {
- expect(data).toBeTruthy();
- expect(data.body.role).toBe('admin'),
- done();
- });
-
- request.write({ id: 4 });
- request.end();
- });
-});

DELETE
packages/middleware-joi/src/joi.middleware.ts
-import * as Joi from 'joi';
-import { Schema, SchemaValidator } from './schema';
-import { Effect, HttpRequest, HttpError, HttpStatus } from '@marblejs/core';
-import { from, of, throwError } from 'rxjs';
-import {
- mergeMap,
- flatMap,
- catchError,
- mapTo,
- switchMap,
- toArray,
- map
-} from 'rxjs/operators';
-
-const validateSource = (req: HttpRequest, rules: Map<string, any>, options) =>
- from(rules.keys()).pipe(
- mergeMap(rule =>
- of(req[rule]).pipe(
- flatMap(item =>
- from(Joi.validate(item || {}, rules.get(rule), options))
- ),
- catchError((err: Error) =>
- throwError(new HttpError(err.message, HttpStatus.BAD_REQUEST))
- ),
- map(result => (req[rule] = result))
- )
- ),
- toArray(),
- mapTo(req)
- );
-
-const validator$ = (
- schema: Schema,
- options: Joi.ValidationOptions = {}
-): Effect<HttpRequest> => request$ => {
- const result = Joi.validate(schema, SchemaValidator);
- const rules = Object.keys(schema).reduce(
- (acc, value) => acc.set(value, Joi.compile(schema[value])),
- new Map()
- );
-
- if (result && result.error) {
- return throwError(result.error);
- }
-
- return request$.pipe(switchMap(req => validateSource(req, rules, options)));
-};
-
-export { Joi, validator$ };

MODIFY
packages/middleware-joi/tsconfig.json
packages/middleware-joi/tsconfig.json
"node_modules/@types",
"src/**/*.ts"
],
+
+ "exclude": [
+ "__tests__"
+ ]
}

tests(middleware-joi): move to specific folder