|
|
|
@@ -173,7 +173,10 @@ async function seedUatCredentials(
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (existingAccount) {
|
|
|
|
|
// skip — already has credential account
|
|
|
|
|
// Re-hash and update the password (mirrors seed.ts behavior)
|
|
|
|
|
const { hashPassword } = await import("better-auth/crypto");
|
|
|
|
|
const passwordHash = await hashPassword(password);
|
|
|
|
|
existingAccount.password = passwordHash;
|
|
|
|
|
} else {
|
|
|
|
|
// Use Better-Auth's hashPassword so test helper matches production seed.ts
|
|
|
|
|
const { hashPassword } = await import("better-auth/crypto");
|
|
|
|
@@ -351,6 +354,49 @@ describe("seedUatCredentials — credential provisioning logic", () => {
|
|
|
|
|
expect(insertedAccounts).toHaveLength(0);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// ── AC-8: existing account password IS updated (not frozen at first-seed) ──
|
|
|
|
|
|
|
|
|
|
it("AC-8: re-seeding with a changed password env var updates the stored hash", async () => {
|
|
|
|
|
const ORIGINAL_PASSWORD = "original-password";
|
|
|
|
|
const ROTATED_PASSWORD = "rotated-password-456";
|
|
|
|
|
|
|
|
|
|
process.env.SEED_UAT_CUSTOMER_PASSWORD = ROTATED_PASSWORD;
|
|
|
|
|
|
|
|
|
|
const preExistingUsers: UserRow[] = [
|
|
|
|
|
{ id: "pre-existing-user", email: "uat-customer@groombook.dev", name: "UAT Customer", emailVerified: true },
|
|
|
|
|
];
|
|
|
|
|
// Account was created with the original password on first seed
|
|
|
|
|
const originalHash = await hashPassword(ORIGINAL_PASSWORD);
|
|
|
|
|
const preExistingAccounts: AccountRow[] = [
|
|
|
|
|
{
|
|
|
|
|
id: "pre-existing-acct",
|
|
|
|
|
accountId: "pre-existing-user",
|
|
|
|
|
providerId: "credential",
|
|
|
|
|
userId: "pre-existing-user",
|
|
|
|
|
password: originalHash,
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// Re-seed with the rotated password env var
|
|
|
|
|
await seedUatCredentials([UAT_ACCOUNTS[2]!], {
|
|
|
|
|
users: preExistingUsers,
|
|
|
|
|
accounts: preExistingAccounts,
|
|
|
|
|
staff: [],
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// No new user or account created
|
|
|
|
|
expect(insertedUsers).toHaveLength(0);
|
|
|
|
|
expect(insertedAccounts).toHaveLength(0);
|
|
|
|
|
|
|
|
|
|
// The pre-existing account's password WAS updated (not frozen at first-seed).
|
|
|
|
|
// hashPassword uses a random salt so we verify by format + that it is a new,
|
|
|
|
|
// different valid hash from the original.
|
|
|
|
|
const updatedAcct = preExistingAccounts[0]!;
|
|
|
|
|
expect(updatedAcct.password).toBeDefined();
|
|
|
|
|
expect(updatedAcct.password).toMatch(/^[a-f0-9]{32}:[a-f0-9]{128}$/);
|
|
|
|
|
expect(updatedAcct.password).not.toBe(originalHash); // it actually changed
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// ── AC-6: missing env var skips with warning ────────────────────────────────
|
|
|
|
|
|
|
|
|
|
it("AC-6: missing SEED_UAT_*_PASSWORD env var skips that account (no error)", async () => {
|
|
|
|
|