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

Add tests for job actions #1644

Open
wants to merge 10 commits into
base: release-jobs
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
5 changes: 5 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@ jobs:
CLUSTER_NAME: es-cluster
MEM_LIMIT: 4G
JOB_CONFIGURATION_FILE: test/config/jobconfig.yaml
RABBITMQ_ENABLED: "yes"
RABBITMQ_HOSTNAME: "rabbitmq"
RABBITMQ_PORT: 5672
RABBITMQ_USERNAME: "guest"
RABBITMQ_PASSWORD: "guest"

# Start mongo container and app before running api tests
run: |
Expand Down
1 change: 1 addition & 0 deletions src/common/email-templates/job-template.spec.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Your {{type}} job with ID {{id}} has been completed successfully.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const actionType = "email";
export interface EmailJobActionOptions extends JobActionOptions {
actionType: typeof actionType;
to: string;
from: string;
from?: string;
subject: string;
bodyTemplateFile: string;
}
Expand All @@ -24,7 +24,7 @@ export function isEmailJobActionOptions(
return (
opts.actionType === actionType &&
typeof opts.to === "string" &&
typeof opts.from === "string" &&
(opts.from === undefined || typeof opts.from === "string") &&
typeof opts.subject === "string" &&
typeof opts.bodyTemplateFile === "string"
);
Expand Down
116 changes: 116 additions & 0 deletions src/config/job-config/actions/emailaction/emailaction.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { EmailJobAction } from "./emailaction";
import { EmailJobActionOptions } from "./emailaction.interface";
import { JobClass } from "../../../../jobs/schemas/job.schema";
import { MailService } from "src/common/mail.service";
import { MailerService } from "@nestjs-modules/mailer";

jest.mock("src/common/mail.service");
jest.mock("@nestjs-modules/mailer");

describe("EmailJobAction", () => {
const config: EmailJobActionOptions = {
actionType: "email",
to: "[email protected]",
from: "[email protected]",
subject: "Job {{id}} completed",
bodyTemplateFile: "src/common/email-templates/job-template.spec.html",
};

const mailService = {
sendMail: jest.fn(),
} as unknown as MailService;

const action = new EmailJobAction(mailService, config);

beforeEach(() => {
jest.clearAllMocks();
});

it("should be configured successfully", async () => {
expect(action).toBeDefined();
});

it("should send an email successfully", async () => {
const job = {
id: "12345",
type: "testemail",
} as JobClass;

await action.performJob(job);

expect(mailService.sendMail).toHaveBeenCalledWith({
to: "[email protected]",
from: "[email protected]",
subject: "Job 12345 completed",
html: "Your testemail job with ID 12345 has been completed successfully.",
});
});

it("should throw an error if email sending fails", async () => {
const job = {
id: "12345",
type: "testemail",
} as JobClass;

(mailService.sendMail as jest.Mock).mockRejectedValue(
new Error("Email sending failed"),
);

await expect(action.performJob(job)).rejects.toThrow(
"Email sending failed",
);
});
});

describe("EmailJobAction with default sender", () => {
const config: EmailJobActionOptions = {
actionType: "email",
to: "[email protected]",
subject: "Job {{id}} completed",
bodyTemplateFile: "src/common/email-templates/job-template.spec.html",
};

const mailService = {
sendMail: jest.fn(),
} as unknown as MailService;

const mailerService = {
sendMail: jest.fn(),
} as unknown as MailerService;

const action = new EmailJobAction(mailService, config);

beforeEach(() => {
jest.clearAllMocks();
});

it("should be configured successfully", async () => {
expect(action).toBeDefined();
});

it("should send an email with default sender successfully", async () => {
const job = {
id: "12345",
type: "testemail",
} as JobClass;

const defaultFrom = "[email protected]";
(mailerService.sendMail as jest.Mock).mockImplementation((mailOptions) => {
mailOptions.from = defaultFrom;
return Promise.resolve();
});

(mailService.sendMail as jest.Mock).mockImplementation((mailOptions) => {
return mailerService.sendMail(mailOptions);
});

await action.performJob(job);

expect(mailerService.sendMail).toHaveBeenCalledWith({
to: "[email protected]",
from: defaultFrom,
subject: "Job 12345 completed",
html: "Your testemail job with ID 12345 has been completed successfully.",
});
});
});
7 changes: 4 additions & 3 deletions src/config/job-config/actions/emailaction/emailaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { MailService } from "src/common/mail.service";
*/
export class EmailJobAction implements JobAction<JobDto> {
private toTemplate: TemplateJob;
private from: string;
private from?: string = undefined;
private subjectTemplate: TemplateJob;
private bodyTemplate: TemplateJob;

Expand All @@ -36,7 +36,9 @@ export class EmailJobAction implements JobAction<JobDto> {

Logger.log("EmailJobAction parameters are valid.", "EmailJobAction");

this.from = options.from as string;
if (options["from"]) {
this.from = options.from as string;
}
this.toTemplate = compileJob(options.to);
this.subjectTemplate = compileJob(options.subject);

Expand All @@ -60,7 +62,6 @@ export class EmailJobAction implements JobAction<JobDto> {
subject: this.subjectTemplate(job),
html: this.bodyTemplate(job),
};
Logger.log(mail);

// Send the email
await this.mailService.sendMail(mail);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { RabbitMQJobAction } from "./rabbitmqaction";
import { RabbitMQJobActionOptions } from "./rabbitmqaction.interface";
import { CreateJobDto } from "../../../../jobs/dto/create-job.dto";
import { JobClass } from "../../../../jobs/schemas/job.schema";
import { RabbitMQService } from "src/common/rabbitmq/rabbitmq.service";
import { ConfigService } from "@nestjs/config";

jest.mock("@nestjs/config");

describe("RabbitMQJobAction", () => {
const options: RabbitMQJobActionOptions = {
actionType: "rabbitmq",
queue: "testQueue",
exchange: "testExchange",
key: "testKey",
};

const mockConfigService = {
get: jest.fn((key: string) => {
switch (key) {
case "rabbitMq.enabled":
return "yes";
case "rabbitMq.hostname":
return "rabbitmq";
case "rabbitMq.port":
return 5672;
case "rabbitMq.username":
return "guest";
case "rabbitMq.password":
return "guest";
default:
return null;
}
}),
};

const mockRabbitMQService = {
sendMessage: jest.fn(),
};

const action = new RabbitMQJobAction<CreateJobDto>(
mockConfigService as unknown as ConfigService,
mockRabbitMQService as unknown as RabbitMQService,
options,
);

it("should be configured successfully", async () => {
expect(action).toBeDefined();
});

it("should connect and send a message to the queue", async () => {
const job = { id: "12345" } as JobClass;

await action.performJob(job);

expect(mockRabbitMQService.sendMessage).toHaveBeenCalledWith(
options.queue,
options.exchange,
options.key,
JSON.stringify(job),
);
});
});
70 changes: 70 additions & 0 deletions src/config/job-config/actions/urlaction/urlaction.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { URLJobAction } from "./urlaction";
import { URLJobActionOptions } from "./urlaction.interface";
import { CreateJobDto } from "../../../../jobs/dto/create-job.dto";
import { JobClass } from "../../../../jobs/schemas/job.schema";

describe("URLJobAction", () => {
const config: URLJobActionOptions = {
actionType: "url",
url: "http://localhost:3000/api/v3/health?jobid={{id}}",
method: "GET",
headers: { accept: "application/json" },
body: "This is the body.",
};

const action = new URLJobAction<CreateJobDto>(config);

beforeEach(() => {
global.fetch = jest.fn();
});

afterEach(() => {
jest.resetAllMocks();
});

it("should be configured successfully", async () => {
expect(action).toBeDefined();
});

it("should perform a GET request successfully", async () => {
const job = { id: "12345" } as JobClass;
(global.fetch as jest.Mock).mockResolvedValue({
ok: true,
status: 200,
text: jest.fn().mockResolvedValue("OK"),
});

await action.performJob(job);

expect(global.fetch).toHaveBeenCalledWith(
"http://localhost:3000/api/v3/health?jobid=12345",
{
method: "GET",
headers: { accept: "application/json" },
body: "This is the body.",
},
);
});

it("should throw an error if the response is not ok", async () => {
const job = { id: "12345" } as JobClass;
(global.fetch as jest.Mock).mockResolvedValue({
ok: false,
status: 500,
text: jest.fn().mockResolvedValue("Internal Server Error"),
});

await expect(action.performJob(job)).rejects.toThrow(
"Got response: Internal Server Error",
);

expect(global.fetch).toHaveBeenCalledWith(
"http://localhost:3000/api/v3/health?jobid=12345",
{
method: "GET",
headers: { accept: "application/json" },
body: "This is the body.",
},
);
});
});
4 changes: 4 additions & 0 deletions src/config/job-config/jobconfig.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { JobConfigService } from "./jobconfig.service";
import { Test } from "@nestjs/testing";
import { actionType as logActionType } from "./actions/logaction/logaction.interface";
import { actionType as validateActionType } from "./actions/validateaction/validateaction.interface";
import { actionType as urlActionType } from "./actions/urlaction/urlaction.interface";
import { actionType as rabbitmqActionType } from "./actions/rabbitmqaction/rabbitmqaction.interface";
import {
CREATE_JOB_ACTION_CREATORS,
UPDATE_JOB_ACTION_CREATORS,
Expand All @@ -15,6 +17,8 @@ const actionCreatorMock = {
const creatorsMock = {
[logActionType]: actionCreatorMock,
[validateActionType]: actionCreatorMock,
[urlActionType]: actionCreatorMock,
[rabbitmqActionType]: actionCreatorMock,
};
const creatorProviders = [
{
Expand Down
30 changes: 27 additions & 3 deletions src/jobs/jobs.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@ import { OrigDatablocksService } from "src/origdatablocks/origdatablocks.service
import { JobsController } from "./jobs.controller";
import { JobsService } from "./jobs.service";
import { UsersService } from "src/users/users.service";
import { RabbitMQService } from "src/common/rabbitmq/rabbitmq.service";
import { MailerModule, MailerService } from "@nestjs-modules/mailer";

import { JobConfigService } from "src/config/job-config/jobconfig.service";
import { ConfigModule } from "@nestjs/config";
import { ConfigModule, ConfigService } from "@nestjs/config";

class JobsServiceMock {}
class DatasetsServiceMock {}
class OrigDatablocksServiceMock {}
class UsersServiceMock {}
class MailerServiceMock {}
class JobsConfigMock {}
class RabbitMQMock {}

describe("JobsController", () => {
let controller: JobsController;
Expand All @@ -27,7 +28,18 @@ describe("JobsController", () => {
controllers: [JobsController],
imports: [
ConfigModule.forRoot({
load: [() => ({ jobConfigurationFile: path })],
load: [
() => ({
jobConfigurationFile: path,
rabbitMq: {
enabled: "yes",
hostname: "rabbitmq",
port: 5672,
username: "guest",
password: "guest",
},
}),
],
}),
MailerModule.forRoot(),
CaslModule,
Expand All @@ -39,6 +51,18 @@ describe("JobsController", () => {
{ provide: OrigDatablocksService, useClass: OrigDatablocksServiceMock },
{ provide: UsersService, useClass: UsersServiceMock },
{ provide: EventEmitter2, useClass: EventEmitter2 },
{ provide: RabbitMQService, useClass: RabbitMQMock },
{
provide: ConfigService,
useValue: {
get: jest.fn((key: string) => {
if (key === "rabbitMq.enabled") {
return true;
}
return null;
}),
},
},
],
})
.overrideProvider(MailerService)
Expand Down
Loading
Loading