Jest Test suite failed to run - AssertionError [ERR_ASSERTION]: opts.entryPoint required - node.js

We had a test running successfully for our code that uses the notifications-node-client to send emails, eg
import { NotifyClient } from 'notifications-node-client';
import notify from './notify';
import config from '../../../config';
jest.mock('fs');
jest.mock('notifications-node-client');
describe('notify', () => {
const EMAIL = 'user#test.com';
const REGION_GB = 'gb';
const REGION_NI = 'ni';
const feedbackOptions = {
personalisation: {
satisfactionLevel: 'satisfied',
improvements: 'improvements',
},
};
test('for original GB submission sendEmail should be called and true returned', async () => {
NotifyClient.prototype.sendEmail.mockReturnValue({
body: {
id: 1,
},
});
expect(await notify(EMAIL, 'submissionSuccess', REGION_GB)).toBeTruthy();
expect(NotifyClient.prototype.sendEmail).toHaveBeenCalledWith(
config.notify.templates.submissionSuccess.gb,
EMAIL,
undefined,
);
});
...
Having swapped out our Winston logging solution to use #dwp/node-logger, the 'notify' tests now do not run, failing with
● Test suite failed to run
AssertionError [ERR_ASSERTION]: opts.entryPoint required
at new Thrift (node_modules/thriftrw/thrift.js:64:5)
at Object.<anonymous> (node_modules/jaeger-client/src/thrift.js:21:20)
at Object.<anonymous> (node_modules/jaeger-client/src/reporters/remote_reporter.js:15:1)
All of the other test suites in the project still run successfully.
Could anyone point me in the right direction about what change to make?
The code that we're testing is
import { NotifyClient } from 'notifications-node-client';
import config from '../../../config/index';
import { RegionKeys } from '../../../config/types';
import logger from '../../../xxxx/lib/logger';
type TemplateId = 'submissionSuccess' | 'supportingEvidence' | 'feedbackTemplateId';
type FeedbackOptions = {
personalisation: {
satisfactionLevel: string,
improvements: string,
},
reference?: string,
}
const { apiKey, templates, proxy } = config.notify;
export default async function notify(
email: string,
templateId: TemplateId,
region: RegionKeys,
options?: FeedbackOptions,
): Promise<boolean> {
let notifyTemplate;
let notifyApiKey;
let notifyOptions;
try {
if (templateId === 'feedbackTemplateId' && !options) {
throw new Error(`Unable to send email - mismatch between template ID (${templateId}) and options supplied`);
} else if (options && templateId !== 'feedbackTemplateId') {
notifyOptions = {};
} else {
notifyOptions = options;
}
const notifyClient = new NotifyClient(apiKey[region]);
notifyClient.setProxy(proxy);
notifyTemplate = templateId === 'feedbackTemplateId' ? templates.feedbackTemplateId : templates[templateId][region];
logger.debug(`apiKey: ${notifyApiKey}`);
logger.debug(`notify template Id: ${notifyTemplate}`);
logger.debug(`proxy: ${JSON.stringify(proxy)}`);
const response = await notifyClient.sendEmail(notifyTemplate, email, notifyOptions);
if (response.body) {
logger.info(`confirmation email sent to ${email} and relates to message id ${response.body.id}`);
}
return true;
} catch (err: any) {
logger.error(`there was an error sending the message: ${err.message}`);
return false;
}
}

Related

Methods called on jest mocked response fails with error

I have the below line in my service.
const timeline = await buildClient.getBuildTimeline(project.id, buildId);
The above code is successfully mocked by jest as below using the __mocks__ approach.
jest.mock('azure-devops-extension-api', () => {
return { getClient: (client: any) => new GitRestClient() };
});
export const mockGetItems = jest
.fn()
.mockReturnValue({ records: [{ state: 2, type: 'TASK', task: { id: TASK_IDS[0] } }] });
class GitRestClient {
public getBuildTimeline(projectId: string, buildId: string): Promise<Timeline[]> {
return new Promise((resolve) => resolve(mockGetItems()));
}
}
But when I get error during jest test in the below find method which acts upon the mocked response.
const record = timeline.records.find((rec) => {
return rec.type === 'Task';
});
How should I update so that the above code snippet is mocked perfectly to return the record value using the find method.

How test listener on my custom event emitter in node typescript

I'm trying to test a service that has a listener of the a custom Event Emitter in node with typescript and mocha, sinon.
My custom emmiter;
class PublishEmitter extends EventEmitter {
publish(id: string) {
this.emit('publish', id);
}
}
My service use case:
export default class PublishVehicle {
constructor(
private findVehicle: FindVehicle, // Service that contains find methods on repository
private updateVehicle: UpdateVehicle, // Service that contains update methods on repository
private logger: ILogger,
) {
this.producer = producer;
this.logger = logger;
}
listen() {
this.logger.log('debug', 'Creating listener on PublishEmitter');
this.publishListener = this.publishListener.bind(this);
pubsub.on('publish', this.publishListener);
}
/**
* Listener on PublishEmitter.
*
* #param event
*/
async publishListener(event: string) {
try {
const vehicle = await this.findVehicle.findById(event);
if (vehicle?.state === State.PENDING_PUBLISH) {
//
const input = { state: State.PUBLISH };
await this.updateVehicle.update(vehicle.id, input);
this.logger.log('debug', `Message sent at ${Date.now() - now} ms`);
}
this.logger.log('debug', `End Vehicle's Publish Event: ${event}`);
} catch (error) {
this.logger.log('error', {
message: `publishListener: ${event}`,
stackTrace: error,
});
}
}
}
and in my test file:
import chai from 'chai';
const { expect } = chai;
import sinon from 'sinon';
import { StubbedInstance, stubInterface } from 'ts-sinon';
import pubsub from './PublishEmitter';
describe('Use Case - Publish Vehicle', function () {
let mockRepository: MockVehicleRepository;
let publishVehicle: PublishVehicle;
let findVehicleUseCase: FindVehicle;
let updateVehicleUseCase: UpdateVehicle;
before(() => {
const logger = Logger.getInstance();
mockRepository = new MockVehicleRepository();
findVehicleUseCase = new FindVehicle(mockRepository, logger);
updateVehicleUseCase = new UpdateVehicle(mockRepository);
publishVehicle = new PublishVehicle(
findVehicleUseCase,
updateVehicleUseCase,
logger,
);
});
afterEach(() => {
// Restore the default sandbox here
sinon.restore();
});
it('Should emit event to publish vehicle', async () => {
const vehicle = { ... }; // dummy data
const stubFindById = sinon
.stub(mockRepository, 'findById')
.returns(Promise.resolve(vehicle));
const stubUpdate = sinon
.stub(mockRepository, 'update')
.returns(Promise.resolve(vehicle));
const spy = sinon.spy(publishVehicle, 'publishListener');
publishVehicle.listen();
pubsub.publish(vehicle.id);
expect(spy.calledOnce).to.be.true; // OK
expect(stubFindById.calledOnce).to.be.true; // Error (0 call)
expect(stubUpdate.calledOnce).to.be.true; // Error (0 call)
});
});
When I debug this test, indeed the methods are called but they seem to be executed after it has gone through the last expect lines.
The output:
1 failing
1) Use Case - Publish Vehicle
Should emit event to publish vehicle:
AssertionError: expected false to be true
+ expected - actual
-false
+true
UPDATE
Finally I was be able to solve my problem wrapping expect lines in setTimeout.
setTimeout(() => {
expect(spy.calledOnce).to.be.true; // OK
expect(stubFindById.calledOnce).to.be.true; // OK
expect(stubUpdate.calledOnce).to.be.true; // OK
done();
}, 0);

Why am I only getting Mailgun.js error in Cloud Run?

I'm trying to send an email using Mailgun's npm client - Mailgun.js.
When sending in development mode, everything works correctly. But when I upload the Node server to Cloud Run, something breaks.
Here is the code in the sendEmail helper file:
import formData from 'form-data';
import Mailgun from 'mailgun.js';
const sendEmail = async ({ to, subject, text, attachment, scheduledDate }) => {
const mailgun = new Mailgun(formData);
const mg = mailgun.client({
username: 'api',
key: process.env.MAILGUN_KEY,
url: 'https://api.eu.mailgun.net'
});
const data = {
from: `<myemail#mydomain.com>`,
to,
subject,
text
};
if (attachment) {
data.attachment = attachment;
}
if (scheduledDate) {
data['o:deliverytime'] = new Date(scheduledDate).toUTCString();
}
try {
const result = await mg.messages.create(process.env.MAILGUN_DOMAIN, data);
if (result.status && result.status !== 200) {
throw ({ code: result.status, message: result.message });
}
return true;
} catch(err) {
console.log(err);
return { error: err };
}
};
export default sendEmail;
And then in another file:
import { Router } from 'express';
import generateInvoicePDF from '../helpers/generateInvoicePDF.js';
import sendEmail from '../helpers/sendEmail.js';
const router = Router();
router.post('/email', async (req, res, next) => {
try {
const file = await generateInvoicePDF(invoice);
if (file?.error) {
throw ({ code: pdf.error.code, message: pdf.error.message });
}
const email = await sendEmail({
to: 'testemail#example.com',
subject: 'Invoice',
text: 'Test',
attachment: { filename: 'Invoice', data: file }
});
if (email?.error) {
throw ({ code: email.error.code, message: email.error.message });
}
res.status(200).json({ success: true });
} catch(err) {
next(err);
}
});
export default router;
The error I get when in production mode in Cloud Run's logs is:
TypeError: fetch failed
at Object.processResponse (node:internal/deps/undici/undici:5575:34)
at node:internal/deps/undici/undici:5901:42
at node:internal/process/task_queues:140:7
at AsyncResource.runInAsyncScope (node:async_hooks:202:9)
at AsyncResource.runMicrotask (node:internal/process/task_queues:137:8) {
cause: TypeError: object2 is not iterable
at action (node:internal/deps/undici/undici:1661:39)
at action.next (<anonymous>)
at Object.pull (node:internal/deps/undici/undici:1709:52)
at ensureIsPromise (node:internal/webstreams/util:172:19)
at readableStreamDefaultControllerCallPullIfNeeded (node:internal/webstreams/readablestream:1884:5)
at node:internal/webstreams/readablestream:1974:7
}
Why the hell does it work in development mode on my local machine, but not when uploaded to Cloud Run?
For anyone struggling with something similar - I eventually figured out the problem.
On my local machine, where everything was working as expected, I'm using Node v16.15.0, whereas in the Dockerfile, I had specified
FROM node:latest
and therefore Cloud Run was using a newer version, which led to the problems...
I've now deployed using version 16.15.0 and everything works fine

How to make kuzzle-device-manager plugin API actions works?

I successfully installed and loaded kuzzle-device-manager in the backend file:
import { Backend } from 'kuzzle';
import { DeviceManagerPlugin } from 'kuzzle-device-manager';
const app = new Backend('playground');
console.log(app.config);
const deviceManager = new DeviceManagerPlugin();
const mappings = {
updatedAt: { type: 'date' },
payloadUuid: { type: 'keyword' },
value: { type: 'float' }
}
deviceManager.devices.registerMeasure('humidity', mappings)
app.plugin.use(deviceManager)
app.start()
.then(async () => {
// Interact with Kuzzle API to create a new index if it does not already exist
console.log(' started!');
})
.catch(console.error);
But when i try to use controllers from that plugin for example device-manager/device with create action i get an error output.
Here is my "client" code in js:
const { Kuzzle, WebSocket } = require("kuzzle-sdk")
const kuzzle = new Kuzzle(
new WebSocket('KUZZLE_IP')
)
kuzzle.on('networkError', error => {
console.error('Network Error: ', error);
})
const run = async () => {
try {
// Connects to the Kuzzle server
await kuzzle.connect();
// Creates an index
const result = await kuzzle.query({
index: "nyc-open-data",
controller: "device-manager/device",
action: "create",
body: {
model: "model-1234",
reference: "reference-1234"
}
}, {
queuable: false
})
console.log(result)
} catch (error) {
console.error(error.message);
} finally {
kuzzle.disconnect();
}
};
run();
And the result log:
API action "device-manager/device":"create" not found
Note: The nyc-open-data index exists and is empty.
We apologize for this mistake in the documentation, the device-manager/device:create method is not available because the plugin is using auto-provisioning until the v2.
You should send a payload to your decoder, the plugin will automatically provision the device if it does not exists https://docs.kuzzle.io/official-plugins/device-manager/1/guides/decoders/#receive-payloads

NestJS Error: Error:Cannot find module './'

I encountered a error when using NestJS. The console shows 0 error first and then crahsed with the Error: Cannot find module './'. This is similar to the
Error:Cannot find module '../module_name'.
However, for this one, it shows './'. And I tried to delete the node_module and the dist folder and rerun the npm install that was usually used to solve the similar people.
The full error message is:
[6:32:11 PM] Starting compilation in watch mode...
[6:32:14 PM] Found 0 errors. Watching for file changes.
Error: Cannot find module './'
Require stack:
- C:\Users\Vibrant\Desktop\inventory_demo\dist\client\client.service.js
- C:\Users\Vibrant\Desktop\inventory_demo\dist\client\client.controller.js
- C:\Users\Vibrant\Desktop\inventory_demo\dist\client\client.module.js
- C:\Users\Vibrant\Desktop\inventory_demo\dist\app.module.js
- C:\Users\Vibrant\Desktop\inventory_demo\dist\main.js
at Function.Module._resolveFilename (node:internal/modules/cjs/loader:933:15)
at Function.Module._load (node:internal/modules/cjs/loader:778:27)
at Module.require (node:internal/modules/cjs/loader:1005:19)
at require (node:internal/modules/cjs/helpers:102:18)
at Object.<anonymous> (C:\Users\Vibrant\Desktop\inventory_demo\src\client\client.service.ts:3:1)
at Module._compile (node:internal/modules/cjs/loader:1101:14)
The client.service.js:
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClientService = void 0;
const common_1 = require("#nestjs/common");
const client_1 = require("./");
let ClientService = class ClientService {
async client(clientWhereUniqueInput) {
return client_1.default.client.findUnique({
where: clientWhereUniqueInput,
});
}
async clients(params) {
const { skip, take, cursor, where } = params;
return client_1.default.client.findMany({
skip,
take,
cursor,
where,
});
}
async createClient(data) {
return client_1.default.client.create({ data });
}
async deleteClient(where) {
return client_1.default.client.delete({ where });
}
async getClientByID(id) {
return await client_1.default.client.findUnique({
where: { client_id: Number(id) },
});
}
async updateClient(params) {
const { where, data } = params;
return client_1.default.client.update({ where, data });
}
};
ClientService = __decorate([
(0, common_1.Injectable)()
], ClientService);
exports.ClientService = ClientService;
//# sourceMappingURL=client.service.js.map
I have two ts files has client in the name:
First one is called client.ts which is a prism database client:
import { PrismaClient } from '#prisma/client';
interface CustomNodeJsGlobal extends NodeJS.Global {
prisma: PrismaClient;
}
// Prevent multiple instances of Prisma Client in development
declare const global: CustomNodeJsGlobal;
const prisma = global.prisma || new PrismaClient();
if (process.env.NODE_ENV === 'development') global.prisma = prisma;
export default prisma;
The second one is a model called client, it only imports the basic nestjs modules I think.
The client module.ts
import { Module } from '#nestjs/common';
import { ClientController } from './client.controller';
import { ClientService } from './client.service';
#Module({
controllers: [ClientController],
providers: [ClientService]
})
export class ClientModule {}
The client controller(haven't start it yet):
import { Controller } from '#nestjs/common';
#Controller('client')
export class ClientController {}
and the client service:
import { Injectable } from '#nestjs/common';
import { Client, Prisma } from '#prisma/client';
import prisma from 'src/client';
#Injectable()
export class ClientService {
// The service to return a single client
async client(
clientWhereUniqueInput: Prisma.ClientWhereUniqueInput,
): Promise<Client> {
return prisma.client.findUnique({
where: clientWhereUniqueInput,
});
}
// The service to return a list of clients
async clients(params: {
skip?: number;
take?: number;
cursor?: Prisma.ClientWhereUniqueInput;
where?: Prisma.ClientWhereInput;
}): Promise<Client[]> {
const { skip, take, cursor, where } = params;
return prisma.client.findMany({
skip,
take,
cursor,
where,
});
}
// The service to create a client
async createClient(data: Prisma.ClientCreateInput): Promise<Client> {
return prisma.client.create({ data });
}
// The service to delete a client
async deleteClient(where: Prisma.ClientWhereUniqueInput): Promise<Client> {
return prisma.client.delete({ where });
}
// The service to find an client by id
async getClientByID(id: string) {
return await prisma.client.findUnique({
where: { client_id: Number(id) },
});
}
// The service to update a client
async updateClient(params: {
where: Prisma.ClientWhereUniqueInput;
data: Prisma.ClientUpdateInput;
}): Promise<Client> {
const { where, data } = params;
return prisma.client.update({ where, data });
}
// End of class
}
The problem is I used a model called client.
The solution is to rename it to clients since NestJS itself uses the name client. After the name modification, I will need to delete the whole dist folder and run npm rebuild.

Resources