mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-13 21:01:21 +08:00
remove old tests
Some checks failed
DB migrations are backwards-compatible / Check if migrations changed (push) Has been cancelled
DB migrations are backwards-compatible / Test migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migrations are backwards-compatible / No migration changes (skipped) (push) Has been cancelled
Some checks failed
DB migrations are backwards-compatible / Check if migrations changed (push) Has been cancelled
DB migrations are backwards-compatible / Test migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migrations are backwards-compatible / No migration changes (skipped) (push) Has been cancelled
This commit is contained in:
parent
c91998ef73
commit
64d9d93f49
@ -406,380 +406,5 @@ describe.sequential('External DB Sync - Race Condition Tests', () => {
|
||||
},
|
||||
LOCAL_TEST_TIMEOUT,
|
||||
);
|
||||
|
||||
/**
|
||||
* Scenario 2:
|
||||
* First transaction commits, then poller runs.
|
||||
* Poller should pick up Transaction 1 and sequenceId should increase.
|
||||
*/
|
||||
test(
|
||||
'Poller picks up first committed transaction',
|
||||
async () => {
|
||||
const dbName = 'race_after_first_commit_test';
|
||||
const { externalClient, user } =
|
||||
await setupExternalDbWithBaseline(dbName);
|
||||
|
||||
const internalDbUrl = makeInternalDbUrl();
|
||||
const internalClient = new Client({ connectionString: internalDbUrl });
|
||||
|
||||
await internalClient.connect();
|
||||
|
||||
try {
|
||||
// Commit Transaction 1
|
||||
await internalClient.query('BEGIN');
|
||||
await internalClient.query(
|
||||
`
|
||||
UPDATE "ProjectUser"
|
||||
SET "displayName" = 'Transaction 1', "updatedAt" = NOW()
|
||||
WHERE "projectUserId" = $1
|
||||
`,
|
||||
[user.userId],
|
||||
);
|
||||
await internalClient.query('COMMIT');
|
||||
|
||||
await waitForCondition(
|
||||
async () => {
|
||||
const res = await externalClient.query<{
|
||||
display_name: string | null,
|
||||
}>(
|
||||
`
|
||||
SELECT "display_name"
|
||||
FROM "users"
|
||||
WHERE "primary_email" = $1
|
||||
`,
|
||||
[`${dbName}@example.com`],
|
||||
);
|
||||
return (
|
||||
res.rows.length === 1 &&
|
||||
res.rows[0].display_name === 'Transaction 1'
|
||||
);
|
||||
},
|
||||
{ description: 'Transaction 1 exported', timeoutMs: 90000 },
|
||||
);
|
||||
|
||||
const afterT1 = await externalClient.query<{
|
||||
display_name: string | null,
|
||||
}>(
|
||||
`
|
||||
SELECT "display_name"
|
||||
FROM "users"
|
||||
WHERE "primary_email" = $1
|
||||
`,
|
||||
[`${dbName}@example.com`],
|
||||
);
|
||||
|
||||
expect(afterT1.rows.length).toBe(1);
|
||||
expect(afterT1.rows[0].display_name).toBe('Transaction 1');
|
||||
} finally {
|
||||
await internalClient.end();
|
||||
}
|
||||
},
|
||||
LOCAL_TEST_TIMEOUT,
|
||||
);
|
||||
|
||||
/**
|
||||
* Scenario 3:
|
||||
* First transaction is committed and synced.
|
||||
* Second transaction has UPDATE done but is still uncommitted.
|
||||
* Poller should STILL see Transaction 1 (not Transaction 2).
|
||||
*/
|
||||
test(
|
||||
'Poller does not see second update until commit',
|
||||
async () => {
|
||||
const dbName = 'race_second_uncommitted_poll_test';
|
||||
const { externalClient, user } =
|
||||
await setupExternalDbWithBaseline(dbName);
|
||||
|
||||
const internalDbUrl = makeInternalDbUrl();
|
||||
const internalClient = new Client({ connectionString: internalDbUrl });
|
||||
|
||||
await internalClient.connect();
|
||||
|
||||
try {
|
||||
await internalClient.query('BEGIN');
|
||||
await internalClient.query(
|
||||
`
|
||||
UPDATE "ProjectUser"
|
||||
SET "displayName" = 'Transaction 1', "updatedAt" = NOW()
|
||||
WHERE "projectUserId" = $1
|
||||
`,
|
||||
[user.userId],
|
||||
);
|
||||
await internalClient.query('COMMIT');
|
||||
|
||||
await waitForCondition(
|
||||
async () => {
|
||||
const res = await externalClient.query<{ display_name: string | null }>(
|
||||
`SELECT "display_name" FROM "users" WHERE "primary_email" = $1`,
|
||||
[`${dbName}@example.com`],
|
||||
);
|
||||
return res.rows.length === 1 && res.rows[0].display_name === 'Transaction 1';
|
||||
},
|
||||
{ description: 'Transaction 1 exported', timeoutMs: 60000 },
|
||||
);
|
||||
|
||||
// Start uncommitted Transaction 2
|
||||
await internalClient.query('BEGIN');
|
||||
await internalClient.query(
|
||||
`
|
||||
UPDATE "ProjectUser"
|
||||
SET "displayName" = 'Transaction 2', "updatedAt" = NOW()
|
||||
WHERE "projectUserId" = $1
|
||||
`,
|
||||
[user.userId],
|
||||
);
|
||||
|
||||
await sleep(7000);
|
||||
|
||||
const duringT2 = await externalClient.query<{
|
||||
display_name: string | null,
|
||||
}>(
|
||||
`
|
||||
SELECT "display_name"
|
||||
FROM "users"
|
||||
WHERE "primary_email" = $1
|
||||
`,
|
||||
[`${dbName}@example.com`],
|
||||
);
|
||||
|
||||
expect(duringT2.rows.length).toBe(1);
|
||||
// Uncommitted Transaction 2 should not be visible
|
||||
expect(duringT2.rows[0].display_name).not.toBe('Transaction 2');
|
||||
expect(duringT2.rows[0].display_name).toBe('Transaction 1');
|
||||
|
||||
await internalClient.query('ROLLBACK');
|
||||
} finally {
|
||||
await internalClient.end();
|
||||
}
|
||||
},
|
||||
LOCAL_TEST_TIMEOUT,
|
||||
);
|
||||
|
||||
/**
|
||||
* Scenario 4:
|
||||
* Two different rows, out-of-order commits:
|
||||
* - T1 starts
|
||||
* - T2 starts
|
||||
* - T2 updates row2
|
||||
* - T1 updates row1
|
||||
* - T2 commits
|
||||
* - Sync → only T2's row visible, T1's row unchanged
|
||||
* - T1 commits
|
||||
* - Sync → T1's row now visible
|
||||
*
|
||||
* Uses two different users to avoid row-level locking.
|
||||
*/
|
||||
test(
|
||||
'Out-of-order commits on different rows: uncommitted changes invisible',
|
||||
async () => {
|
||||
const dbName = 'race_two_rows_out_of_order_test';
|
||||
const connectionString = await dbManager.createDatabase(dbName);
|
||||
|
||||
await createProjectWithExternalDb({
|
||||
main: {
|
||||
type: 'postgres',
|
||||
connectionString,
|
||||
},
|
||||
});
|
||||
|
||||
const externalClient = dbManager.getClient(dbName);
|
||||
|
||||
const user1 = await User.create({ primary_email: 'row1@example.com' });
|
||||
const user2 = await User.create({ primary_email: 'row2@example.com' });
|
||||
|
||||
await waitForTable(externalClient, 'users');
|
||||
|
||||
await waitForCondition(
|
||||
async () => {
|
||||
const res = await externalClient.query(`SELECT COUNT(*) as count FROM "users"`);
|
||||
return parseInt(res.rows[0].count, 10) === 2;
|
||||
},
|
||||
{ description: 'both users synced initially', timeoutMs: 60000 },
|
||||
);
|
||||
|
||||
const internalDbUrl = makeInternalDbUrl();
|
||||
const t1Client = new Client({ connectionString: internalDbUrl });
|
||||
const t2Client = new Client({ connectionString: internalDbUrl });
|
||||
|
||||
await t1Client.connect();
|
||||
await t2Client.connect();
|
||||
|
||||
try {
|
||||
await t1Client.query('BEGIN');
|
||||
|
||||
await t2Client.query('BEGIN');
|
||||
|
||||
await t2Client.query(
|
||||
`
|
||||
UPDATE "ProjectUser"
|
||||
SET "displayName" = 'T2 Updated', "updatedAt" = NOW()
|
||||
WHERE "projectUserId" = $1
|
||||
`,
|
||||
[user2.userId],
|
||||
);
|
||||
|
||||
await t1Client.query(
|
||||
`
|
||||
UPDATE "ProjectUser"
|
||||
SET "displayName" = 'T1 Updated', "updatedAt" = NOW()
|
||||
WHERE "projectUserId" = $1
|
||||
`,
|
||||
[user1.userId],
|
||||
);
|
||||
|
||||
await t2Client.query('COMMIT');
|
||||
|
||||
await waitForCondition(
|
||||
async () => {
|
||||
const res = await externalClient.query<{ display_name: string | null }>(
|
||||
`SELECT "display_name" FROM "users" WHERE "primary_email" = $1`,
|
||||
['row2@example.com'],
|
||||
);
|
||||
return res.rows.length === 1 && res.rows[0].display_name === 'T2 Updated';
|
||||
},
|
||||
{ description: 'T2 row synced after T2 commit', timeoutMs: 90000 },
|
||||
);
|
||||
|
||||
const row1BeforeT1Commit = await externalClient.query<{ display_name: string | null }>(
|
||||
`SELECT "display_name" FROM "users" WHERE "primary_email" = $1`,
|
||||
['row1@example.com'],
|
||||
);
|
||||
expect(row1BeforeT1Commit.rows.length).toBe(1);
|
||||
expect(row1BeforeT1Commit.rows[0].display_name).not.toBe('T1 Updated');
|
||||
|
||||
await t1Client.query('COMMIT');
|
||||
|
||||
await waitForCondition(
|
||||
async () => {
|
||||
const res = await externalClient.query<{ display_name: string | null }>(
|
||||
`SELECT "display_name" FROM "users" WHERE "primary_email" = $1`,
|
||||
['row1@example.com'],
|
||||
);
|
||||
return res.rows.length === 1 && res.rows[0].display_name === 'T1 Updated';
|
||||
},
|
||||
{ description: 'T1 row synced after T1 commit', timeoutMs: 90000 },
|
||||
);
|
||||
|
||||
const finalRow1 = await externalClient.query<{ display_name: string | null }>(
|
||||
`SELECT "display_name" FROM "users" WHERE "primary_email" = $1`,
|
||||
['row1@example.com'],
|
||||
);
|
||||
const finalRow2 = await externalClient.query<{ display_name: string | null }>(
|
||||
`SELECT "display_name" FROM "users" WHERE "primary_email" = $1`,
|
||||
['row2@example.com'],
|
||||
);
|
||||
|
||||
expect(finalRow1.rows[0].display_name).toBe('T1 Updated');
|
||||
expect(finalRow2.rows[0].display_name).toBe('T2 Updated');
|
||||
} finally {
|
||||
await t1Client.end();
|
||||
await t2Client.end();
|
||||
}
|
||||
},
|
||||
LOCAL_TEST_TIMEOUT,
|
||||
);
|
||||
|
||||
/**
|
||||
* Scenario 5:
|
||||
* Full lifecycle:
|
||||
* - baseline
|
||||
* - Transaction 1 committed & synced
|
||||
* - Transaction 2 committed after a later sync
|
||||
* Final state must be Transaction 2.
|
||||
*/
|
||||
test(
|
||||
'Sequential updates both sync correctly',
|
||||
async () => {
|
||||
const dbName = 'race_full_lifecycle_test';
|
||||
const { externalClient, user } =
|
||||
await setupExternalDbWithBaseline(dbName);
|
||||
|
||||
const internalDbUrl = makeInternalDbUrl();
|
||||
const internalClient = new Client({ connectionString: internalDbUrl });
|
||||
|
||||
await internalClient.connect();
|
||||
|
||||
try {
|
||||
await internalClient.query('BEGIN');
|
||||
await internalClient.query(
|
||||
`
|
||||
UPDATE "ProjectUser"
|
||||
SET "displayName" = 'Transaction 1', "updatedAt" = NOW()
|
||||
WHERE "projectUserId" = $1
|
||||
`,
|
||||
[user.userId],
|
||||
);
|
||||
await internalClient.query('COMMIT');
|
||||
|
||||
await waitForCondition(
|
||||
async () => {
|
||||
const res = await externalClient.query<{
|
||||
display_name: string | null,
|
||||
}>(
|
||||
`SELECT "display_name" FROM "users" WHERE "primary_email" = $1`,
|
||||
[`${dbName}@example.com`],
|
||||
);
|
||||
return res.rows.length === 1 && res.rows[0].display_name === 'Transaction 1';
|
||||
},
|
||||
{ description: 'T1 synced', timeoutMs: 90000 },
|
||||
);
|
||||
|
||||
const afterT1 = await externalClient.query<{
|
||||
display_name: string | null,
|
||||
}>(
|
||||
`
|
||||
SELECT "display_name"
|
||||
FROM "users"
|
||||
WHERE "primary_email" = $1
|
||||
`,
|
||||
[`${dbName}@example.com`],
|
||||
);
|
||||
|
||||
expect(afterT1.rows.length).toBe(1);
|
||||
expect(afterT1.rows[0].display_name).toBe('Transaction 1');
|
||||
|
||||
await internalClient.query('BEGIN');
|
||||
await internalClient.query(
|
||||
`
|
||||
UPDATE "ProjectUser"
|
||||
SET "displayName" = 'Transaction 2', "updatedAt" = NOW()
|
||||
WHERE "projectUserId" = $1
|
||||
`,
|
||||
[user.userId],
|
||||
);
|
||||
await internalClient.query('COMMIT');
|
||||
|
||||
await waitForCondition(
|
||||
async () => {
|
||||
const res = await externalClient.query<{
|
||||
display_name: string | null,
|
||||
}>(
|
||||
`SELECT "display_name" FROM "users" WHERE "primary_email" = $1`,
|
||||
[`${dbName}@example.com`],
|
||||
);
|
||||
return res.rows.length === 1 && res.rows[0].display_name === 'Transaction 2';
|
||||
},
|
||||
{ description: 'T2 synced', timeoutMs: 90000 },
|
||||
);
|
||||
|
||||
const afterT2 = await externalClient.query<{
|
||||
display_name: string | null,
|
||||
}>(
|
||||
`
|
||||
SELECT "display_name"
|
||||
FROM "users"
|
||||
WHERE "primary_email" = $1
|
||||
`,
|
||||
[`${dbName}@example.com`],
|
||||
);
|
||||
|
||||
expect(afterT2.rows.length).toBe(1);
|
||||
expect(afterT2.rows[0].display_name).toBe('Transaction 2');
|
||||
} finally {
|
||||
await internalClient.end();
|
||||
}
|
||||
},
|
||||
LOCAL_TEST_TIMEOUT,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user