Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(mock): NewContext returns a promise #91

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 71 additions & 57 deletions mock/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as protoLoader from "@grpc/proto-loader";
import path, { resolve } from "path";
import { ProtoGrpcType } from "../proto/services";
import Mode, { MODE_TEST } from "../src/mode";
import { createExecutionContext, getExecutionContext } from "../src/context";
import { createExecutionContext } from "../src/context";
import { startRecordingMocks } from "./utils";

const PORT = 6789;
Expand All @@ -26,12 +26,26 @@ export interface Config {
Path: string;
Mode: string;
}
export function NewContext(conf: Config) {

// NewContext is used to populate the context to mock/capture the dependency calls.
// Since, grpc unary calls are made, Promise is returned to sync the outputs
// before the external dependency calls from unit tests.
export function NewContext(conf: Config): Promise<string> {
const mode = new Mode();
let path = conf !== undefined && conf.Path !== undefined ? conf.Path : "";
// default mode: TEST
// default mode should be TEST
mode.SetMode(MODE_TEST);
if (
process.env.KEPLOY_MODE !== undefined &&
Mode.Valid(process.env.KEPLOY_MODE)
) {
mode.SetMode(process.env.KEPLOY_MODE);
}
// mode mostly dependent on conf.Mode
if (Mode.Valid(conf.Mode)) {
mode.SetMode(conf.Mode);
}

let path = conf !== undefined && conf.Path !== undefined ? conf.Path : "";
if (path === "") {
try {
path = process.cwd();
Expand All @@ -50,53 +64,6 @@ export function NewContext(conf: Config) {
path += "/mocks";
mockPath = path;

if (
process.env.KEPLOY_MODE !== undefined &&
Mode.Valid(process.env.KEPLOY_MODE)
) {
// if (process.)
mode.SetMode(process.env.KEPLOY_MODE);
}
// mode mostly dependent on conf.Mode
if (Mode.Valid(conf.Mode)) {
mode.SetMode(conf.Mode);
}
switch (mode.GetMode()) {
case "test":
if (conf.Name === "") {
console.log(
"🚨 Please enter the auto generated name to mock the dependencies using Keploy."
);
}
createExecutionContext({
mode: mode.GetMode(),
testId: conf.Name,
mocks: [],
fileExport: true,
});
const ctx = getExecutionContext().context;
grpcClient.GetMocks({ Path: path, Name: conf.Name }, (err, response) => {
if (err) {
console.error(err);
return;
}
ctx.mocks = response?.Mocks;
return response;
});
break;
case "record":
createExecutionContext({
mode: mode.GetMode(),
testId: conf.Name,
mocks: [],
fileExport: true,
});
break;
default:
console.log("Keploy mode: (", mode.GetMode(), ") is not a valid mode");
break;
}

let name = "";
if (conf.Name !== "") {
name = "for " + conf.Name;
Expand All @@ -108,10 +75,57 @@ export function NewContext(conf: Config) {
name,
".\n If you dont see any logs about your dependencies below, your dependency/s are NOT wrapped.\n"
);
startRecordingMocks(
path + "/" + conf.Name + ".yaml",
mode.GetMode(),
name,
conf.Name
);
const ctx: { mode: string; testId: string; fileExport: boolean; mocks: any } =
{
mode: mode.GetMode(),
testId: conf.Name,
fileExport: true,
mocks: [],
};
switch (mode.GetMode()) {
case "test":
if (conf.Name === "") {
console.log(
"🚨 Please enter the auto generated name to mock the dependencies using Keploy."
);
}
createExecutionContext(ctx);
case "record":
createExecutionContext(ctx);
}

// returns Promise to sync the outputs from the grpc unary calls
return new Promise((rsolve, reject) => {
switch (mode.GetMode()) {
case "test":
grpcClient.GetMocks(
{ Path: path, Name: conf.Name },
(err, response) => {
if (err) {
console.error(err);
reject(err);
return;
}
ctx.mocks = response?.Mocks;
rsolve("passed");
return response;
}
);
break;
case "record":
startRecordingMocks(
path + "/" + conf.Name + ".yaml",
mode.GetMode(),
name,
conf.Name,
rsolve,
reject
);
break;
default:
console.log("Keploy mode: (", mode.GetMode(), ") is not a valid mode");
reject(`Keploy mode: (${mode.GetMode()}) is not a valid mode`);
break;
}
});
}
7 changes: 5 additions & 2 deletions mock/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { response } from "express";
import { Mock } from "../proto/services/Mock";
import { grpcClient, MockIds, mockPath } from "./mock";

Expand All @@ -20,7 +19,9 @@ export function startRecordingMocks(
path: string,
mode: string,
name: string,
mockId: string
mockId: string,
resolve: (value: string | PromiseLike<string>) => void,
reject: (reason?: string) => void
) {
grpcClient.StartMocking(
{
Expand All @@ -30,6 +31,7 @@ export function startRecordingMocks(
function (err, response) {
if (err !== null) {
console.error("failed to start mocking due to error: ", err);
reject(`failed to start mocking due to error: ${err}`);
return;
}
if (response?.Exists) {
Expand All @@ -42,6 +44,7 @@ export function startRecordingMocks(
);
MockIds[mockId] = true;
}
resolve("Passed");
}
);
}
6 changes: 2 additions & 4 deletions src/context.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-explicit-any */
import asyncHooks from "async_hooks";

const executionContextMap = new Map();

const asyncHook = asyncHooks.createHook({ init, destroy });
asyncHook.enable();

function init(asyncId: any, type: any, triggerAsyncId: any) {
function init(asyncId: number, type: string, triggerAsyncId: number) {
const parentContext = executionContextMap.get(triggerAsyncId);
if (!parentContext) return;

executionContextMap.set(asyncId, { context: parentContext.context });
}

function destroy(asyncId: any) {
function destroy(asyncId: number) {
executionContextMap.delete(asyncId);
}

Expand Down