How to cover async function by unit test? - javascript

I'm trying to cover code below by unit test, but in code coverage report lines are still red. And I see warning that property debug is undefined. How I can successfully cover this function?
import sinon from "sinon"
import policyTesterTypeDefs from "./typeDefinitions/policyTesterTypeDefinitions"
import { typeDefs, testPolicyOnDatapoint } from "./policyTester"
describe("policyTester.js", () => {
let log
beforeEach(() => {
log = {
debug: sinon.stub(),
}
})
afterEach(() => {
log.debug.restore()
})
it("Is policy test schema valid", () => {
expect(typeDefs).to.equal(policyTesterTypeDefs)
})
it("returns valid datapoint for policy tester", async () => {
const input = {
policy: {
ingest: {
type: "ingest",
name: "zorro"
}
}
}
expect(await testPolicyOnDatapoint(input, {})).to.equal(true)
})
})
code which I'm trying to cover
import * as log from "../../logger"
import { getGrpcRequestContext } from "../../clients"
import policyTesterTypeDefs from "./typeDefinitions/policyTesterTypeDefinitions"
export const typeDefs = policyTesterTypeDefs
export async function testPolicyOnDatapoint(input, ctx) {
const { type, ...validInput } = input
log.contextLogger(ctx).debug({ validInput }, "Testing policy on datapoint")
const requestContext = getGrpcRequestContext(ctx)
try {
validInput.datapoint = inputDataToScalars(validInput.datapoint, "metadata")
if (validInput.datapoint.event !== null && validInput.datapoint.event !== undefined) {
validInput.datapoint = inputDataToScalars(validInput.datapoint, "dimensions")
}
const response = await ctx.dataSources.policyTesterSvc
.TestPolicyOnDatapoint(validInput, requestContext)
response.datapoint = response.datapoint ?
convertDatapointScalarsToArrays(response.datapoint) :
null
return response
} catch (e) {
log.contextLogger(ctx).warn(e, "Failed to test policy on datapoint")
throw (e)
}
}
code coverage report

Related

Unable to mock a class method in Javascript/Typescript

I am not getting any clue how to mock a method. I have to write a unit test for this function:
index.ts
export async function getTenantExemptionNotes(platform: string) {
return Promise.all([(await getCosmosDbInstance()).getNotes(platform)])
.then(([notes]) => {
return notes;
})
.catch((error) => {
return Promise.reject(error);
});
}
api/CosmosDBAccess.ts
import { Container, CosmosClient, SqlQuerySpec } from "#azure/cosmos";
import { cosmosdbConfig } from "config/Config";
import { Workload } from "config/PlatformConfig";
import { fetchSecret } from "./FetchSecrets";
export class CosmoDbAccess {
private static instance: CosmoDbAccess;
private container: Container;
private constructor(client: CosmosClient) {
this.container = client
.database(cosmosdbConfig.database)
.container(cosmosdbConfig.container);
}
static async getInstance() {
if (!CosmoDbAccess.instance) {
try {
const connectionString = await fetchSecret(
"CosmosDbConnectionString"
);
const client: CosmosClient = new CosmosClient(connectionString);
// Deleting to avoid error: Refused to set unsafe header "user-agent"
delete client["clientContext"].globalEndpointManager.options
.defaultHeaders["User-Agent"];
CosmoDbAccess.instance = new CosmoDbAccess(client);
return CosmoDbAccess.instance;
} catch (error) {
// todo - send to app insights
}
}
return CosmoDbAccess.instance;
}
public async getAllNotesForLastSixMonths() {
const querySpec: SqlQuerySpec = {
// Getting data from past 6 months
query: `SELECT * FROM c
WHERE (udf.convertToDate(c["Date"]) > DateTimeAdd("MM", -6, GetCurrentDateTime()))
AND c.IsArchived != true
ORDER BY c.Date DESC`,
parameters: [],
};
const query = this.container.items.query(querySpec);
const response = await query.fetchAll();
return response.resources;
}
}
export const getCosmosDbInstance = async () => {
const cosmosdb = await CosmoDbAccess.getInstance();
return cosmosdb;
};
index.test.ts
describe("getExemptionNotes()", () => {
beforeEach(() => {
jest.resetAllMocks();
});
it("makes a network call to getKustoResponse which posts to axios and returns what axios returns", async () => {
const mockNotes = [
{
},
];
const cosmosDBInstance = jest
.spyOn(CosmoDbAccess, "getInstance")
.mockReturnValue(Promise.resolve(CosmoDbAccess.instance));
const kustoResponseSpy = jest
.spyOn(CosmoDbAccess.prototype, "getAllNotesForLastSixMonths")
.mockReturnValue(Promise.resolve([mockNotes]));
const actual = await getExemptionNotes();
expect(kustoResponseSpy).toHaveBeenCalledTimes(1);
expect(actual).toEqual(mockNotes);
});
});
I am not able to get instance of CosmosDB or spyOn just the getAllNotesForLastSixMonths method. Please help me code it or give hints. The complexity is because the class is singleton or the methods are static and private

getting error while firebase version 9 FCM implementation in Next 12

I wanted to add push notification on my next js(version 12) app. so implemented firebase Cloud messaging on that. implementation looks like this :
import { initializeApp, getApp, getApps } from "firebase/app"
import { getMessaging, getToken } from "firebase/messaging"
import { firebaseConfig } from "./config"
const app = !getApps.length ? initializeApp(firebaseConfig) : getApp()
and added this cloudMessaging function for getting FCM token. and onMessageListener function for displaying foreground messages
export const cloudMessaging = async () => {
const token = await isTokenAvailable()
if (token !== null) {
return Promise.resolve({
status: true,
token: token,
})
}
try {
const permission = await Notification.requestPermission()
const messaging = getMessaging(app)
console.log(messaging)
console.log(permission)
if (permission === "granted") {
const FCM_TOKEN = await getToken(messaging, {
vapidKey: process.env.NEXT_PUBLIC_FCM_VAPID_KEY,
})
if (FCM_TOKEN) {
localStorage.setItem("fcm_token_prac", FCM_TOKEN)
return Promise.resolve({
status: true,
token: FCM_TOKEN,
})
}
}
} catch (err) {
console.log(err, "cloudmessaging error")
return Promise.resolve({
status: false,
})
}
}
export const onMessageListener = () => {
const messaging = getMessaging(app)
console.log(messaging)
return new Promise((res) => {
messaging.onMessage((payload) => {
res(payload)
})
})
}
And invoking these function from my Layout component
useEffect(() => {
firebaseInit()
async function firebaseInit() {
try {
await cloudMessaging()
} catch (err) {
console.log(err)
}
}
}, [])
useEffect(() => {
onMessageListener()
.then((payload) => {
console.log(payload, "onMessageListener")
})
.catch((err) => console.log(err, "onMessageListener useEffect"))
}, [])
But getting this error in my console :
TypeError: messaging.onMessage is not a function
at eval (firbase.js?100f:58:15)
at new Promise (<anonymous>)
at onMessageListener (firbase.js?100f:57:10)
at eval (Layout.js?4f8d:33:22)
at commitHookEffectListMount (react-dom.development.js?ac89:23049:1)
I'm unable to find out where I made the mistake. Can anyone guide me to implement this?
I think you can try adding onMessage in the import part.
import { getMessaging, getToken, onMessage } from "firebase/messaging"
Remove messaging on line
messaging.onMessage
becomes
onMessage((payload) => {
res(payload)
})
This is for SDK version 9

Stub out node-vault import

How can the 'node-vault' module be stubbed out using Sinon.JS?
Here is the sample code to be tested:
const NodeVault = require('node-vault');
class BearerAuth {
getVaultConfig (token) {
return {
apiVersion: 'v1',
endpoint: app.get('vault'),
token: token
};
}
verify (token) {
const vault = NodeVault(this.getVaultConfig(token));
const resp = vault.tokenLookupSelf().then((result) => {
console.log(`Vault token: ${token}, successfully authenticated.`);
return Promise.resolve({
'name': result.data.meta.username
});
}).catch((err) => {
console.error(err.message);
return Promise.reject(new this.ApplicationError.Authentication('Bearer token is incorrect.'));
});
return resp;
}
}
module.exports = BearerAuth;
}
The test code which attempts to stub out the module using sinon.stub:
const assert = require('assert');
const sinon = require('sinon');
const nodeVault = require('node-vault');
const BearerAuth = require('bearer');
describe('Bearer AuthConfig', () => {
beforeEach(async () => {
class TestBearerAuth extends BearerAuth {
// override some other methods
}
testAuth = new TestBearerAuth();
const vaultConfig = {
endpoint: 'http://localhost:8200',
token: '123'
};
testAuth.getVaultConfig = sinon.stub.returns(vaultConfig);
});
it('returns user when valid token', async () => {
const user = await testAuth.verify(null, 'mytoken');
assert.deepEqual(user, {name: 'sre'});
})
}
This fails when run with this error:
1) Bearer Auth
returns user when valid token:
TypeError: Attempted to wrap undefined property undefined as function
at wrapMethod (node_modules/sinon/lib/sinon/util/core/wrap-method.js:70:21)
at TestBearerAuth.stub [as getVaultConfig] (node_modules/sinon/lib/sinon/stub.js:65:44)
at TestBearerAuthConfig.verify (src/modules/auth/bearer.js:34:34)
at Context.<anonymous> (test/unit/modules/auth/test-bearer.js:57:39)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
The node-vault library has the following export in index.d.ts:
declare function NodeVault(options?: NodeVault.VaultOptions): NodeVault.client;
export = NodeVault;
The unit test code from the node-vault library provided the solution:
Test code:
const assert = require('assert');
const sinon = require('sinon');
const nodeVault = require('node-vault');
const BearerAuth = require('bearer');
describe('Bearer AuthConfig', () => {
beforeEach(async () => {
request = sinon.stub();
response = sinon.stub();
response.statusCode = 200;
response.body = {
data: {
meta: {
username: 'jack'
}
}
};
request.returns({
then (fn) {
return fn(response);
},
catch (fn) {
return fn();
}
});
class TestBearerAuth extends BearerAuth {
getVaultConfig (token) {
return {
endpoint: 'http://localhost:8200',
token: '123',
namespace: 'test',
'request-promise': {
defaults: () => request // dependency injection of stub
}
};
}
}
testAuth = new TestBearerAuth();
});
it('returns user when valid token', async () => {
const user = await testAuth.verify(null, 'mytoken');
assert.deepEqual(user, {name: 'sre'});
})
}
Adding this here in case someone else runs into a similar issue.

How to check test case response using jasemine?

I am writing test case for API where I have passed params and main file method is calling http request to get the data, So core() is basically calling the API and getting the response. This is just backend code that we have to test using jasemine.
First how to see response in test case if that's matched with success that is defined in test.
What is a correct way to write test in below scenario.
balance.spec.ts
import { GetAccountBalance } from './GetAccountBalance.node'
import { Promise } from 'es6-promise'
import { HttpRequest } from '../../../core/http/HttpRequest.node'
import * as sinon from 'sinon'
import { getValidationError } from '../../../common/ValidationErrorFactory'
// for easy mocking and cleanup
const sandbox = sinon.createSandbox()
afterAll(function afterTests () {
sandbox.restore()
})
describe('getAccountBalance', function () {
// the module under test
const module = new GetAccountBalance()
const testURL = 'https://essdd.max.com:4535'
const urlPath = '/webServices/instrument/accountBalance'
const fullURL = testURL + urlPath
const options = { isJSON: true }
let result
const stubbedEC = sandbox.spy(getValidationError)
const stubbedHttp = sandbox.createStubInstance(HttpRequest)
const success = {
header: {
serviceName: 'accountBalance',
statusCode: '0000',
statusDesc: 'SUCCESS',
},
response: {
balanceAccount: '6346.44',
},
}
const params = { Id: '21544', appName: 'instrucn', channelName: 'Web' }
describe('core() is called with correct params', function () {
beforeEach(function () {
result = module.core(params, stubbedHttp)
})
it('it should return the response from server', function () {
// Invoke the unit being tested as necessary
result.then((data) => {
expect(data).toBe(success)
})
})
})
})
getaccountBalnce.ts
public core(args: IAccountBalanceParam, httpRequest: HttpRequestBase): Promise<any> {
console.log('enter core');
const DBPLurl: string = this.constructDBPLurl(args);
const DBPLrequest: IAccountBalanceDBPLParam = this.constructDBPLrequest(args);
return Promise.resolve(httpRequest.makeRequest(DBPLurl, DBPLrequest, {isJSON: true}));
}

How to use mockDOMSource to test a stream of actions in Cycle.js?

I realize there is probably a better way using cycle/time, but I'm just trying to understand the basics. Somehow, my action$ stream doesn't seem to be running; I've tried to construct multiple mock doms using xs.periodic. The test framework is mocha.
import 'mocha';
import {expect} from 'chai';
import xs from 'xstream';
import Stream from 'xstream';
import {mockDOMSource, DOMSource} from '#cycle/dom';
import {HTTPSource} from '#cycle/http';
import XStreamAdapter from '#cycle/xstream-adapter';
export interface Props {
displayAbs: boolean
}
export interface ISources {
DOM: DOMSource;
http: HTTPSource;
}
function testIntent(sources: ISources):Stream<Props> {
return xs.merge<Props>(
sources.DOM
.select('.absShow').events('click')
.mapTo( { displayAbs: true } ),
sources.DOM
.select('.absHide').events('click')
.mapTo( { displayAbs: false } )
).startWith( {displayAbs: false } );
}
describe( 'Test', ()=>{
describe( 'intent()', ()=>{
it('should change on click to shows and hides', () => {
let listenerGotEnd = false;
const mDOM$: Stream<DOMSource> = xs.periodic(1000).take(6).map(ii => {
if (ii % 2 == 0) {
return mockDOMSource(XStreamAdapter, {
'.absShow': {'click': xs.of({target: {}})}
})
}
else {
return mockDOMSource(XStreamAdapter, {
'.absHide': {'click': xs.of({target: {}})}
})
}
});
const action$ = mDOM$.map(mDOM => testIntent({
DOM: mDOM,
http: {} as HTTPSource,
})).flatten();
action$.addListener({
next: (x) => {
console.log("x is " + x.displayAbs);
},
error: (err) => {
console.log("error is:" + err);
throw err;
},
complete: () => { listenerGotEnd = true; }
});
expect(listenerGotEnd).to.equal(true);
});
});/* end of describe intent */
});
The primary reason the test is not running is because it's asynchronous, so in mocha we need to take in the done callback and then call it when our test is done.
Without using #cycle/time, this is how I would write this test:
import 'mocha';
import {expect} from 'chai';
import xs, {Stream} from 'xstream';
import {mockDOMSource, DOMSource} from '#cycle/dom';
import XStreamAdapter from '#cycle/xstream-adapter';
export interface Props {
displayAbs: boolean
}
export interface ISources {
DOM: DOMSource;
}
function testIntent(sources: ISources):Stream<Props> {
return xs.merge<Props>(
sources.DOM
.select('.absShow').events('click')
.mapTo( { displayAbs: true } ),
sources.DOM
.select('.absHide').events('click')
.mapTo( { displayAbs: false } )
).startWith( {displayAbs: false } );
}
describe('Test', () => {
describe('intent()', () => {
it('should change on click to shows and hides', (done) => {
const show$ = xs.create();
const hide$ = xs.create();
const DOM = mockDOMSource(XStreamAdapter, {
'.absShow': {
'click': show$
},
'.absHide': {
'click': hide$
}
});
const intent$ = testIntent({DOM});
const expectedValues = [
{displayAbs: false},
{displayAbs: true},
{displayAbs: false},
]
intent$.take(expectedValues.length).addListener({
next: (x) => {
expect(x).to.deep.equal(expectedValues.shift());
},
error: done,
complete: done
});
show$.shamefullySendNext({});
hide$.shamefullySendNext({});
});
});
});
This test runs in 11ms, which is a fair bit faster than using xs.periodic(1000).take(6)
For comparison, here is how I would write it with #cycle/time:
import {mockTimeSource} from '#cycle/time'
describe('Test', () => {
describe('intent()', () => {
it('should change on click to shows and hides', (done) => {
const Time = mockTimeSource();
const show$ = Time.diagram('---x-----');
const hide$ = Time.diagram('------x--');
const expected$ = Time.diagram('f--t--f--', {f: false, t: true});
const DOM = mockDOMSource({
'.absShow': {
'click': show$
},
'.absHide': {
'click': hide$
}
});
const intent$ = testIntent({DOM}).map(intent => intent.displayAbs);
Time.assertEqual(intent$, expected$);
Time.run(done);
});
});
});
The first version is effectively what #cycle/time is doing for you under the hood, this is just a slightly nicer way of writing it. It's also nice to have better error messages.

Categories

Resources