arb / celebrate

A joi validation middleware for Express.
MIT License
1.34k stars 65 forks source link

Unit testing validators #55

Closed juliancoleman closed 6 years ago

juliancoleman commented 6 years ago

Am I to be using Joi.validate(), or does Celebrate expose its own custom validator?

giltho commented 6 years ago

Celebrate exposes his own version of joi So basically

{ Joi } = require('celebrate');
Joi.validate(objectToValidate);

I am not entirely sure it answers to your question though

juliancoleman commented 6 years ago

Great. Thanks for the quick reply! That's all I needed to know.

juliancoleman commented 6 years ago

Perhaps that wasn't the solution. I'll outline my code below. What I'm trying to do is accomplish the same result as Joi.validate(payload, validator);

// validator.js
const { celebrate, Joi } = require('celebrate');

const Validator = celebrate({
  params : Joi.object().keys({
    eventKey : Joi.string().required(),
  }).unknown(false),
});

module.exports = Validator;
// validator_test.js
const { Joi } = require('celebrate');
const R = require('ramda');

const Validator = require('./validator');
const helpers = require('./helpers');

const validPayload = {
  params : {
    eventKey : 'testKey',
  },
};

describe('DeleteEventsValidator', () => {
  it(' does not allow unknown keys', () => {
    const payload = helpers.mergeObjectWithValidPayload(R.lensProp('params'), { unknownKey : 'hello' }, validPayload);
    const result = Joi.validate(payload, Validator);
    const { error } = result;

    expect(error).to.not.be.null;
    expect(R.prop('details', error)).to.have.length(1);

    expect(error.message).to.eql('"unknownKey" is not allowed');
  });
});

helpers.mergeObjectWithValidPayload is just a deep object merge. It will merge { unknownKey : 'hello' } into validPayload.params.

Anyway, this throws the following error:

1) DeleteEventsValidator does not allow unknown keys:
   TypeError: Cannot read property 'params' of null
giltho commented 6 years ago

Hum, something is unclear with what you are doing Celebrate is a middleware that should be used to validate inputs such as query/body/headers. I don't see why you need to use Celebrate here, isn't Joi enough ?

celebrate(...)doesn't create a validator, it creates a middleware (a function that takes req, res and next)

Also, i don't know what tries to read the params property of your object but it might not be celebrate, I think the error comes from your call of helpers.mergeObjectWithValidPayload (Although it does not change your question since it would have failed anyway because of your usage of celebrate

I realize this is not very clear, but you might want to try something more like :

const Validator = Joi.object.keys( {
  params : Joi.object().keys( {
    eventKey : Joi.string().required(),
  } ).unknown( false ),
} );

And then use Joi.validate as you do

juliancoleman commented 6 years ago

I have no excuse for not figuring that out sooner. I apologize for the miscommunication. I thought Celebrate was a wrapper around Joi. Now I know! Thanks a bunch for another quick response. I've gotten my test passing now. I can continue with my work.

And then when I want to use the Celebrate middleware, will it just be...


router.delete('/events/:eventKey', Celebrate(Validator), (req, res) => {
  // ...
});
giltho commented 6 years ago

It sould be :) Although depending on your version it might be celebrate(Validator) instead of Celebrate(Validator) (I think there's a mistake in the readme on that point)

giltho commented 6 years ago

Wait that is not true, if you actually want to use Celebrate, the actual code would be :

const Validator =  {
  params : Joi.object().keys( {
    eventKey : Joi.string().required(),
  } ).unknown( false ),
} ;

router.delete('/events/:eventKey', celebrate(validator), (req, res) => {
   // ...
});

celebrate takes an object with properties params, headers etc. (at least one of them), and each of these property should be a Joi schema.

juliancoleman commented 6 years ago

That is correct, yes