Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { afterEach, beforeEach, describe, expect, it, Mock, vi } from 'vitest';
import { AppError } from '../../common/errors';
import { writeJsonFile } from '../../common/fileUtils';
import { RedminePool } from '../../common/RedminePool';
import { GroupNameEnum } from '../../configs/weeklyFinancialReport';
import { getTargetUnits } from './getTargetUnits';

type TargetUnit = {
Expand Down Expand Up @@ -114,10 +113,10 @@ describe('getTargetUnits', () => {
}));
};

it('returns fileLink when successful (default group)', async () => {
it('returns fileLink when successful', async () => {
mockRepo(true);
writeJsonFileMock.mockResolvedValue(undefined);
const result = await getTargetUnits(GroupNameEnum.SD_REPORT);
const result = await getTargetUnits();

expect(result).toEqual({ fileLink: mockFile });
expect(writeJsonFile).toHaveBeenCalledWith(mockFile, mockUnits);
Expand All @@ -126,29 +125,25 @@ describe('getTargetUnits', () => {
it('throws AppError when repo.getTargetUnits throws', async () => {
mockRepo(false);
writeJsonFileMock.mockResolvedValue(undefined);
await expect(getTargetUnits(GroupNameEnum.SD_REPORT)).rejects.toThrow(
AppError,
);
await expect(getTargetUnits(GroupNameEnum.SD_REPORT)).rejects.toThrow(
await expect(getTargetUnits()).rejects.toThrow(AppError);
await expect(getTargetUnits()).rejects.toThrow(
'Failed to get Target Units',
);
});

it('throws AppError when writeJsonFile throws', async () => {
mockRepo(true);
writeJsonFileMock.mockRejectedValue(new Error('fail-write'));
await expect(getTargetUnits(GroupNameEnum.SD_REPORT)).rejects.toThrow(
AppError,
);
await expect(getTargetUnits(GroupNameEnum.SD_REPORT)).rejects.toThrow(
await expect(getTargetUnits()).rejects.toThrow(AppError);
await expect(getTargetUnits()).rejects.toThrow(
'Failed to get Target Units',
);
});

it('always ends the Redmine pool', async () => {
mockRepo(true);
writeJsonFileMock.mockResolvedValue(undefined);
await getTargetUnits(GroupNameEnum.SD_REPORT);
await getTargetUnits();
expect(endPool).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
import { AppError } from '../../common/errors';
import { writeJsonFile } from '../../common/fileUtils';
import { RedminePool } from '../../common/RedminePool';
import { GroupName } from '../../common/types';
import { redmineDatabaseConfig } from '../../configs/redmineDatabase';
import { TargetUnitRepository } from '../../services/TargetUnit/TargetUnitRepository';

interface GetTargetUnitsResult {
fileLink: string;
}

export const getTargetUnits = async (
groupName: GroupName,
): Promise<GetTargetUnitsResult> => {
export const getTargetUnits = async (): Promise<GetTargetUnitsResult> => {
const redminePool = new RedminePool(redmineDatabaseConfig);

try {
const pool = redminePool.getPool();

const repo = new TargetUnitRepository(pool);
const result = await repo.getTargetUnits(groupName);
const result = await repo.getTargetUnits();
const filename = `data/weeklyFinancialReportsWorkflow/getTargetUnits/target-units-${Date.now()}.json`;

await writeJsonFile(filename, result);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { GroupName, TargetUnit } from '../../common/types';
import { TargetUnit } from '../../common/types';

export interface ITargetUnitRepository {
getTargetUnits(groupName: GroupName): Promise<TargetUnit[]>;
getTargetUnits(): Promise<TargetUnit[]>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { afterEach, beforeEach, describe, expect, it, Mock, vi } from 'vitest';

import { TargetUnitRepositoryError } from '../../common/errors';
import { TargetUnit } from '../../common/types';
import { GroupNameEnum } from '../../configs/weeklyFinancialReport';
import { TargetUnitRepository } from './TargetUnitRepository';
import { TargetUnitRow } from './types';

Expand Down Expand Up @@ -38,7 +37,7 @@ describe('TargetUnitRepository', () => {
];

mockPool.query.mockResolvedValueOnce([rows]);
const result = await repo.getTargetUnits(GroupNameEnum.SD_REPORT);
const result = await repo.getTargetUnits();

expect(result).toEqual<Partial<TargetUnit>[]>([
{
Expand All @@ -56,15 +55,13 @@ describe('TargetUnitRepository', () => {

it('throws TargetUnitRepositoryError if query does not return array', async () => {
mockPool.query.mockResolvedValueOnce([null]);
await expect(repo.getTargetUnits(GroupNameEnum.SD_REPORT)).rejects.toThrow(
await expect(repo.getTargetUnits()).rejects.toThrow(
TargetUnitRepositoryError,
);
});

it('throws TargetUnitRepositoryError on query error', async () => {
mockPool.query.mockRejectedValueOnce(new Error('db error'));
await expect(
repo.getTargetUnits(GroupNameEnum.SD_REPORT),
).rejects.toThrowError('db error');
await expect(repo.getTargetUnits()).rejects.toThrowError('db error');
});
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Pool } from 'mysql2/promise';

import { TargetUnitRepositoryError } from '../../common/errors';
import { GroupName, TargetUnit } from '../../common/types';
import { TargetUnit } from '../../common/types';
import { ITargetUnitRepository } from './ITargetUnitRepository';
import { TARGET_UNITS_QUERY } from './queries';
import { TargetUnitRow } from './types';
Expand Down Expand Up @@ -33,12 +33,9 @@ export class TargetUnitRepository implements ITargetUnitRepository {
total_hours: Number(total_hours),
});

async getTargetUnits(groupName: GroupName): Promise<TargetUnit[]> {
async getTargetUnits(): Promise<TargetUnit[]> {
try {
const [rows] = await this.pool.query<TargetUnitRow[]>(
TARGET_UNITS_QUERY,
[groupName],
);
const [rows] = await this.pool.query<TargetUnitRow[]>(TARGET_UNITS_QUERY);

if (!Array.isArray(rows)) {
throw new TargetUnitRepositoryError('Query did not return an array');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import * as workflowModule from '@temporalio/workflow';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { AppError } from '../../common/errors';
import { GroupNameEnum } from '../../configs/weeklyFinancialReport';
import { weeklyFinancialReportsWorkflow } from './weeklyFinancialReports.workflow';

vi.mock('@temporalio/workflow', () => {
Expand Down Expand Up @@ -44,35 +42,19 @@ describe('weeklyFinancialReportsWorkflow', () => {
sendReportToSlackMock.mockReset();
});

it('throws AppError for invalid group name', async () => {
const allowedValues = Object.values(GroupNameEnum).join('", "');
const expectedMessage = `Invalid groupName parameter: INVALID_GROUP. Allowed values: "${allowedValues}"`;

await expect(
weeklyFinancialReportsWorkflow(
'INVALID_GROUP' as unknown as GroupNameEnum,
),
).rejects.toThrow(AppError);
await expect(
weeklyFinancialReportsWorkflow(
'INVALID_GROUP' as unknown as GroupNameEnum,
),
).rejects.toThrow(expectedMessage);
});

it('propagates error from getTargetUnits', async () => {
getTargetUnitsMock.mockRejectedValueOnce(new Error('activity error'));
await expect(
weeklyFinancialReportsWorkflow(GroupNameEnum.SD_REPORT),
).rejects.toThrow('activity error');
await expect(weeklyFinancialReportsWorkflow()).rejects.toThrow(
'activity error',
);
});

it('propagates error from fetchFinancialAppData', async () => {
getTargetUnitsMock.mockResolvedValueOnce({ fileLink: 'file.json' });
fetchFinancialAppDataMock.mockRejectedValueOnce(new Error('fetch error'));
await expect(
weeklyFinancialReportsWorkflow(GroupNameEnum.SD_REPORT),
).rejects.toThrow('fetch error');
await expect(weeklyFinancialReportsWorkflow()).rejects.toThrow(
'fetch error',
);
});

it('returns fileLink on success', async () => {
Expand All @@ -81,12 +63,10 @@ describe('weeklyFinancialReportsWorkflow', () => {
fileLink: 'result.json',
});
sendReportToSlackMock.mockResolvedValueOnce('slack-link.json');
const result = await weeklyFinancialReportsWorkflow(
GroupNameEnum.SD_REPORT,
);
const result = await weeklyFinancialReportsWorkflow();

expect(result).toBe('slack-link.json');
expect(getTargetUnitsMock).toHaveBeenCalledWith(GroupNameEnum.SD_REPORT);
expect(getTargetUnitsMock).toHaveBeenCalledWith();
expect(fetchFinancialAppDataMock).toHaveBeenCalledWith('file.json');
expect(sendReportToSlackMock).toHaveBeenCalledWith(
'file.json',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,14 @@
import { proxyActivities } from '@temporalio/workflow';

import type * as activities from '../../activities/weeklyFinancialReports';
import { AppError } from '../../common/errors';
import { GroupName } from '../../common/types';
import { GroupNameEnum } from '../../configs/weeklyFinancialReport';

const { getTargetUnits, fetchFinancialAppData, sendReportToSlack } =
proxyActivities<typeof activities>({
startToCloseTimeout: '10 minutes',
});

export async function weeklyFinancialReportsWorkflow(
groupName: GroupName,
): Promise<string> {
if (!(Object.values(GroupNameEnum) as GroupName[]).includes(groupName)) {
throw new AppError(
`Invalid groupName parameter: ${groupName}. Allowed values: "${Object.values(GroupNameEnum).join('", "')}"`,
'weeklyFinancialReportsWorkflow',
);
}
const targetUnits = await getTargetUnits(groupName);
export async function weeklyFinancialReportsWorkflow(): Promise<string> {
const targetUnits = await getTargetUnits();
const finData = await fetchFinancialAppData(targetUnits.fileLink);

return await sendReportToSlack(targetUnits.fileLink, finData.fileLink);
Expand Down