feat: add admin accounts and image templates
This commit is contained in:
258
tests/organization-client.test.ts
Normal file
258
tests/organization-client.test.ts
Normal file
@@ -0,0 +1,258 @@
|
||||
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" }
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user