Add comprehensive e2e tests for API procedures with 100% coverage

- Add admin.test.ts: Tests for superuser operations (users, orgs, sites)
- Add orgs.test.ts: Tests for org management, members, invites, sites
- Expand me.test.ts: Add API tokens, invites, authMiddleware error paths
- Expand auth.test.ts: Add loginRequestMiddleware tests, weak password test fix

Bug fixes:
- Fix countOwners() in orgs/helpers.ts to convert PostgreSQL bigint to number
- Fix signup race condition by handling unique constraint violations gracefully

All 283 tests pass with 100% function coverage on procedures.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
igm
2026-01-12 12:53:19 +08:00
parent 44a480179b
commit ebc85af62c
7 changed files with 4461 additions and 57 deletions

View File

@@ -52,17 +52,26 @@ export async function signupWithPassword(
// Hash password
const passwordHash = await hashPassword(password);
// Create user
const user = await db
.insertInto("users")
.values({
email,
password_hash: passwordHash,
})
.returning(["id"])
.executeTakeFirstOrThrow();
// Create user (handle race condition if concurrent signup with same email)
try {
const user = await db
.insertInto("users")
.values({
email,
password_hash: passwordHash,
})
.returning(["id"])
.executeTakeFirstOrThrow();
return user.id;
return user.id;
} catch (error) {
// Handle duplicate email (unique constraint violation)
// Use generic error to prevent email enumeration
if (error instanceof Error && error.message.includes("users_email_key")) {
throw new ORPCError("BAD_REQUEST", { message: "Unable to create account" });
}
throw error;
}
}
/**
@@ -146,55 +155,64 @@ export async function signupWithPasskey(
});
}
// Create user and passkey in a transaction
const result = await db.transaction().execute(async (trx) => {
// Create user
const user = await trx
.insertInto("users")
.values({
email,
password_hash: null,
})
.returning(["id"])
.executeTakeFirstOrThrow();
// Create user and passkey in a transaction (handle race condition if concurrent signup)
try {
const result = await db.transaction().execute(async (trx) => {
// Create user
const user = await trx
.insertInto("users")
.values({
email,
password_hash: null,
})
.returning(["id"])
.executeTakeFirstOrThrow();
const newUserId = user.id;
const newUserId = user.id;
// Get friendly name from AAGUID
const guidName = KNOWN_AAGUIDS[registrationInfo.aaguid];
const passkeyName = guidName ?? "Default";
// Get friendly name from AAGUID
const guidName = KNOWN_AAGUIDS[registrationInfo.aaguid];
const passkeyName = guidName ?? "Default";
// Store the passkey
const { credential, credentialDeviceType, credentialBackedUp } =
registrationInfo;
// Store the passkey
const { credential, credentialDeviceType, credentialBackedUp } =
registrationInfo;
await trx
.insertInto("passkeys")
.values({
user_id: newUserId,
credential_id: Buffer.from(credential.id, "base64url"),
public_key: Buffer.from(credential.publicKey),
webauthn_user_id: options.user.id,
counter: BigInt(credential.counter),
device_type: credentialDeviceType as "singleDevice" | "multiDevice",
backup_eligible: registrationInfo.credentialBackedUp,
backup_status: credentialBackedUp,
transports: JSON.stringify(response.response.transports ?? []),
rpid: rpInfo.rpID,
name: passkeyName,
})
.execute();
await trx
.insertInto("passkeys")
.values({
user_id: newUserId,
credential_id: Buffer.from(credential.id, "base64url"),
public_key: Buffer.from(credential.publicKey),
webauthn_user_id: options.user.id,
counter: BigInt(credential.counter),
device_type: credentialDeviceType as "singleDevice" | "multiDevice",
backup_eligible: registrationInfo.credentialBackedUp,
backup_status: credentialBackedUp,
transports: JSON.stringify(response.response.transports ?? []),
rpid: rpInfo.rpID,
name: passkeyName,
})
.execute();
// Delete the challenge
await trx
.deleteFrom("webauthn_challenges")
.where("id", "=", String(challengeId))
.execute();
// Delete the challenge
await trx
.deleteFrom("webauthn_challenges")
.where("id", "=", String(challengeId))
.execute();
return { userId: newUserId };
});
return { userId: newUserId };
});
return result.userId;
return result.userId;
} catch (error) {
// Handle duplicate email (unique constraint violation)
// Use generic error to prevent email enumeration
if (error instanceof Error && error.message.includes("users_email_key")) {
throw new ORPCError("BAD_REQUEST", { message: "Unable to create account" });
}
throw error;
}
}
/**
@@ -241,7 +259,7 @@ export const signup = os.auth.signup.handler(async ({ input, context }) => {
);
userId = await signupWithPasskey(context.db, email, passkeyInfo, rpInfo);
} else {
// Should never reach here due to schema validation
// Unreachable - schema validation requires password or passkeyInfo
throw new ORPCError("BAD_REQUEST", {
message: "Either password or passkeyInfo is required",
});