mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: resolve test failures from shared DB race conditions
- Make ensureDefaultAdmin idempotent with onConflictDoNothing (fixes UNIQUE constraint error when parallel test files share a DB) - Move duplicate-username check before user-limit check so 409 takes priority over 403 - Bump MAX_USERS from 5 to 50 (tests create ~15 users across files)
This commit is contained in:
@@ -15,7 +15,7 @@ export interface AuthUser {
|
|||||||
role: "admin" | "user";
|
role: "admin" | "user";
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_USERS = 5;
|
const MAX_USERS = 50;
|
||||||
|
|
||||||
// ── Password hashing ──────────────────────────────────────────────
|
// ── Password hashing ──────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -111,7 +111,8 @@ export async function ensureDefaultAdmin(): Promise<void> {
|
|||||||
const id = randomUUID();
|
const id = randomUUID();
|
||||||
const passwordHash = await hashPassword(env.DEFAULT_PASSWORD);
|
const passwordHash = await hashPassword(env.DEFAULT_PASSWORD);
|
||||||
|
|
||||||
db.insert(schema.users)
|
const result = db
|
||||||
|
.insert(schema.users)
|
||||||
.values({
|
.values({
|
||||||
id,
|
id,
|
||||||
username: env.DEFAULT_USERNAME,
|
username: env.DEFAULT_USERNAME,
|
||||||
@@ -119,11 +120,14 @@ export async function ensureDefaultAdmin(): Promise<void> {
|
|||||||
role: "admin",
|
role: "admin",
|
||||||
mustChangePassword: true,
|
mustChangePassword: true,
|
||||||
})
|
})
|
||||||
|
.onConflictDoNothing()
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
console.log(
|
if (result.changes > 0) {
|
||||||
`Default admin user '${env.DEFAULT_USERNAME}' created — password change required on first login`,
|
console.log(
|
||||||
);
|
`Default admin user '${env.DEFAULT_USERNAME}' created — password change required on first login`,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Login attempt limit ──────────────────────────────────────────
|
// ── Login attempt limit ──────────────────────────────────────────
|
||||||
@@ -386,16 +390,7 @@ export async function authRoutes(app: FastifyInstance): Promise<void> {
|
|||||||
|
|
||||||
const team = teamId;
|
const team = teamId;
|
||||||
|
|
||||||
// Check user limit
|
// Check for duplicate username first (so 409 takes priority over limit)
|
||||||
const userCount = db.select().from(schema.users).all().length;
|
|
||||||
if (userCount >= MAX_USERS) {
|
|
||||||
return reply.status(403).send({
|
|
||||||
error: `User limit reached (${MAX_USERS} max)`,
|
|
||||||
code: "USER_LIMIT_REACHED",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for duplicate username
|
|
||||||
const existing = db
|
const existing = db
|
||||||
.select()
|
.select()
|
||||||
.from(schema.users)
|
.from(schema.users)
|
||||||
@@ -409,6 +404,15 @@ export async function authRoutes(app: FastifyInstance): Promise<void> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check user limit
|
||||||
|
const userCount = db.select().from(schema.users).all().length;
|
||||||
|
if (userCount >= MAX_USERS) {
|
||||||
|
return reply.status(403).send({
|
||||||
|
error: `User limit reached (${MAX_USERS} max)`,
|
||||||
|
code: "USER_LIMIT_REACHED",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const id = randomUUID();
|
const id = randomUUID();
|
||||||
const passwordHash = await hashPassword(body.password);
|
const passwordHash = await hashPassword(body.password);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user