259 lines
9.3 KiB
TypeScript
259 lines
9.3 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import {
|
|
addOrganizationMemberAndCreatePlatformUser,
|
|
createOrganizationGroup,
|
|
getOrganizationApiConfig,
|
|
isOrganizationPermissionDenied,
|
|
isOrganizationRouteNotFound,
|
|
listOrganizationMembers,
|
|
resetPlatformUserPassword
|
|
} from "@/lib/server/organization-client";
|
|
|
|
describe("organization account client", () => {
|
|
afterEach(() => {
|
|
vi.unstubAllEnvs();
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
it("tracks required organization and staff API configuration separately", () => {
|
|
vi.stubEnv("ZHINIAN_ORG_API_BASE_URL", "https://gateway.example.com/basic");
|
|
vi.stubEnv("ZHINIAN_ORG_API_TOKEN", "org-token");
|
|
|
|
expect(getOrganizationApiConfig()).toMatchObject({
|
|
configured: true,
|
|
staffConfigured: false,
|
|
staffMissing: ["ZHINIAN_STAFF_API_BASE_URL", "ZHINIAN_ORG_TENANT_ID"]
|
|
});
|
|
});
|
|
|
|
it("accepts the current login token instead of a static organization token", () => {
|
|
vi.stubEnv("ZHINIAN_ORG_API_BASE_URL", "https://gateway.example.com/basic");
|
|
vi.stubEnv("ZHINIAN_STAFF_API_BASE_URL", "https://gateway.example.com/staff");
|
|
vi.stubEnv("ZHINIAN_ORG_TENANT_ID", "1");
|
|
|
|
expect(getOrganizationApiConfig("login-token")).toMatchObject({
|
|
organizationToken: "login-token",
|
|
staffToken: "login-token",
|
|
configured: true,
|
|
staffConfigured: true,
|
|
missing: [],
|
|
staffMissing: []
|
|
});
|
|
});
|
|
|
|
it("calls member list through the hotelStaff admin organization proxy by default", async () => {
|
|
const seen: Array<{ input: string; init?: RequestInit }> = [];
|
|
stubOrganizationEnv();
|
|
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
seen.push({ input: String(input), init });
|
|
return jsonResponse({
|
|
code: 0,
|
|
data: { records: [], total: 0, size: 10, current: 1, pages: 0 }
|
|
});
|
|
});
|
|
|
|
await listOrganizationMembers({ organizationId: "org_1", pageNum: 1, pageSize: 10 });
|
|
|
|
expect(seen[0].input).toBe("https://gateway.example.com/basic/adminOrganization/organizationMember/organizationMemberList");
|
|
expect(seen[0].init?.method).toBe("POST");
|
|
expect(seen[0].init?.headers).toMatchObject({
|
|
Authorization: "Bearer org-token",
|
|
tenantId: "1"
|
|
});
|
|
expect(seen[0].init?.headers).not.toMatchObject({ from: "Y" });
|
|
});
|
|
|
|
it("prefers the current login token when forwarding organization requests", async () => {
|
|
const seen: Array<{ input: string; init?: RequestInit }> = [];
|
|
stubOrganizationEnv();
|
|
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
seen.push({ input: String(input), init });
|
|
return jsonResponse({
|
|
code: 0,
|
|
data: { records: [], total: 0, size: 10, current: 1, pages: 0 }
|
|
});
|
|
});
|
|
|
|
await listOrganizationMembers(
|
|
{ organizationId: "org_1", pageNum: 1, pageSize: 10 },
|
|
{ accessToken: "login-token" }
|
|
);
|
|
|
|
expect(seen[0].init?.headers).toMatchObject({
|
|
Authorization: "Bearer login-token",
|
|
tenantId: "1"
|
|
});
|
|
});
|
|
|
|
it("creates organization groups through the documented group endpoint", async () => {
|
|
const seen: Array<{ input: string; init?: RequestInit }> = [];
|
|
stubOrganizationEnv();
|
|
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
seen.push({ input: String(input), init });
|
|
return jsonResponse({ code: 0, data: true });
|
|
});
|
|
|
|
await createOrganizationGroup({
|
|
organizationId: "org_1",
|
|
groupName: "客房部",
|
|
groupDesc: "处理客房相关工单"
|
|
}, { accessToken: "login-token" });
|
|
|
|
expect(seen[0].input).toBe("https://gateway.example.com/basic/organizationGroup/createOrganizationGroup");
|
|
expect(seen[0].init?.headers).toMatchObject({
|
|
Authorization: "Bearer login-token",
|
|
tenantId: "1"
|
|
});
|
|
expect(JSON.parse(String(seen[0].init?.body))).toMatchObject({
|
|
organizationId: "org_1",
|
|
groupName: "客房部",
|
|
groupDesc: "处理客房相关工单"
|
|
});
|
|
});
|
|
|
|
it("allows overriding the member list path exposed by the gateway", async () => {
|
|
const seen: Array<{ input: string; init?: RequestInit }> = [];
|
|
stubOrganizationEnv();
|
|
vi.stubEnv("ZHINIAN_ORG_MEMBER_LIST_PATH", "/adminOrganization/organizationMember/list");
|
|
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
seen.push({ input: String(input), init });
|
|
return jsonResponse({
|
|
code: 0,
|
|
data: { records: [], total: 0, size: 10, current: 1, pages: 0 }
|
|
});
|
|
});
|
|
|
|
await listOrganizationMembers({ organizationId: "org_1", pageNum: 1, pageSize: 10 });
|
|
|
|
expect(seen[0].input).toBe("https://gateway.example.com/basic/adminOrganization/organizationMember/list");
|
|
expect(seen[0].init?.headers).not.toMatchObject({ from: "Y" });
|
|
});
|
|
|
|
it("keeps the inner marker when the member list path points directly at the basic service", async () => {
|
|
const seen: Array<{ input: string; init?: RequestInit }> = [];
|
|
stubOrganizationEnv();
|
|
vi.stubEnv("ZHINIAN_ORG_MEMBER_LIST_PATH", "/organizationMember/organizationMemberList");
|
|
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
seen.push({ input: String(input), init });
|
|
return jsonResponse({
|
|
code: 0,
|
|
data: { records: [], total: 0, size: 10, current: 1, pages: 0 }
|
|
});
|
|
});
|
|
|
|
await listOrganizationMembers({ organizationId: "org_1", pageNum: 1, pageSize: 10 });
|
|
|
|
expect(seen[0].input).toBe("https://gateway.example.com/basic/organizationMember/organizationMemberList");
|
|
expect(seen[0].init?.headers).toMatchObject({ from: "Y" });
|
|
});
|
|
|
|
it("marks static resource errors as missing organization routes", async () => {
|
|
stubOrganizationEnv();
|
|
vi.stubGlobal("fetch", async () => new Response(JSON.stringify({
|
|
code: 1,
|
|
msg: "No static resource organizationMember/organizationMemberList."
|
|
}), {
|
|
status: 404,
|
|
headers: { "Content-Type": "application/json" }
|
|
}));
|
|
|
|
try {
|
|
await listOrganizationMembers({ organizationId: "org_1", pageNum: 1, pageSize: 10 });
|
|
throw new Error("expected organization request to fail");
|
|
} catch (error) {
|
|
expect(isOrganizationRouteNotFound(error)).toBe(true);
|
|
expect(error).toMatchObject({
|
|
status: 404,
|
|
message: expect.stringContaining("ZHINIAN_ORG_MEMBER_LIST_PATH")
|
|
});
|
|
}
|
|
});
|
|
|
|
it("marks upstream administrator-role errors as organization permission denials", async () => {
|
|
stubOrganizationEnv();
|
|
vi.stubGlobal("fetch", async () => jsonResponse({
|
|
code: 1,
|
|
msg: "仅管理员角色允许调用"
|
|
}));
|
|
|
|
try {
|
|
await listOrganizationMembers({ organizationId: "org_1", pageNum: 1, pageSize: 10 });
|
|
throw new Error("expected organization request to fail");
|
|
} catch (error) {
|
|
expect(isOrganizationPermissionDenied(error)).toBe(true);
|
|
expect(isOrganizationRouteNotFound(error)).toBe(false);
|
|
expect(error).toMatchObject({
|
|
status: 502,
|
|
message: "仅管理员角色允许调用"
|
|
});
|
|
}
|
|
});
|
|
|
|
it("uses staff service endpoints for user creation and password reset", async () => {
|
|
const seen: Array<{ input: string; init?: RequestInit }> = [];
|
|
stubOrganizationEnv();
|
|
vi.stubGlobal("fetch", async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
seen.push({ input: String(input), init });
|
|
if (String(input).endsWith("/resetPlatformUserPassword")) return jsonResponse({ code: 0, data: true });
|
|
return jsonResponse({
|
|
code: 0,
|
|
data: {
|
|
userId: "10001",
|
|
username: "zhangsan",
|
|
phone: "13800000000",
|
|
tenantId: 1,
|
|
initialPassword: "Nianxx@123456"
|
|
}
|
|
});
|
|
});
|
|
|
|
const user = await addOrganizationMemberAndCreatePlatformUser({
|
|
tenantId: 1,
|
|
username: "zhangsan",
|
|
phone: "13800000000",
|
|
name: "张三",
|
|
memberName: "张三",
|
|
memberPhone: "13800000000",
|
|
roleId: "role_1",
|
|
organizationId: "org_1",
|
|
groupId: "group_1",
|
|
mustChangePassword: true
|
|
});
|
|
await resetPlatformUserPassword({
|
|
tenantId: 1,
|
|
userId: 10001,
|
|
newPassword: "Nianxx@654321",
|
|
mustChangePassword: true
|
|
});
|
|
|
|
expect(user.initialPassword).toBe("Nianxx@123456");
|
|
expect(seen[0].input).toBe("https://gateway.example.com/staff/adminOrganization/organizationMember/addOrganizationMemberAndCreatePlatformUser");
|
|
expect(seen[1].input).toBe("https://gateway.example.com/staff/adminPcUser/resetPlatformUserPassword");
|
|
expect(seen[0].init?.headers).toMatchObject({
|
|
Authorization: "Bearer staff-token",
|
|
tenantId: "1"
|
|
});
|
|
expect(JSON.parse(String(seen[1].init?.body))).toMatchObject({
|
|
tenantId: 1,
|
|
userId: 10001,
|
|
newPassword: "Nianxx@654321",
|
|
mustChangePassword: true
|
|
});
|
|
});
|
|
});
|
|
|
|
function stubOrganizationEnv() {
|
|
vi.stubEnv("ZHINIAN_ORG_API_BASE_URL", "https://gateway.example.com/basic");
|
|
vi.stubEnv("ZHINIAN_ORG_API_TOKEN", "org-token");
|
|
vi.stubEnv("ZHINIAN_STAFF_API_BASE_URL", "https://gateway.example.com/staff");
|
|
vi.stubEnv("ZHINIAN_STAFF_API_TOKEN", "staff-token");
|
|
vi.stubEnv("ZHINIAN_ORG_TENANT_ID", "1");
|
|
}
|
|
|
|
function jsonResponse(value: unknown) {
|
|
return new Response(JSON.stringify(value), {
|
|
status: 200,
|
|
headers: { "Content-Type": "application/json" }
|
|
});
|
|
}
|