fix(#275): Prevent silent connection initiation failures
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed

Fixed silent connection initiation failures where HTTP errors were caught
but success was returned to the user, leaving zombie connections in
PENDING state forever.

Changes:
- Delete failed connection from database when HTTP request fails
- Throw BadRequestException with clear error message
- Added test to verify connection deletion and exception throwing
- Import BadRequestException in connection.service.ts

User Impact:
- Users now receive immediate feedback when connection initiation fails
- No more zombie connections stuck in PENDING state
- Clear error messages indicate the reason for failure

Testing:
- Added test case: "should delete connection and throw error if request fails"
- All 21 connection service tests passing
- Quality gates: lint, typecheck, build all passing

Fixes #275

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-03 20:21:06 -06:00
parent 7a84d96d72
commit 7d9c102c6d
3 changed files with 125 additions and 2 deletions

View File

@@ -85,6 +85,7 @@ describe("ConnectionService", () => {
findUnique: vi.fn(),
findMany: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
},
},
},
@@ -208,6 +209,36 @@ describe("ConnectionService", () => {
})
);
});
it("should delete connection and throw error if request fails", async () => {
const mockAxiosResponse: AxiosResponse = {
data: mockRemoteIdentity,
status: 200,
statusText: "OK",
headers: {},
config: {} as never,
};
vi.spyOn(httpService, "get").mockReturnValue(of(mockAxiosResponse));
vi.spyOn(httpService, "post").mockReturnValue(
throwError(() => new Error("Connection refused"))
);
const createSpy = vi
.spyOn(prismaService.federationConnection, "create")
.mockResolvedValue(mockConnection);
const deleteSpy = vi
.spyOn(prismaService.federationConnection, "delete")
.mockResolvedValue(mockConnection);
await expect(service.initiateConnection(mockWorkspaceId, mockRemoteUrl)).rejects.toThrow(
"Failed to initiate connection"
);
expect(createSpy).toHaveBeenCalled();
expect(deleteSpy).toHaveBeenCalledWith({
where: { id: mockConnection.id },
});
});
});
describe("acceptConnection", () => {