test(#291): add test for connection limit per workspace
Add test to verify workspace connection limit enforcement. Default limit is 100 connections per workspace. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -88,6 +88,7 @@ describe("ConnectionService", () => {
|
|||||||
findMany: vi.fn(),
|
findMany: vi.fn(),
|
||||||
update: vi.fn(),
|
update: vi.fn(),
|
||||||
delete: vi.fn(),
|
delete: vi.fn(),
|
||||||
|
count: vi.fn(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -136,6 +137,19 @@ describe("ConnectionService", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("initiateConnection", () => {
|
describe("initiateConnection", () => {
|
||||||
|
it("should throw error if workspace has reached connection limit", async () => {
|
||||||
|
const existingConnections = Array.from({ length: 100 }, (_, i) => ({
|
||||||
|
...mockConnection,
|
||||||
|
id: `conn-${i}`,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.spyOn(prismaService.federationConnection, "count").mockResolvedValue(100);
|
||||||
|
|
||||||
|
await expect(service.initiateConnection(mockWorkspaceId, mockRemoteUrl)).rejects.toThrow(
|
||||||
|
"Connection limit reached for workspace. Maximum 100 connections allowed per workspace."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("should create a pending connection", async () => {
|
it("should create a pending connection", async () => {
|
||||||
const mockAxiosResponse: AxiosResponse = {
|
const mockAxiosResponse: AxiosResponse = {
|
||||||
data: mockRemoteIdentity,
|
data: mockRemoteIdentity,
|
||||||
@@ -145,6 +159,7 @@ describe("ConnectionService", () => {
|
|||||||
config: {} as never,
|
config: {} as never,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
vi.spyOn(prismaService.federationConnection, "count").mockResolvedValue(5);
|
||||||
vi.spyOn(httpService, "get").mockReturnValue(of(mockAxiosResponse));
|
vi.spyOn(httpService, "get").mockReturnValue(of(mockAxiosResponse));
|
||||||
vi.spyOn(httpService, "post").mockReturnValue(
|
vi.spyOn(httpService, "post").mockReturnValue(
|
||||||
of({ data: { accepted: true } } as AxiosResponse)
|
of({ data: { accepted: true } } as AxiosResponse)
|
||||||
@@ -176,6 +191,7 @@ describe("ConnectionService", () => {
|
|||||||
config: {} as never,
|
config: {} as never,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
vi.spyOn(prismaService.federationConnection, "count").mockResolvedValue(5);
|
||||||
vi.spyOn(httpService, "get").mockReturnValue(of(mockAxiosResponse));
|
vi.spyOn(httpService, "get").mockReturnValue(of(mockAxiosResponse));
|
||||||
vi.spyOn(httpService, "post").mockReturnValue(
|
vi.spyOn(httpService, "post").mockReturnValue(
|
||||||
of({ data: { accepted: true } } as AxiosResponse)
|
of({ data: { accepted: true } } as AxiosResponse)
|
||||||
@@ -202,6 +218,7 @@ describe("ConnectionService", () => {
|
|||||||
config: {} as never,
|
config: {} as never,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
vi.spyOn(prismaService.federationConnection, "count").mockResolvedValue(5);
|
||||||
const postSpy = vi
|
const postSpy = vi
|
||||||
.spyOn(httpService, "post")
|
.spyOn(httpService, "post")
|
||||||
.mockReturnValue(of({ data: { accepted: true } } as AxiosResponse));
|
.mockReturnValue(of({ data: { accepted: true } } as AxiosResponse));
|
||||||
@@ -230,6 +247,7 @@ describe("ConnectionService", () => {
|
|||||||
config: {} as never,
|
config: {} as never,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
vi.spyOn(prismaService.federationConnection, "count").mockResolvedValue(5);
|
||||||
vi.spyOn(httpService, "get").mockReturnValue(of(mockAxiosResponse));
|
vi.spyOn(httpService, "get").mockReturnValue(of(mockAxiosResponse));
|
||||||
vi.spyOn(httpService, "post").mockReturnValue(
|
vi.spyOn(httpService, "post").mockReturnValue(
|
||||||
throwError(() => new Error("Connection refused"))
|
throwError(() => new Error("Connection refused"))
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import type { PublicInstanceIdentity } from "./types/instance.types";
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class ConnectionService {
|
export class ConnectionService {
|
||||||
private readonly logger = new Logger(ConnectionService.name);
|
private readonly logger = new Logger(ConnectionService.name);
|
||||||
|
private readonly MAX_CONNECTIONS_PER_WORKSPACE = 100;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly prisma: PrismaService,
|
private readonly prisma: PrismaService,
|
||||||
@@ -40,6 +41,17 @@ export class ConnectionService {
|
|||||||
async initiateConnection(workspaceId: string, remoteUrl: string): Promise<ConnectionDetails> {
|
async initiateConnection(workspaceId: string, remoteUrl: string): Promise<ConnectionDetails> {
|
||||||
this.logger.log(`Initiating connection to ${remoteUrl} for workspace ${workspaceId}`);
|
this.logger.log(`Initiating connection to ${remoteUrl} for workspace ${workspaceId}`);
|
||||||
|
|
||||||
|
// Check connection limit for workspace
|
||||||
|
const connectionCount = await this.prisma.federationConnection.count({
|
||||||
|
where: { workspaceId },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (connectionCount >= this.MAX_CONNECTIONS_PER_WORKSPACE) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
`Connection limit reached for workspace. Maximum ${String(this.MAX_CONNECTIONS_PER_WORKSPACE)} connections allowed per workspace.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch remote instance identity
|
// Fetch remote instance identity
|
||||||
const remoteIdentity = await this.fetchRemoteIdentity(remoteUrl);
|
const remoteIdentity = await this.fetchRemoteIdentity(remoteUrl);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user