Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/apps/web/app/modules/ipAddress/services/IpAddressService.server.test.ts
T

134 lines
4.9 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
vi.mock("@hivegrid/db/pretenant-security.server", () => ({
appendIpAddressLogCapability: vi.fn(),
findIpAddressCapability: vi.fn(),
upsertIpAddressCapability: vi.fn(),
}));
vi.mock("~/utils/cache.server", () => ({
cachified: vi.fn(({ getFreshValue }) => getFreshValue()),
clearCacheKey: vi.fn(),
}));
vi.mock("~/utils/server/IpUtils", () => ({ getTrustedClientIpOrNull: vi.fn(() => "203.0.113.10") }));
vi.mock("~/utils/db/blacklist.db.server", () => ({
addBlacklistAttempt: vi.fn(),
addToBlacklist: vi.fn(),
findInBlacklist: vi.fn(() => null),
}));
// AD-22: Keep GeoIP and hashing hermetic while preserving deterministic hashes.
vi.mock("./geoip.server", () => ({
hashIp: (ip: string) => `hash-${ip.length}`,
lookupCountry: vi.fn(async () => null),
}));
import { appendIpAddressLogCapability, findIpAddressCapability, upsertIpAddressCapability } from "@hivegrid/db/pretenant-security.server";
import { addBlacklistAttempt, findInBlacklist } from "~/utils/db/blacklist.db.server";
import IpAddressServiceServer from "./IpAddressService.server";
const RAW_IP = "203.0.113.10";
const HASHED_IP = `hash-${RAW_IP.length}`;
const ipRecord = {
id: "ip_1",
ip: HASHED_IP,
provider: "",
type: "",
countryCode: "",
countryName: "",
regionCode: "",
regionName: "",
city: "",
zipCode: "",
latitude: null,
longitude: null,
metadata: "null",
createdAt: new Date("2026-01-01T00:00:00.000Z"),
updatedAt: new Date("2026-01-01T00:00:00.000Z"),
} as Awaited<ReturnType<typeof upsertIpAddressCapability>>;
const appendLogMock = vi.mocked(appendIpAddressLogCapability);
const findIpAddressMock = vi.mocked(findIpAddressCapability);
const upsertIpAddressMock = vi.mocked(upsertIpAddressCapability);
const findInBlacklistMock = vi.mocked(findInBlacklist);
const addBlacklistAttemptMock = vi.mocked(addBlacklistAttempt);
describe("IpAddressServiceServer", () => {
beforeEach(() => {
vi.clearAllMocks();
findInBlacklistMock.mockResolvedValue(null);
findIpAddressMock.mockResolvedValue(null);
upsertIpAddressMock.mockResolvedValue(ipRecord);
appendLogMock.mockResolvedValue({ id: "log_1" } as Awaited<ReturnType<typeof appendIpAddressLogCapability>>);
});
it("persists only the IP hash through pre-tenant capabilities (AD-22)", async () => {
const request = new Request("https://admin.hivegrid.de/login");
await expect(IpAddressServiceServer.log(request, { action: "login", description: "admin@example.com" })).resolves.toBeUndefined();
expect(findIpAddressMock).toHaveBeenCalledWith(HASHED_IP);
expect(upsertIpAddressMock).toHaveBeenCalledWith(
expect.objectContaining({
ip: HASHED_IP,
metadata: "null",
})
);
expect(appendLogMock).toHaveBeenCalledWith({
ipAddressId: "ip_1",
ip: HASHED_IP,
url: "/login",
action: "login",
description: "admin@example.com",
metadata: null,
error: null,
success: true,
});
expect(JSON.stringify(upsertIpAddressMock.mock.calls)).not.toContain(RAW_IP);
expect(JSON.stringify(appendLogMock.mock.calls)).not.toContain(RAW_IP);
});
it("does not replace the request IP with a fixed development address", async () => {
const request = new Request("https://admin.hivegrid.de/login");
await expect(IpAddressServiceServer.log(request, { action: "login", description: "admin@example.com" })).resolves.toBeUndefined();
expect(upsertIpAddressMock).toHaveBeenCalledWith(expect.objectContaining({ ip: HASHED_IP }));
expect(upsertIpAddressMock).not.toHaveBeenCalledWith(expect.objectContaining({ ip: "hash-14" }));
});
it("persists stable blacklisted error codes with a hashed IP", async () => {
findInBlacklistMock.mockResolvedValue({ id: "blacklist_1" } as never);
const request = new Request("https://admin.hivegrid.de/login");
await expect(IpAddressServiceServer.log(request, { action: "login", description: "admin@example.com" })).rejects.toThrow("blacklisted");
expect(addBlacklistAttemptMock).toHaveBeenCalledWith({ id: "blacklist_1" });
expect(appendLogMock).toHaveBeenCalledWith(
expect.objectContaining({
ip: HASHED_IP,
error: "blacklisted",
success: false,
})
);
});
it("persists stable unauthorized codes for provider-rejected IPs", async () => {
findIpAddressMock.mockResolvedValue({
...ipRecord,
provider: "ipapi.is",
metadata: JSON.stringify({ is_crawler: true }),
});
const request = new Request("https://admin.hivegrid.de/login");
await expect(IpAddressServiceServer.log(request, { action: "login", description: "admin@example.com" })).rejects.toThrow("unauthorized");
expect(upsertIpAddressMock).not.toHaveBeenCalled();
expect(appendLogMock).toHaveBeenCalledWith(
expect.objectContaining({
ip: HASHED_IP,
error: "unauthorized",
success: false,
})
);
});
});