mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-16 19:00:38 +09:00
127 lines
3.1 KiB
TypeScript
127 lines
3.1 KiB
TypeScript
|
|
import express from "express";
|
||
|
|
import request from "supertest";
|
||
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||
|
|
import { accessRoutes } from "../routes/access.js";
|
||
|
|
import { errorHandler } from "../middleware/index.js";
|
||
|
|
|
||
|
|
const logActivityMock = vi.fn();
|
||
|
|
|
||
|
|
vi.mock("../services/index.js", () => ({
|
||
|
|
accessService: () => ({
|
||
|
|
isInstanceAdmin: vi.fn(),
|
||
|
|
canUser: vi.fn(),
|
||
|
|
hasPermission: vi.fn(),
|
||
|
|
}),
|
||
|
|
agentService: () => ({
|
||
|
|
getById: vi.fn(),
|
||
|
|
}),
|
||
|
|
boardAuthService: () => ({
|
||
|
|
createChallenge: vi.fn(),
|
||
|
|
resolveBoardAccess: vi.fn(),
|
||
|
|
assertCurrentBoardKey: vi.fn(),
|
||
|
|
revokeBoardApiKey: vi.fn(),
|
||
|
|
}),
|
||
|
|
deduplicateAgentName: vi.fn(),
|
||
|
|
logActivity: (...args: unknown[]) => logActivityMock(...args),
|
||
|
|
notifyHireApproved: vi.fn(),
|
||
|
|
}));
|
||
|
|
|
||
|
|
function createDbStub() {
|
||
|
|
const createdInvite = {
|
||
|
|
id: "invite-1",
|
||
|
|
companyId: "company-1",
|
||
|
|
inviteType: "company_join",
|
||
|
|
allowedJoinTypes: "human",
|
||
|
|
tokenHash: "hash",
|
||
|
|
defaultsPayload: { humanRole: "viewer" },
|
||
|
|
expiresAt: new Date("2027-03-10T00:00:00.000Z"),
|
||
|
|
invitedByUserId: null,
|
||
|
|
revokedAt: null,
|
||
|
|
acceptedAt: null,
|
||
|
|
createdAt: new Date("2026-03-07T00:00:00.000Z"),
|
||
|
|
updatedAt: new Date("2026-03-07T00:00:00.000Z"),
|
||
|
|
};
|
||
|
|
|
||
|
|
return {
|
||
|
|
insert() {
|
||
|
|
return {
|
||
|
|
values() {
|
||
|
|
return {
|
||
|
|
returning() {
|
||
|
|
return Promise.resolve([createdInvite]);
|
||
|
|
},
|
||
|
|
};
|
||
|
|
},
|
||
|
|
};
|
||
|
|
},
|
||
|
|
select(_shape?: unknown) {
|
||
|
|
return {
|
||
|
|
from() {
|
||
|
|
const query = {
|
||
|
|
leftJoin() {
|
||
|
|
return query;
|
||
|
|
},
|
||
|
|
where() {
|
||
|
|
return Promise.resolve([{
|
||
|
|
name: "Acme Robotics",
|
||
|
|
brandColor: "#114488",
|
||
|
|
logoAssetId: "logo-1",
|
||
|
|
}]);
|
||
|
|
},
|
||
|
|
};
|
||
|
|
return query;
|
||
|
|
},
|
||
|
|
};
|
||
|
|
},
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function createApp() {
|
||
|
|
const app = express();
|
||
|
|
app.use(express.json());
|
||
|
|
app.use((req, _res, next) => {
|
||
|
|
(req as any).actor = {
|
||
|
|
type: "board",
|
||
|
|
source: "local_implicit",
|
||
|
|
userId: null,
|
||
|
|
companyIds: ["company-1"],
|
||
|
|
};
|
||
|
|
next();
|
||
|
|
});
|
||
|
|
app.use(
|
||
|
|
"/api",
|
||
|
|
accessRoutes(createDbStub() as any, {
|
||
|
|
deploymentMode: "local_trusted",
|
||
|
|
deploymentExposure: "private",
|
||
|
|
bindHost: "127.0.0.1",
|
||
|
|
allowedHostnames: [],
|
||
|
|
}),
|
||
|
|
);
|
||
|
|
app.use(errorHandler);
|
||
|
|
return app;
|
||
|
|
}
|
||
|
|
|
||
|
|
describe("POST /companies/:companyId/invites", () => {
|
||
|
|
beforeEach(() => {
|
||
|
|
logActivityMock.mockReset();
|
||
|
|
});
|
||
|
|
|
||
|
|
it("returns an absolute invite URL using the request base URL", async () => {
|
||
|
|
const app = createApp();
|
||
|
|
|
||
|
|
const res = await request(app)
|
||
|
|
.post("/api/companies/company-1/invites")
|
||
|
|
.set("host", "paperclip.example")
|
||
|
|
.set("x-forwarded-proto", "https")
|
||
|
|
.send({
|
||
|
|
allowedJoinTypes: "human",
|
||
|
|
humanRole: "viewer",
|
||
|
|
});
|
||
|
|
|
||
|
|
expect(res.status).toBe(201);
|
||
|
|
expect(res.body.companyName).toBe("Acme Robotics");
|
||
|
|
expect(res.body.invitePath).toMatch(/^\/invite\/pcp_invite_/);
|
||
|
|
expect(res.body.inviteUrl).toMatch(/^https:\/\/paperclip\.example\/invite\/pcp_invite_/);
|
||
|
|
});
|
||
|
|
});
|