Skip to content

Commit 5ae0d34

Browse files
feat(financial): implement financial report formatting and enhance project unit retrieval
- Added `FinancialReportFormatter` to format weekly financial reports based on project units. - Introduced tests for `formatFinancialReport` to ensure correct output structure and content. - Updated `weeklyFinancialReportsWorkflow` to utilize the new formatter, improving code clarity and maintainability. - Created a new test file for `RedmineRepository` to validate the behavior of `getProjectUnits` and ensure accurate data retrieval. These changes enhance the financial reporting capabilities and improve the overall structure of the financial module.
1 parent 7329ae1 commit 5ae0d34

File tree

5 files changed

+109
-3
lines changed

5 files changed

+109
-3
lines changed

workers/main/src/repositories/financial/IProjectUnitRepository.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { ProjectUnit } from '../../../common/types';
1+
import { ProjectUnit } from '../../common/types';
22

33
export interface IProjectUnitRepository {
44
getProjectUnits(): Promise<ProjectUnit[]>;
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import type { Pool } from 'mysql2/promise';
2+
import { beforeEach, describe, expect, it, vi } from 'vitest';
3+
4+
import { RedminePool } from '../../common/RedminePool';
5+
import { RedmineRepository } from './RedmineRepository';
6+
7+
// Mock RedminePool and Pool
8+
const mockExecute = vi.fn();
9+
const mockPool: Partial<Pool> = {
10+
execute: mockExecute,
11+
};
12+
13+
const mockRedminePool: Partial<RedminePool> = {
14+
getPool: () => mockPool as Pool,
15+
};
16+
17+
describe('RedmineRepository', () => {
18+
let repo: RedmineRepository;
19+
20+
beforeEach(() => {
21+
vi.clearAllMocks();
22+
repo = new RedmineRepository(mockRedminePool as RedminePool);
23+
});
24+
25+
it('getProjectUnitsQuery returns correct SQL', () => {
26+
// @ts-expect-error: access private method for test
27+
const query = repo.getProjectUnitsQuery();
28+
29+
expect(query).toContain('SELECT');
30+
expect(query).toContain('group_id');
31+
expect(query).toContain('SUM(total_hours) AS total_hours');
32+
expect(query).toContain('ORDER BY group_name ASC');
33+
});
34+
35+
it('getProjectUnits returns rows from pool', async () => {
36+
const mockRows = [
37+
{ group_id: 1, group_name: 'A', project_id: 2, project_name: 'B' },
38+
];
39+
40+
mockExecute.mockResolvedValueOnce([mockRows]);
41+
const result = await repo.getProjectUnits();
42+
43+
expect(result).toEqual(mockRows);
44+
expect(mockExecute).toHaveBeenCalled();
45+
});
46+
47+
it('getProjectUnits returns empty array if no rows', async () => {
48+
mockExecute.mockResolvedValueOnce([[]]);
49+
const result = await repo.getProjectUnits();
50+
51+
expect(result).toEqual([]);
52+
});
53+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { ProjectUnit } from '../../common/types';
2+
3+
export function formatFinancialReport(units: ProjectUnit[]) {
4+
const reportTitle = 'Weekly Financial Report';
5+
6+
return `${reportTitle}\n${JSON.stringify(units, null, 2)}`;
7+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { vi } from 'vitest';
3+
4+
import { formatFinancialReport } from './FinancialReportFormatter';
5+
6+
describe('formatFinancialReport', () => {
7+
it('should format report with given project units', () => {
8+
const result = formatFinancialReport([
9+
{
10+
group_id: 1,
11+
group_name: 'Engineering',
12+
project_id: 101,
13+
project_name: 'Project Alpha',
14+
},
15+
]);
16+
17+
expect(result).toContain('Weekly Financial Report');
18+
expect(result).toContain('Engineering');
19+
expect(result).toContain('Project Alpha');
20+
});
21+
});
22+
23+
vi.mock('@temporalio/workflow', () => ({
24+
proxyActivities: () => ({
25+
getProjectUnits: vi.fn().mockResolvedValue([
26+
{
27+
group_id: 1,
28+
group_name: 'Engineering',
29+
project_id: 101,
30+
project_name: 'Project Alpha',
31+
},
32+
]),
33+
}),
34+
}));
35+
36+
import { weeklyFinancialReportsWorkflow } from './weeklyFinancialReports.workflow';
37+
38+
describe('weeklyFinancialReportsWorkflow', () => {
39+
it('should return formatted report from workflow', async () => {
40+
const result = await weeklyFinancialReportsWorkflow();
41+
42+
expect(result).toContain('Weekly Financial Report');
43+
expect(result).toContain('Engineering');
44+
expect(result).toContain('Project Alpha');
45+
});
46+
});
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { proxyActivities } from '@temporalio/workflow';
22

33
import type * as activities from '../../activities/financial';
4+
import { formatFinancialReport } from './FinancialReportFormatter';
45

56
const { getProjectUnits } = proxyActivities<typeof activities>({
67
startToCloseTimeout: '10 minutes',
78
});
89

910
export async function weeklyFinancialReportsWorkflow(): Promise<string> {
10-
const reportTitle = 'Weekly Financial Report';
1111
const projectUnits = await getProjectUnits();
1212

13-
return `${reportTitle}\n${JSON.stringify(projectUnits, null, 2)}`;
13+
return formatFinancialReport(projectUnits);
1414
}

0 commit comments

Comments
 (0)