Test for an AJV-based middleware fails - jestjs

I am trying to create a middleware that validates data send to my Firebase HTTPS functions. It's meant to simply take the data, check it, pass it through if it is valid and throw an HttpsError when it is invalid (plus it throws an error if for whatever reason the schema can't be loaded).
The Test
This loads a valid schema but submits invalid data:
it("should fail with an error when submitting invalid data", () => {
const middleware = createValidationMiddleware("customer.schema.json");
expect(() => middleware({})).toThrow(HttpsError);
});
The Validation Script
export const createValidationMiddleware: (schema: Schemas) => Middleware =
(schema) => (data, context, next) => {
const validate = ajv.getSchema(schema);
if (!validate)
throw new HttpsError(
"failed-precondition",
`schema ${schema} does not exist`
);
if (!validate(data))
throw new HttpsError(
"invalid-argument",
`The data is invalid: ${validate.errors}`
);
return next(data, context);
};
The Error
I can see in the terminal all the AJV validation errors, i.e.:
ValidationError: validation failed
The test fails with:
FAIL src/middlewares/validation.test.ts
● Test suite failed to run
Jest worker encountered 4 child process exceptions, exceeding retry limit
at ChildProcessWorker.initialize (node_modules/jest-worker/build/workers/ChildProcessWorker.js:170:21)
I think the problem might be the ValidationErrors thrown by AJV, and I tried wrapping the validate(data) in a try / catch, but the error was the same, and Google hasn't been particularly helpful...

Related

UnhandledPromiseRejectionWarning Persists Even After Chaining a .catch()

My system consists of an Angular UI and a Node API. The UI submits a file to the API for processing, then gets the result back. This all works - however - the API sometimes fails at processing unexpected data.
I want to be able to catch the error(s) when they arise, stop execution so they won't screw up the UI, then send a message back to UI.
Here is my code so far:
const IncomingForm = require('formidable').IncomingForm;
asynch function myApi(req, res)
{
try // (1)
{
var form = new IncomingForm(...);
form.on('file', async(field, file) =>
{
const [result] = await sometimesBad(inParam); // (2) attach .catch(...);
...
res.send({goodFinal}); // execution should not reach here if error occurs before
});
form.on('end', ()=> {})
form.parse(req)
}
catch (erApi) // (3)
{
... // (4)
}
}
async function sometimesBad(x)
{
try // (5)
{
... lines of code could have run-time error depends on x ...
return goodResult;
}
catch(err2) // (6)
{
... // (7)
}
}
Currently, after hours of searching and trial and error, I:
am able to send a message back by chaining a .catch() at (2)
am unable to stop the execution via any combination of (1), (3), (4), (5), (6), (7), including the use of next(), throw new Error(), await Promise.reject(), return Promise.reject().
am always getting UnhandledPromiseRejectionWarning: Unhandled promise rejection.
Node version: 14.9
Update: In addition to accepted answer, there is no need to have (5), (6), (7).
In your code if (2) throws the error indeed is not handled. To handle it, you need to wrap the code inside async (field, file) => ... into try / catch, similar to how you did on the top level of middleware, and inside the catch you do next(error). Also add default error handler after all routes in your app. See How to return error to the client client without making node server crash? regarding that.
You can stop unhandledRejection(s) from crashing your app by handling them. However, if you fail to handle them using catch blocks, you can also watch for events on the process.
Code Example from the Docs:
process.on('unhandledRejection', (reason, promise) => {
console.log('Unhandled Rejection at:', promise, 'reason:', reason);
// Application specific logging, throwing an error, or other logic here
});
somePromise.then((res) => {
return reportToUser(JSON.pasre(res)); // Note the typo (`pasre`)
}); // No `.catch()` or `.then()`
Alternatively, you can make your sometimesBad function return a Promise, which would cause all errors happening inside the Promise body to be thrown, which can then be handled in the catch block of the caller.

There is any way to handle errors with correct status code in NestJS or with GraphQL?

Actually, the GQL validation errors working correctly but when I throw the nestjs error with Exceptions of nestjs I don't get proper status code or exact error on the base array or inner objects the actual error found in the dept of the array. And in the browser network tab always show the 200 status even the nestjs throw an error, there is any way to catch the error which is thrown by the nestjs and correct status code in browser network tab when catch error?
The Apollo Server constructor accepts a formatError function that is run on each error passed back to the client. This can be used to mask errors as well as for logging and the in the NestJS you can use this method in your GraphQLModule mostly exists in the app.module.ts
GraphQLModule.forRoot({
....
...
formatError: (error) => {
const graphQLFormattedError = {
message:
error.extensions?.exception?.response?.message || error.message,
code:
error.extensions?.code || "SERVER_ERROR",
name: error.extensions?.exception?.name || error.name,
};
return graphQLFormattedError;
},
}),
I solved this type of problem by throwing grapqhl type error, where you can easily manage message and code.
throw new GraphQLError(
customMessage,
{
extensions: {
code: customCode,
},
},
);

Get informative stack trace in node-pg

I am using node-pg with typescript.
I have a getPool utility from the doc https://node-postgres.com/features/pooling
export const getPool = (config?: PoolConfig) => {
const pool = new Pool(config);
pool.on('error', (err, client) => {
console.error('Unexpected error on idle client', err);
process.exit(-1);
});
return pool;
};
I use it like this in an async/await context
const pool = getPool();
await pool.query('my sql query here...');
When I have an invalid SQL query I get this kind of error:
error: null value in column "foo" violates not-null constraint
at Parser.parseErrorMessage (node_modules/pg-protocol/src/parser.ts:357:11)
at Parser.handlePacket (node_modules/pg-protocol/src/parser.ts:186:21)
at Parser.parse (node_modules/pg-protocol/src/parser.ts:101:30)
at Socket.<anonymous> (node_modules/pg-protocol/src/index.ts:7:48)
Note: I would understand if it was the pool.on('error')'s callback that stole my stack trace, but the errors are not prefixed with Unexpected error on idle client
Notice in the stack trace there is no line that points to a file in my codebase.
My problem is, I have hundereds of queries in my codebase, and I would like to be able to trace the line that called the failing pool.query(). This would help a lot to find which query triggered the error.
Expected :
error: null value in column "foo" violates not-null constraint
at ...
at mycodebase/src/myfile.ts:42
I use dirty hack (patch Pool.prototype), but it works for me:
const originalPoolQuery = Pool.prototype.query;
Pool.prototype.query = async function query(...args) {
try {
return await originalPoolQuery.apply(this, args);
} catch (e) {
// All magic is here. new Error will generate new stack, but message will copyid from e
throw new Error(e)
}
}
// After hack create pool and good luck
const pool = new Pool({})
await pool.query('SELECT * FROM ...')
Stacktrace in this case will be more informative.
I think that pool.on('error', cb) is not for catching query errors, it for connection errors (i am not sure)

node-sqlite insert fails on not-null constraint, but data are there?

I've got the following code in my nodejs app as part of an expressjs service:
let db = sqlite.open(conf.database, { Promise }).then( (dba) => {
P.all([dba.exec(accesslog_ddl),dba.exec(tokens_ddl)]).then(()=>{
console.log("Database initialized ...");
});
return dba;
});
let upsertToken = function (token, expire, customer) {
return db.then(d=>{
console.log(`${token}, ${expire}, ${customer}`);
return d.exec("insert into tokens (tokenid, name, email, cell, expire) values (?,?,'none','none','never')",
[token,expire]);
});
};
// ... expressjs setup ...
router.post('/tokens',(req,res) => {
tokens.addToken(req.body.token, req.body.expires, req.body.name)
.then(() => {res.status(201).send()})
.catch((e) => {res.status(500).send(e)});
});
The output in the logs is as follows:
bluhbleh, never, fred flintstone
{ [Error: SQLITE_CONSTRAINT: NOT NULL constraint failed: tokens.name] errno: 19, code: 'SQLITE_CONSTRAINT' }
POST /services/tokens 500 20.287 ms - 2
This proves that token, expire, and customer are all present prior to the exec() call, yet the call fails due to a NOT NULL constraint failure.
A few odd things about this code:
I'm using the promisified node-sqlite package.
I'm using a promisified expressjs router.
I really am new to promises (and nodejs generally), and am probably not doing something right.
My full code is here:
https://bitbucket.org/highaltitudearchery/locker/src/master/
You're using exec wrong. The promisified version of Database#exec only takes a single argument, the SQL to execute.

How to assert the validate error in sequelizejs unit test with jest?

I am unit testing a User model. The user name shouldn't be empty. The unit test is the following:
it('should reject no name', async () => {
name = '';
expect(await User.create(payload())).toThrow();
});
when payload has all the data except with empty name, the unit test throw the following error:
SequelizeValidationError: Validation error: Invalid validator function: unique,
Validation error: Invalid validator function: msg,
Validation error: column "cell" does not exist
However the assertion here .toThrow is not right and the test case still fails. Tried toBeUndefined with no avail. What is the correct assertion for this type of error?
Assuming User.create returns a Promise that rejects with the thrown error:
await expect(User.create(payload())).rejects.toThrow(); // SUCCESS
Note that toThrow was fixed for promises with PR 4884 so if you are using an older version of Jest (before 22.0.0) you will need to use something like toEqual:
await expect(User.create(payload())).rejects.toEqual(expect.any(Error)); // SUCCESS
you need to do this.
await expect( User.create(payload())).to.be.rejectedWith(Error)
Now, test will pass if user.create throws error, & fail if it didn't throw the error.

Resources