Skip to content

Commit 0992d75

Browse files
committed
fixed feature for storing audit data
1 parent c584bb7 commit 0992d75

23 files changed

+456
-352
lines changed

packages/application/src/application/publishers/refresh-audit-publisher.test.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import { GithubClient } from '@src/infrastructure/clients/github';
77
import { TYPES } from '@src/types';
88
import { GithubConfig } from '@src/domain/types';
99
import { ICommandBus } from '@filecoin-plus/core/src/interfaces/ICommandBus';
10-
import { AuditMapper, IAuditMapper } from '@src/infrastructure/mappers/audit-mapper';
10+
import { IAuditMapper } from '@src/infrastructure/mappers/audit-mapper';
11+
import { Logger } from '@filecoin-plus/core';
1112

1213
describe('RefreshAuditPublisher', () => {
1314
let container: Container;
@@ -37,6 +38,7 @@ describe('RefreshAuditPublisher', () => {
3738
datacap_amount: a.datacapAmount as number,
3839
})),
3940
};
41+
const loggerMock = { info: vi.fn(), error: vi.fn() };
4042

4143
const baseAllocator = {
4244
application_number: 123,
@@ -66,6 +68,7 @@ describe('RefreshAuditPublisher', () => {
6668
container
6769
.bind<IAuditMapper>(TYPES.AuditMapper)
6870
.toConstantValue(auditMapperMock as unknown as IAuditMapper);
71+
container.bind<Logger>(TYPES.Logger).toConstantValue(loggerMock as unknown as Logger);
6972
container
7073
.bind(TYPES.AllocatorRegistryConfig)
7174
.toConstantValue(configMock as unknown as GithubConfig);
@@ -82,7 +85,7 @@ describe('RefreshAuditPublisher', () => {
8285
githubMock.mergePullRequest.mockResolvedValue({});
8386
githubMock.deleteBranch.mockResolvedValue({});
8487

85-
commandBusMock.send.mockResolvedValue({ success: true, data: structuredClone(baseAllocator) });
88+
commandBusMock.send.mockResolvedValue({ success: true, data: baseAllocator });
8689
});
8790

8891
it('newAudit creates a new pending audit and publishes PR', async () => {
@@ -103,7 +106,7 @@ describe('RefreshAuditPublisher', () => {
103106
it.each([AuditOutcome.PENDING, AuditOutcome.APPROVED])(
104107
'newAudit throws when last audit is %s',
105108
async outcome => {
106-
const pendingAllocator = structuredClone(baseAllocator);
109+
const pendingAllocator = baseAllocator;
107110
pendingAllocator.audits[pendingAllocator.audits.length - 1].outcome = outcome;
108111
commandBusMock.send.mockResolvedValueOnce({ success: true, data: pendingAllocator });
109112

packages/application/src/application/publishers/refresh-audit-publisher.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import {
1212
import { GithubConfig } from '@src/domain/types';
1313
import { IAuditMapper } from '@src/infrastructure/mappers/audit-mapper';
1414
import { nanoid } from 'nanoid';
15+
import { LOG_MESSAGES } from '@src/constants';
16+
17+
const LOG = LOG_MESSAGES.REFRESH_AUDIT_PUBLISHER;
1518

1619
export interface IRefreshAuditPublisher {
1720
newAudit(jsonHash: string): Promise<{
@@ -40,6 +43,7 @@ export class RefreshAuditPublisher implements IRefreshAuditPublisher {
4043
@inject(TYPES.AllocatorRegistryConfig) private readonly _allocatorRegistryConfig: GithubConfig,
4144
@inject(TYPES.CommandBus) private readonly _commandBus: ICommandBus,
4245
@inject(TYPES.AuditMapper) private readonly _auditMapper: IAuditMapper,
46+
@inject(TYPES.Logger) private readonly _logger: Logger,
4347
) {}
4448

4549
async newAudit(jsonHash: string): Promise<{
@@ -65,6 +69,8 @@ export class RefreshAuditPublisher implements IRefreshAuditPublisher {
6569

6670
const result = await this.publish(jsonHash, allocator);
6771

72+
this._logger.info(`${LOG.NEW_AUDIT_PUBLISHED} jsonHash: ${jsonHash}`);
73+
6874
return {
6975
auditChange: newAudit,
7076
...result,
@@ -74,6 +80,7 @@ export class RefreshAuditPublisher implements IRefreshAuditPublisher {
7480
async updateAudit(
7581
jsonHash: string,
7682
auditData: Partial<AuditData> | ((jsonA: ApplicationPullRequestFile) => Partial<AuditData>),
83+
expectedPreviousAuditOutcome?: AuditOutcome[],
7784
): Promise<{
7885
auditChange: Partial<AuditData>;
7986
branchName: string;
@@ -83,6 +90,14 @@ export class RefreshAuditPublisher implements IRefreshAuditPublisher {
8390
}> {
8491
const { allocator } = await this.getAllocatorJsonDetails(jsonHash);
8592

93+
if (
94+
expectedPreviousAuditOutcome &&
95+
!expectedPreviousAuditOutcome.includes(allocator.audits.at(-1)?.outcome as AuditOutcome)
96+
)
97+
throw new Error(
98+
`Previous status not allowed. Expected: ${expectedPreviousAuditOutcome.join(', ')} but got: ${allocator.audits.at(-1)?.outcome}`,
99+
);
100+
86101
const auditDataToUpdate = typeof auditData === 'function' ? auditData(allocator!) : auditData;
87102

88103
Object.entries(this._auditMapper.partialFromAuditDataToDomain(auditDataToUpdate)).forEach(
@@ -93,6 +108,10 @@ export class RefreshAuditPublisher implements IRefreshAuditPublisher {
93108

94109
const result = await this.publish(jsonHash, allocator);
95110

111+
this._logger.info(
112+
`${LOG.AUDIT_UPDATED} jsonHash: ${jsonHash} outcome: ${auditDataToUpdate.outcome}`,
113+
);
114+
96115
return {
97116
auditChange: auditDataToUpdate,
98117
...result,
@@ -116,7 +135,7 @@ export class RefreshAuditPublisher implements IRefreshAuditPublisher {
116135

117136
private async publish(jsonHash: string, allocator: ApplicationPullRequestFile) {
118137
const branchName = `refresh-audit-${jsonHash}-${allocator.audits.length}-${nanoid()}`;
119-
const prTitle = `Refresh Audit ${allocator.application_number} - ${allocator.audits.length}`;
138+
const prTitle = `Refresh Audit ${allocator.application_number} - ${allocator.audits.length} ${allocator.audits.at(-1)?.outcome}`;
120139
const prBody = `This PR is a refresh audit for the application ${allocator.application_number}.`;
121140

122141
const branch = await this._github.createBranch(

packages/application/src/application/services/refresh-audit.service.test.ts

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { Container } from 'inversify';
44
import { TYPES } from '@src/types';
55
import { AuditOutcome } from '@src/infrastructure/repositories/issue-details';
66
import { RefreshAuditService } from './refresh-audit.service';
7-
import { ApplicationPullRequestFile } from './pull-request.types';
87

98
describe('RefreshAuditService', () => {
109
let container: Container;
@@ -61,7 +60,12 @@ describe('RefreshAuditService', () => {
6160

6261
expect(refreshAuditPublisherMock.updateAudit).toHaveBeenCalledWith(
6362
jsonHash,
64-
expect.any(Function),
63+
{
64+
datacapAmount,
65+
ended: new Date().toISOString(),
66+
outcome: AuditOutcome.APPROVED,
67+
},
68+
[AuditOutcome.PENDING],
6569
);
6670
expect(result.auditChange).toEqual(expectedChange);
6771
});
@@ -83,37 +87,12 @@ describe('RefreshAuditService', () => {
8387

8488
expect(refreshAuditPublisherMock.updateAudit).toHaveBeenCalledWith(
8589
jsonHash,
86-
expect.any(Function),
90+
{
91+
ended: new Date().toISOString(),
92+
outcome: AuditOutcome.REJECTED,
93+
},
94+
[AuditOutcome.PENDING],
8795
);
8896
expect(result.auditChange).toEqual(expectedChange);
8997
});
90-
91-
it('should throw error if current audit is not in the correct status', async () => {
92-
await expect(
93-
service.ensureCorrectCurrentJsonAuditStatus(
94-
{ audits: [{ outcome: AuditOutcome.GRANTED }] } as unknown as ApplicationPullRequestFile,
95-
[AuditOutcome.PENDING],
96-
),
97-
).rejects.toThrow('Cannot update audit because it is not in the correct status');
98-
});
99-
100-
it('should throw error if allocator has no audits', async () => {
101-
await expect(
102-
service.ensureCorrectCurrentJsonAuditStatus(
103-
{ audits: [] } as unknown as ApplicationPullRequestFile,
104-
[AuditOutcome.PENDING],
105-
),
106-
).rejects.toThrow('Allocator must have at least one audit from completed application');
107-
});
108-
109-
it('should resolve outcome correctly', async () => {
110-
await expect(
111-
service.ensureCorrectCurrentJsonAuditStatus(
112-
{
113-
audits: [{ outcome: AuditOutcome.GRANTED }, { outcome: AuditOutcome.PENDING }],
114-
} as unknown as ApplicationPullRequestFile,
115-
[AuditOutcome.PENDING],
116-
),
117-
).resolves.toBeUndefined();
118-
});
11998
});

packages/application/src/application/services/refresh-audit.service.ts

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -34,49 +34,41 @@ export class RefreshAuditService implements IRefreshAuditService {
3434
}
3535

3636
async approveAudit(jsonHash: string, datacapAmount: number): Promise<UpdateAuditResult> {
37-
return this._refreshAuditPublisher.updateAudit(jsonHash, allocator => {
38-
this.ensureCorrectCurrentJsonAuditStatus(allocator, [AuditOutcome.PENDING]);
39-
40-
return {
37+
return this._refreshAuditPublisher.updateAudit(
38+
jsonHash,
39+
{
4140
ended: new Date().toISOString(),
4241
outcome: AuditOutcome.APPROVED,
4342
datacapAmount,
44-
};
45-
});
43+
},
44+
[AuditOutcome.PENDING],
45+
);
4646
}
4747

4848
async rejectAudit(jsonHash: string): Promise<UpdateAuditResult> {
49-
return this._refreshAuditPublisher.updateAudit(jsonHash, allocator => {
50-
this.ensureCorrectCurrentJsonAuditStatus(allocator, [AuditOutcome.PENDING]);
51-
52-
return {
49+
return this._refreshAuditPublisher.updateAudit(
50+
jsonHash,
51+
{
5352
ended: new Date().toISOString(),
5453
outcome: AuditOutcome.REJECTED,
55-
};
56-
});
54+
},
55+
[AuditOutcome.PENDING],
56+
);
5757
}
5858

5959
async finishAudit(jsonHash: string): Promise<UpdateAuditResult> {
60-
return this._refreshAuditPublisher.updateAudit(jsonHash, allocator => {
61-
this.ensureCorrectCurrentJsonAuditStatus(allocator, [AuditOutcome.APPROVED]);
62-
const prevAudit = allocator.audits.at(-2);
63-
const currentAudit = allocator.audits.at(-1);
64-
65-
return {
66-
dcAllocated: new Date().toISOString(),
67-
outcome: this._auditOutcomeResolver.resolve(prevAudit, currentAudit),
68-
};
69-
});
70-
}
60+
return this._refreshAuditPublisher.updateAudit(
61+
jsonHash,
62+
allocator => {
63+
const prevAudit = allocator.audits.at(-2);
64+
const currentAudit = allocator.audits.at(-1);
7165

72-
async ensureCorrectCurrentJsonAuditStatus(
73-
allocator: ApplicationPullRequestFile,
74-
status: AuditOutcome[],
75-
): Promise<void> {
76-
const lastAudit = allocator?.audits?.at(-1);
77-
if (!lastAudit)
78-
throw new Error('Allocator must have at least one audit from completed application');
79-
if (!status.includes(lastAudit.outcome as AuditOutcome))
80-
throw new Error('Cannot update audit because it is not in the correct status');
66+
return {
67+
dcAllocated: new Date().toISOString(),
68+
outcome: this._auditOutcomeResolver.resolve(prevAudit, currentAudit),
69+
};
70+
},
71+
[AuditOutcome.APPROVED],
72+
);
8173
}
8274
}

packages/application/src/application/use-cases/refresh-issues/approve-refresh.command.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,6 @@ export class ApproveRefreshCommandHandler implements ICommandHandler<ApproveRefr
7777
return {
7878
...issueDetails,
7979
refreshStatus: RefreshStatus.APPROVED,
80-
currentAudit: {
81-
...issueDetails.currentAudit,
82-
...auditResult.auditChange,
83-
},
8480
auditHistory,
8581
};
8682
}

packages/application/src/application/use-cases/refresh-issues/reject-refesh.command.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,6 @@ export class RejectRefreshCommandHandler implements ICommandHandler<RejectRefres
7373
return {
7474
...issueDetails,
7575
refreshStatus: RefreshStatus.REJECTED,
76-
currentAudit: {
77-
...issueDetails.currentAudit,
78-
...auditResult.auditChange,
79-
},
8076
auditHistory,
8177
};
8278
}

packages/application/src/application/use-cases/refresh-issues/save-issue-with-new-audit.command.test.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ describe('SaveIssueWithNewAuditCommand', () => {
5050
SaveIssueWithNewAuditCommandHandler,
5151
);
5252

53-
(refreshAuditServiceMock.startAudit as any).mockResolvedValue(auditResult);
54-
(commandBusMock.send as any).mockResolvedValue({ success: true });
53+
refreshAuditServiceMock.startAudit.mockResolvedValue(auditResult);
54+
commandBusMock.send.mockResolvedValue({ success: true });
5555
vi.clearAllMocks();
5656
});
5757

@@ -61,16 +61,18 @@ describe('SaveIssueWithNewAuditCommand', () => {
6161
expect(refreshAuditServiceMock.startAudit).toHaveBeenCalledWith(issue.jsonNumber);
6262
expect(commandBusMock.send).toHaveBeenCalled();
6363

64-
const sentCommand = (commandBusMock.send as any).mock.calls[0][0] as SaveIssueCommand;
64+
const sentCommand = commandBusMock.send.mock.calls[0][0] as SaveIssueCommand;
6565

6666
expect(sentCommand).toBeInstanceOf(SaveIssueCommand);
67-
expect(sentCommand.issueDetails.currentAudit?.started).toBe(auditResult.auditChange.started);
67+
expect(sentCommand.issueDetails.auditHistory?.at(-1)?.auditChange.started).toBe(
68+
auditResult.auditChange.started,
69+
);
6870
expect(result).toStrictEqual({ success: true });
6971
});
7072

7173
it('returns failure when startAudit throws', async () => {
7274
const error = new Error('audit failed');
73-
(refreshAuditServiceMock.startAudit as any).mockRejectedValueOnce(error);
75+
refreshAuditServiceMock.startAudit.mockRejectedValueOnce(error);
7476

7577
const result = await handler.handle(new SaveIssueWithNewAuditCommand(issue));
7678
expect(result).toStrictEqual({ success: false, error });

packages/application/src/application/use-cases/refresh-issues/save-issue-with-new-audit.command.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,6 @@ export class SaveIssueWithNewAuditCommandHandler
3838

3939
const issueWithAudit = {
4040
...command.issueDetails,
41-
currentAudit: {
42-
...command.issueDetails.currentAudit,
43-
...auditResult.auditChange,
44-
},
4541
auditHistory,
4642
};
4743

packages/application/src/application/use-cases/refresh-issues/upsert-issue.command.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { IIssueMapper } from '@src/infrastructure/mappers/issue-mapper';
1111
import { IAuditMapper } from '@src/infrastructure/mappers/audit-mapper';
1212
import { IUpsertStrategy } from './upsert-issue.strategy';
1313
import { SaveIssueCommand } from './save-issue.command';
14+
import { SaveIssueWithNewAuditCommand } from './save-issue-with-new-audit.command';
1415

1516
vi.mock('nanoid', () => ({
1617
nanoid: vi.fn().mockReturnValue('guid'),
@@ -166,7 +167,9 @@ describe('UpsertIssueCommand', () => {
166167
const command = new UpsertIssueCommand({ ...fixtureIssueDetails, jsonNumber: '' });
167168
const result = await handler.handle(command);
168169

169-
expect(commandBusMock.send).not.toHaveBeenCalled();
170+
expect(commandBusMock.send).toHaveBeenCalledWith(expect.any(FetchAllocatorCommand));
171+
expect(commandBusMock.send).not.toHaveBeenCalledWith(expect.any(SaveIssueCommand));
172+
expect(commandBusMock.send).not.toHaveBeenCalledWith(expect.any(SaveIssueWithNewAuditCommand));
170173
expect(result).toStrictEqual({
171174
success: false,
172175
error: expect.objectContaining({

0 commit comments

Comments
 (0)