Fix QA validation issues and add M7.1 security fixes (#318)
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #318.
This commit is contained in:
@@ -119,9 +119,14 @@ describe("CoordinatorIntegrationService - Concurrency", () => {
|
||||
expect(result.status).toBe(RunnerJobStatus.RUNNING);
|
||||
|
||||
// Verify SELECT FOR UPDATE was used
|
||||
expect(mockTxClient.$queryRaw).toHaveBeenCalledWith(
|
||||
expect.anything() // Raw SQL with FOR UPDATE
|
||||
);
|
||||
expect(mockTxClient.$queryRaw).toHaveBeenCalled();
|
||||
const sqlCall = mockTxClient.$queryRaw.mock.calls[0];
|
||||
expect(sqlCall).toBeDefined();
|
||||
// Verify the SQL contains FOR UPDATE (raw SQL is passed as template parts)
|
||||
const sqlString = sqlCall?.toString() ?? "";
|
||||
expect(
|
||||
sqlString.includes("FOR UPDATE") || sqlCall?.[0]?.toString().includes("FOR UPDATE")
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("should handle concurrent status updates by coordinator and API", async () => {
|
||||
@@ -313,8 +318,11 @@ describe("CoordinatorIntegrationService - Concurrency", () => {
|
||||
version: mockJob.version + 1,
|
||||
};
|
||||
|
||||
vi.mocked(prisma.runnerJob.findUnique).mockResolvedValue(mockJob as any);
|
||||
// First findUnique returns the current job state
|
||||
vi.mocked(prisma.runnerJob.findUnique).mockResolvedValueOnce(mockJob as any);
|
||||
// updateMany succeeds
|
||||
vi.mocked(prisma.runnerJob.updateMany).mockResolvedValue({ count: 1 });
|
||||
// Second findUnique returns the updated job state
|
||||
vi.mocked(prisma.runnerJob.findUnique).mockResolvedValueOnce(updatedJob as any);
|
||||
|
||||
const result = await service.updateJobProgress(jobId, {
|
||||
|
||||
@@ -58,7 +58,10 @@ describe("CoordinatorIntegrationService", () => {
|
||||
create: vi.fn(),
|
||||
findUnique: vi.fn(),
|
||||
update: vi.fn(),
|
||||
updateMany: vi.fn(),
|
||||
},
|
||||
$transaction: vi.fn(),
|
||||
$queryRaw: vi.fn(),
|
||||
};
|
||||
|
||||
const mockJobEventsService = {
|
||||
@@ -97,6 +100,9 @@ describe("CoordinatorIntegrationService", () => {
|
||||
jobEventsService = module.get<JobEventsService>(JobEventsService);
|
||||
heraldService = module.get<HeraldService>(HeraldService);
|
||||
bullMqService = module.get<BullMqService>(BullMqService);
|
||||
|
||||
// Set default mock return values
|
||||
mockPrismaService.runnerJob.updateMany.mockResolvedValue({ count: 1 });
|
||||
});
|
||||
|
||||
describe("createJob", () => {
|
||||
@@ -145,8 +151,26 @@ describe("CoordinatorIntegrationService", () => {
|
||||
it("should update job status to RUNNING", async () => {
|
||||
const updatedJob = { ...mockJob, status: RunnerJobStatus.RUNNING, startedAt: new Date() };
|
||||
|
||||
mockPrismaService.runnerJob.findUnique.mockResolvedValue(mockJob);
|
||||
mockPrismaService.runnerJob.update.mockResolvedValue(updatedJob);
|
||||
// Mock transaction that passes through the callback
|
||||
mockPrismaService.$transaction.mockImplementation(async (callback) => {
|
||||
const mockTx = {
|
||||
$queryRaw: vi
|
||||
.fn()
|
||||
.mockResolvedValue([
|
||||
{
|
||||
id: mockJob.id,
|
||||
status: mockJob.status,
|
||||
workspace_id: mockJob.workspaceId,
|
||||
version: 1,
|
||||
},
|
||||
]),
|
||||
runnerJob: {
|
||||
update: vi.fn().mockResolvedValue(updatedJob),
|
||||
},
|
||||
};
|
||||
return callback(mockTx);
|
||||
});
|
||||
|
||||
mockJobEventsService.emitJobStarted.mockResolvedValue(mockEvent);
|
||||
mockHeraldService.broadcastJobEvent.mockResolvedValue(undefined);
|
||||
|
||||
@@ -160,7 +184,16 @@ describe("CoordinatorIntegrationService", () => {
|
||||
});
|
||||
|
||||
it("should throw NotFoundException if job does not exist", async () => {
|
||||
mockPrismaService.runnerJob.findUnique.mockResolvedValue(null);
|
||||
// Mock transaction with empty result
|
||||
mockPrismaService.$transaction.mockImplementation(async (callback) => {
|
||||
const mockTx = {
|
||||
$queryRaw: vi.fn().mockResolvedValue([]),
|
||||
runnerJob: {
|
||||
update: vi.fn(),
|
||||
},
|
||||
};
|
||||
return callback(mockTx);
|
||||
});
|
||||
|
||||
await expect(
|
||||
service.updateJobStatus("non-existent", { status: "RUNNING" as const })
|
||||
@@ -168,8 +201,25 @@ describe("CoordinatorIntegrationService", () => {
|
||||
});
|
||||
|
||||
it("should throw BadRequestException for invalid status transition", async () => {
|
||||
const completedJob = { ...mockJob, status: RunnerJobStatus.COMPLETED };
|
||||
mockPrismaService.runnerJob.findUnique.mockResolvedValue(completedJob);
|
||||
// Mock transaction with completed job
|
||||
mockPrismaService.$transaction.mockImplementation(async (callback) => {
|
||||
const mockTx = {
|
||||
$queryRaw: vi
|
||||
.fn()
|
||||
.mockResolvedValue([
|
||||
{
|
||||
id: mockJob.id,
|
||||
status: RunnerJobStatus.COMPLETED,
|
||||
workspace_id: mockJob.workspaceId,
|
||||
version: 1,
|
||||
},
|
||||
]),
|
||||
runnerJob: {
|
||||
update: vi.fn(),
|
||||
},
|
||||
};
|
||||
return callback(mockTx);
|
||||
});
|
||||
|
||||
await expect(
|
||||
service.updateJobStatus("job-123", { status: "RUNNING" as const })
|
||||
@@ -179,11 +229,12 @@ describe("CoordinatorIntegrationService", () => {
|
||||
|
||||
describe("updateJobProgress", () => {
|
||||
it("should update job progress percentage", async () => {
|
||||
const runningJob = { ...mockJob, status: RunnerJobStatus.RUNNING };
|
||||
const updatedJob = { ...runningJob, progressPercent: 50 };
|
||||
const runningJob = { ...mockJob, status: RunnerJobStatus.RUNNING, version: 1 };
|
||||
const updatedJob = { ...runningJob, progressPercent: 50, version: 2 };
|
||||
|
||||
mockPrismaService.runnerJob.findUnique.mockResolvedValue(runningJob);
|
||||
mockPrismaService.runnerJob.update.mockResolvedValue(updatedJob);
|
||||
mockPrismaService.runnerJob.updateMany.mockResolvedValue({ count: 1 });
|
||||
mockPrismaService.runnerJob.findUnique.mockResolvedValue(updatedJob);
|
||||
mockJobEventsService.emitEvent.mockResolvedValue(mockEvent);
|
||||
|
||||
const result = await service.updateJobProgress("job-123", {
|
||||
@@ -217,8 +268,26 @@ describe("CoordinatorIntegrationService", () => {
|
||||
completedAt: new Date(),
|
||||
};
|
||||
|
||||
mockPrismaService.runnerJob.findUnique.mockResolvedValue(runningJob);
|
||||
mockPrismaService.runnerJob.update.mockResolvedValue(completedJob);
|
||||
// Mock transaction with running job
|
||||
mockPrismaService.$transaction.mockImplementation(async (callback) => {
|
||||
const mockTx = {
|
||||
$queryRaw: vi
|
||||
.fn()
|
||||
.mockResolvedValue([
|
||||
{
|
||||
id: mockJob.id,
|
||||
status: RunnerJobStatus.RUNNING,
|
||||
workspace_id: mockJob.workspaceId,
|
||||
version: 1,
|
||||
},
|
||||
]),
|
||||
runnerJob: {
|
||||
update: vi.fn().mockResolvedValue(completedJob),
|
||||
},
|
||||
};
|
||||
return callback(mockTx);
|
||||
});
|
||||
|
||||
mockJobEventsService.emitJobCompleted.mockResolvedValue(mockEvent);
|
||||
mockHeraldService.broadcastJobEvent.mockResolvedValue(undefined);
|
||||
|
||||
@@ -243,8 +312,26 @@ describe("CoordinatorIntegrationService", () => {
|
||||
completedAt: new Date(),
|
||||
};
|
||||
|
||||
mockPrismaService.runnerJob.findUnique.mockResolvedValue(runningJob);
|
||||
mockPrismaService.runnerJob.update.mockResolvedValue(failedJob);
|
||||
// Mock transaction with running job
|
||||
mockPrismaService.$transaction.mockImplementation(async (callback) => {
|
||||
const mockTx = {
|
||||
$queryRaw: vi
|
||||
.fn()
|
||||
.mockResolvedValue([
|
||||
{
|
||||
id: mockJob.id,
|
||||
status: RunnerJobStatus.RUNNING,
|
||||
workspace_id: mockJob.workspaceId,
|
||||
version: 1,
|
||||
},
|
||||
]),
|
||||
runnerJob: {
|
||||
update: vi.fn().mockResolvedValue(failedJob),
|
||||
},
|
||||
};
|
||||
return callback(mockTx);
|
||||
});
|
||||
|
||||
mockJobEventsService.emitJobFailed.mockResolvedValue(mockEvent);
|
||||
mockHeraldService.broadcastJobEvent.mockResolvedValue(undefined);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user