ken_nogi/NodeSrv/apps/org-master-sync/test/keycloakClient.test.js
Kenichiro NOGI ce58cb4be4 初回コミット: dev配下(NodeSrv/Pleasanter等)をGitea管理下に統合
GitHub(nextgroup2706/ken_nogi)は今後使わず自社Gitea運用に切替え。
NodeSrvは旧リポジトリの履歴を破棄しファイルのみ統合(Dokploy用サービスアカウントは
別途mygit-admin/NodeSrv.gitに履歴あり)。notepmエクスポート(12GB)とPleasanter
インストーラzip(208MB)はサイズが大きいため.gitignoreで除外。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-04 15:37:06 +09:00

188 lines
11 KiB
JavaScript

const { test } = require("node:test");
const assert = require("node:assert");
const {
fetchAccessToken, getUserProfile, updateUserProfile, findUserByAttribute, findUserByEmail,
createUser, updateUser, deleteUser, getAllGroups, createGroup, addUserToGroup,
getGroup, getGroupChildren, createSubGroup, updateGroup,
getFederatedIdentities, linkFederatedIdentity,
} = require("../src/lib/keycloakClient");
test("fetchAccessToken posts client_credentials grant and returns the access_token", async () => {
let capturedUrl, capturedBody;
const fetchImpl = async (url, opts) => {
capturedUrl = url;
capturedBody = opts.body;
return { ok: true, json: async () => ({ access_token: "fake-token" }) };
};
const token = await fetchAccessToken({
baseUrl: "https://kc.example.test", realm: "nexthd", clientId: "cid", clientSecret: "secret", fetchImpl,
});
assert.strictEqual(token, "fake-token");
assert.strictEqual(capturedUrl, "https://kc.example.test/realms/nexthd/protocol/openid-connect/token");
assert.ok(capturedBody.includes("grant_type=client_credentials"));
assert.ok(capturedBody.includes("client_id=cid"));
});
test("getUserProfile returns the attributes array from /users/profile", async () => {
const fetchImpl = async () => ({ ok: true, json: async () => ({ attributes: [{ name: "email" }] }) });
const profile = await getUserProfile({ baseUrl: "https://kc.example.test", realm: "nexthd", accessToken: "t", fetchImpl });
assert.strictEqual(profile.attributes[0].name, "email");
});
test("updateUserProfile PUTs the full profile object to /users/profile", async () => {
let capturedUrl, capturedMethod, capturedBody;
const fetchImpl = async (url, opts) => { capturedUrl = url; capturedMethod = opts.method; capturedBody = JSON.parse(opts.body); return { ok: true }; };
await updateUserProfile({ baseUrl: "https://kc.example.test", realm: "nexthd", accessToken: "t", profile: { attributes: [{ name: "x" }] }, fetchImpl });
assert.strictEqual(capturedMethod, "PUT");
assert.ok(capturedUrl.endsWith("/users/profile"));
assert.deepStrictEqual(capturedBody, { attributes: [{ name: "x" }] });
});
test("findUserByAttribute queries with q=attrName:attrValue and returns the first match or null", async () => {
let capturedUrl;
const fetchImpl = async (url) => {
capturedUrl = url;
return { ok: true, json: async () => (url.includes("hit") ? [{ id: "u1" }] : []) };
};
const found = await findUserByAttribute({ baseUrl: "https://kc.example.test", realm: "nexthd", accessToken: "t", attrName: "pleasanterResultId", attrValue: "hit-123", fetchImpl });
assert.strictEqual(found.id, "u1");
assert.ok(capturedUrl.includes("q=pleasanterResultId%3Ahit-123"));
const notFound = await findUserByAttribute({ baseUrl: "https://kc.example.test", realm: "nexthd", accessToken: "t", attrName: "pleasanterResultId", attrValue: "miss-456", fetchImpl });
assert.strictEqual(notFound, null);
});
test("findUserByEmail queries with email + exact=true and returns the first match or null", async () => {
let capturedUrl;
const fetchImpl = async (url) => { capturedUrl = url; return { ok: true, json: async () => [{ id: "u2" }] }; };
const found = await findUserByEmail({ baseUrl: "https://kc.example.test", realm: "nexthd", accessToken: "t", email: "taro@example.com", fetchImpl });
assert.strictEqual(found.id, "u2");
assert.ok(capturedUrl.includes("email=taro%40example.com&exact=true"));
});
test("createUser posts to /users and returns the new id from the Location header", async () => {
const fetchImpl = async () => ({
ok: true,
headers: { get: (name) => (name.toLowerCase() === "location" ? "https://kc.example.test/admin/realms/nexthd/users/new-id-123" : null) },
});
const id = await createUser({ baseUrl: "https://kc.example.test", realm: "nexthd", accessToken: "t", user: { username: "u1" }, fetchImpl });
assert.strictEqual(id, "new-id-123");
});
test("updateUser puts the full user object to /users/{id}", async () => {
let capturedUrl, capturedMethod, capturedBody;
const fetchImpl = async (url, opts) => { capturedUrl = url; capturedMethod = opts.method; capturedBody = JSON.parse(opts.body); return { ok: true }; };
await updateUser({ baseUrl: "https://kc.example.test", realm: "nexthd", accessToken: "t", userId: "u1", user: { username: "u1", email: "a@b.com", enabled: true }, fetchImpl });
assert.strictEqual(capturedMethod, "PUT");
assert.ok(capturedUrl.endsWith("/users/u1"));
assert.deepStrictEqual(capturedBody, { username: "u1", email: "a@b.com", enabled: true });
});
test("deleteUser sends DELETE to /users/{id}", async () => {
let capturedUrl, capturedMethod;
const fetchImpl = async (url, opts) => { capturedUrl = url; capturedMethod = opts.method; return { ok: true }; };
await deleteUser({ baseUrl: "https://kc.example.test", realm: "nexthd", accessToken: "t", userId: "u1", fetchImpl });
assert.strictEqual(capturedMethod, "DELETE");
assert.ok(capturedUrl.endsWith("/users/u1"));
});
test("getAllGroups returns the group array from /groups", async () => {
const fetchImpl = async () => ({ ok: true, json: async () => [{ id: "g1", name: "組織:情報システム部" }] });
const groups = await getAllGroups({ baseUrl: "https://kc.example.test", realm: "nexthd", accessToken: "t", fetchImpl });
assert.strictEqual(groups[0].name, "組織:情報システム部");
});
test("createGroup posts to /groups and returns the new id from the Location header", async () => {
const fetchImpl = async () => ({
ok: true,
headers: { get: (name) => (name.toLowerCase() === "location" ? "https://kc.example.test/admin/realms/nexthd/groups/g-new" : null) },
});
const id = await createGroup({ baseUrl: "https://kc.example.test", realm: "nexthd", accessToken: "t", name: "役職:課長", fetchImpl });
assert.strictEqual(id, "g-new");
});
test("createGroup includes attributes in the body when provided (org-unit hierarchy uses this for orgUnitId)", async () => {
let capturedBody;
const fetchImpl = async (url, opts) => {
capturedBody = JSON.parse(opts.body);
return { ok: true, headers: { get: () => "https://kc.example.test/admin/realms/nexthd/groups/g-new" } };
};
await createGroup({ baseUrl: "https://kc.example.test", realm: "nexthd", accessToken: "t", name: "組織", attributes: { orgUnitId: ["ou-1"] }, fetchImpl });
assert.deepStrictEqual(capturedBody, { name: "組織", attributes: { orgUnitId: ["ou-1"] } });
});
test("getGroup GETs /groups/{id} and returns the group object", async () => {
let capturedUrl;
const fetchImpl = async (url) => { capturedUrl = url; return { ok: true, json: async () => ({ id: "g1", name: "西東京建設", attributes: { orgUnitId: ["ou-1"] } }) }; };
const group = await getGroup({ baseUrl: "https://kc.example.test", realm: "nexthd", accessToken: "t", groupId: "g1", fetchImpl });
assert.ok(capturedUrl.endsWith("/groups/g1"));
assert.strictEqual(group.name, "西東京建設");
});
test("getGroupChildren GETs /groups/{id}/children and returns the child group array", async () => {
let capturedUrl;
const fetchImpl = async (url) => { capturedUrl = url; return { ok: true, json: async () => [{ id: "g2", name: "営業設計部" }] }; };
const children = await getGroupChildren({ baseUrl: "https://kc.example.test", realm: "nexthd", accessToken: "t", groupId: "g1", fetchImpl });
assert.ok(capturedUrl.endsWith("/groups/g1/children?max=1000"));
assert.strictEqual(children[0].name, "営業設計部");
});
test("createSubGroup posts to /groups/{parentId}/children with attributes and returns the new id", async () => {
let capturedUrl, capturedBody;
const fetchImpl = async (url, opts) => {
capturedUrl = url;
capturedBody = JSON.parse(opts.body);
return { ok: true, headers: { get: () => "https://kc.example.test/admin/realms/nexthd/groups/g-child" } };
};
const id = await createSubGroup({
baseUrl: "https://kc.example.test", realm: "nexthd", accessToken: "t",
parentId: "g1", name: "営業設計部", attributes: { orgUnitId: ["ou-2"] }, fetchImpl,
});
assert.ok(capturedUrl.endsWith("/groups/g1/children"));
assert.deepStrictEqual(capturedBody, { name: "営業設計部", attributes: { orgUnitId: ["ou-2"] } });
assert.strictEqual(id, "g-child");
});
test("updateGroup PUTs the full group object to /groups/{id}", async () => {
let capturedUrl, capturedMethod, capturedBody;
const fetchImpl = async (url, opts) => { capturedUrl = url; capturedMethod = opts.method; capturedBody = JSON.parse(opts.body); return { ok: true }; };
await updateGroup({ baseUrl: "https://kc.example.test", realm: "nexthd", accessToken: "t", groupId: "g1", group: { name: "西東京建設(改称)", attributes: { orgUnitId: ["ou-1"] } }, fetchImpl });
assert.strictEqual(capturedMethod, "PUT");
assert.ok(capturedUrl.endsWith("/groups/g1"));
assert.deepStrictEqual(capturedBody, { name: "西東京建設(改称)", attributes: { orgUnitId: ["ou-1"] } });
});
test("addUserToGroup PUTs to /users/{userId}/groups/{groupId} with no body", async () => {
let capturedUrl, capturedMethod;
const fetchImpl = async (url, opts) => { capturedUrl = url; capturedMethod = opts.method; return { ok: true }; };
await addUserToGroup({ baseUrl: "https://kc.example.test", realm: "nexthd", accessToken: "t", userId: "u1", groupId: "g1", fetchImpl });
assert.strictEqual(capturedMethod, "PUT");
assert.ok(capturedUrl.endsWith("/users/u1/groups/g1"));
});
test("callApi throws with status and body text when the response is not ok", async () => {
const fetchImpl = async () => ({ ok: false, status: 403, text: async () => "Forbidden" });
await assert.rejects(() => getAllGroups({ baseUrl: "https://kc.example.test", realm: "nexthd", accessToken: "t", fetchImpl }), /403/);
});
test("getFederatedIdentities returns the federated-identity array for a user", async () => {
const fetchImpl = async () => ({ ok: true, json: async () => [{ identityProvider: "lineworks", userId: "taro@next-hd.co.jp" }] });
const identities = await getFederatedIdentities({ baseUrl: "https://kc.example.test", realm: "nexthd", accessToken: "t", userId: "u1", fetchImpl });
assert.strictEqual(identities[0].identityProvider, "lineworks");
});
test("linkFederatedIdentity POSTs identityProvider/userId/userName to /users/{userId}/federated-identity/{provider}", async () => {
let capturedUrl, capturedMethod, capturedBody;
const fetchImpl = async (url, opts) => { capturedUrl = url; capturedMethod = opts.method; capturedBody = JSON.parse(opts.body); return { ok: true }; };
await linkFederatedIdentity({
baseUrl: "https://kc.example.test", realm: "nexthd", accessToken: "t",
userId: "u1", provider: "lineworks", identityUserId: "taro@next-hd.co.jp", fetchImpl,
});
assert.strictEqual(capturedMethod, "POST");
assert.ok(capturedUrl.endsWith("/users/u1/federated-identity/lineworks"));
assert.deepStrictEqual(capturedBody, { identityProvider: "lineworks", userId: "taro@next-hd.co.jp", userName: "taro@next-hd.co.jp" });
});