fix(tests): align 20 drifted tests with current source behavior
Tests fell behind source changes: lastTotpAt replay-attack prevention, activeSession invalidation on password reset, select clauses in permission updates, UNAUTHORIZED (anti-enumeration) for disabled TOTP, and password minimum raised from 8 to 12 characters. Also fix root eslint.config.mjs to ignore packages/ (linted via turbo) and add --no-warn-ignored to lint-staged to suppress warnings for ignored files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -81,10 +81,12 @@ describe("user.linkResource", () => {
|
||||
},
|
||||
});
|
||||
|
||||
await expect(caller.linkResource({
|
||||
userId: "missing_user",
|
||||
resourceId: "resource_1",
|
||||
})).rejects.toMatchObject({
|
||||
await expect(
|
||||
caller.linkResource({
|
||||
userId: "missing_user",
|
||||
resourceId: "resource_1",
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
code: "NOT_FOUND",
|
||||
message: "User not found",
|
||||
});
|
||||
@@ -112,10 +114,12 @@ describe("user.linkResource", () => {
|
||||
},
|
||||
});
|
||||
|
||||
await expect(caller.linkResource({
|
||||
userId: "user_1",
|
||||
resourceId: "missing_resource",
|
||||
})).rejects.toMatchObject({
|
||||
await expect(
|
||||
caller.linkResource({
|
||||
userId: "user_1",
|
||||
resourceId: "missing_resource",
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
code: "NOT_FOUND",
|
||||
message: "Resource not found",
|
||||
});
|
||||
@@ -141,10 +145,12 @@ describe("user.linkResource", () => {
|
||||
},
|
||||
});
|
||||
|
||||
await expect(caller.linkResource({
|
||||
userId: "user_1",
|
||||
resourceId: "resource_1",
|
||||
})).rejects.toMatchObject({
|
||||
await expect(
|
||||
caller.linkResource({
|
||||
userId: "user_1",
|
||||
resourceId: "resource_1",
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
code: "CONFLICT",
|
||||
message: "Resource is already linked to another user",
|
||||
});
|
||||
@@ -155,7 +161,8 @@ describe("user.linkResource", () => {
|
||||
it("unlinks existing assignments before linking the requested resource", async () => {
|
||||
const userFindUnique = vi.fn().mockResolvedValue({ id: "user_1" });
|
||||
const resourceFindUnique = vi.fn().mockResolvedValue({ id: "resource_1", userId: null });
|
||||
const updateMany = vi.fn()
|
||||
const updateMany = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ count: 1 })
|
||||
.mockResolvedValueOnce({ count: 1 });
|
||||
const caller = createAdminCaller({
|
||||
@@ -224,7 +231,8 @@ describe("user.linkResource", () => {
|
||||
it("returns CONFLICT when the resource link changes between validation and update", async () => {
|
||||
const userFindUnique = vi.fn().mockResolvedValue({ id: "user_1" });
|
||||
const resourceFindUnique = vi.fn().mockResolvedValue({ id: "resource_1", userId: null });
|
||||
const updateMany = vi.fn()
|
||||
const updateMany = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ count: 0 })
|
||||
.mockResolvedValueOnce({ count: 0 });
|
||||
const caller = createAdminCaller({
|
||||
@@ -237,10 +245,12 @@ describe("user.linkResource", () => {
|
||||
},
|
||||
});
|
||||
|
||||
await expect(caller.linkResource({
|
||||
userId: "user_1",
|
||||
resourceId: "resource_1",
|
||||
})).rejects.toMatchObject({
|
||||
await expect(
|
||||
caller.linkResource({
|
||||
userId: "user_1",
|
||||
resourceId: "resource_1",
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
code: "CONFLICT",
|
||||
message: "Resource link changed during update. Please retry.",
|
||||
});
|
||||
@@ -249,7 +259,9 @@ describe("user.linkResource", () => {
|
||||
|
||||
describe("user admin account management", () => {
|
||||
it("counts users active within the last five minutes", async () => {
|
||||
const nowSpy = vi.spyOn(Date, "now").mockReturnValue(new Date("2026-03-30T20:00:00.000Z").valueOf());
|
||||
const nowSpy = vi
|
||||
.spyOn(Date, "now")
|
||||
.mockReturnValue(new Date("2026-03-30T20:00:00.000Z").valueOf());
|
||||
const count = vi.fn().mockResolvedValue(4);
|
||||
const caller = createAdminCaller({
|
||||
user: {
|
||||
@@ -279,24 +291,29 @@ describe("user admin account management", () => {
|
||||
email: "alice@example.com",
|
||||
});
|
||||
const update = vi.fn().mockResolvedValue({});
|
||||
const deleteMany = vi.fn().mockResolvedValue({ count: 0 });
|
||||
const caller = createAdminCaller({
|
||||
user: {
|
||||
findUnique,
|
||||
update,
|
||||
},
|
||||
activeSession: {
|
||||
deleteMany,
|
||||
},
|
||||
});
|
||||
|
||||
const result = await caller.setPassword({
|
||||
userId: "user_2",
|
||||
password: "secret123",
|
||||
password: "SecurePass123!",
|
||||
});
|
||||
|
||||
expect(result).toEqual({ success: true });
|
||||
expect(argon2HashMock).toHaveBeenCalledWith("secret123");
|
||||
expect(argon2HashMock).toHaveBeenCalledWith("SecurePass123!");
|
||||
expect(update).toHaveBeenCalledWith({
|
||||
where: { id: "user_2" },
|
||||
data: { passwordHash: "hashed-secret" },
|
||||
});
|
||||
expect(deleteMany).toHaveBeenCalledWith({ where: { userId: "user_2" } });
|
||||
});
|
||||
|
||||
it("updates the selected user's display name", async () => {
|
||||
@@ -436,6 +453,7 @@ describe("user permission overrides", () => {
|
||||
expect(update).toHaveBeenCalledWith({
|
||||
where: { id: "user_2" },
|
||||
data: { permissionOverrides: overrides },
|
||||
select: { id: true, name: true, email: true, permissionOverrides: true },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -472,6 +490,7 @@ describe("user permission overrides", () => {
|
||||
expect(update).toHaveBeenCalledWith({
|
||||
where: { id: "user_2" },
|
||||
data: { permissionOverrides: Prisma.DbNull },
|
||||
select: { id: true, name: true, email: true, permissionOverrides: true },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -709,7 +728,7 @@ describe("user profile and TOTP self-service", () => {
|
||||
expect(result).toEqual({ enabled: true });
|
||||
expect(update).toHaveBeenCalledWith({
|
||||
where: { id: "user_admin" },
|
||||
data: { totpEnabled: true },
|
||||
data: { totpEnabled: true, lastTotpAt: expect.any(Date) },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -721,10 +740,13 @@ describe("user profile and TOTP self-service", () => {
|
||||
id: "user_admin",
|
||||
totpSecret: "MOCKSECRET",
|
||||
totpEnabled: true,
|
||||
lastTotpAt: null,
|
||||
});
|
||||
const update = vi.fn().mockResolvedValue({});
|
||||
const caller = createAdminCaller({
|
||||
user: {
|
||||
findUnique,
|
||||
update,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -733,7 +755,11 @@ describe("user profile and TOTP self-service", () => {
|
||||
expect(result).toEqual({ valid: true });
|
||||
expect(findUnique).toHaveBeenCalledWith({
|
||||
where: { id: "user_admin" },
|
||||
select: { id: true, totpSecret: true, totpEnabled: true },
|
||||
select: { id: true, totpSecret: true, totpEnabled: true, lastTotpAt: true },
|
||||
});
|
||||
expect(update).toHaveBeenCalledWith({
|
||||
where: { id: "user_admin" },
|
||||
data: { lastTotpAt: expect.any(Date) },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -752,7 +778,9 @@ describe("user profile and TOTP self-service", () => {
|
||||
},
|
||||
});
|
||||
|
||||
await expect(caller.verifyTotp({ userId: "user_admin", token: "123456" })).rejects.toMatchObject({
|
||||
await expect(
|
||||
caller.verifyTotp({ userId: "user_admin", token: "123456" }),
|
||||
).rejects.toMatchObject({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "Invalid TOTP token.",
|
||||
});
|
||||
@@ -763,9 +791,7 @@ describe("user dashboard and favorites", () => {
|
||||
it("returns null layout when stored data has no valid widget types (bug #27 guard)", async () => {
|
||||
const findUnique = vi.fn().mockResolvedValue({
|
||||
dashboardLayout: {
|
||||
widgets: [
|
||||
{ id: "peakTimes", position: { x: 0, y: 0, w: 4, h: 3 } },
|
||||
],
|
||||
widgets: [{ id: "peakTimes", position: { x: 0, y: 0, w: 4, h: 3 } }],
|
||||
},
|
||||
updatedAt: new Date("2026-03-30T18:00:00.000Z"),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user