mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-04 21:04:37 +08:00
fix: CI failures — Node 22, test timeouts, QEMU transaction timeout (#1479)
This commit is contained in:
parent
b48902fcf2
commit
6a8b45e6c5
@ -26,10 +26,10 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- name: Setup Node.js v20
|
||||
- name: Setup Node.js v22
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 22
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
|
||||
|
||||
4
.github/workflows/setup-tests.yaml
vendored
4
.github/workflows/setup-tests.yaml
vendored
@ -24,10 +24,10 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- name: Setup Node.js v20
|
||||
- name: Setup Node.js v22
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 22
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
|
||||
|
||||
@ -681,7 +681,7 @@ async function seedDummyUsers(options: SeedDummyUsersOptions): Promise<Map<strin
|
||||
if (directPermissionRows.length > 0) {
|
||||
await tx.projectUserDirectPermission.createMany({ data: directPermissionRows });
|
||||
}
|
||||
});
|
||||
}, { timeout: 90_000 });
|
||||
}
|
||||
|
||||
// Team memberships for the named seed users — bulk-inserted the same way.
|
||||
@ -725,7 +725,7 @@ async function seedDummyUsers(options: SeedDummyUsersOptions): Promise<Map<strin
|
||||
if (teamMemberPermissionRows.length > 0) {
|
||||
await tx.teamMemberDirectPermission.createMany({ data: teamMemberPermissionRows });
|
||||
}
|
||||
});
|
||||
}, { timeout: 90_000 });
|
||||
}
|
||||
|
||||
return userEmailToId;
|
||||
|
||||
@ -480,7 +480,7 @@ class TransactionErrorThatShouldNotBeRetried extends Error {
|
||||
/**
|
||||
* @deprecated Prisma transactions are slow and lock the database. Use rawQuery with CTEs instead. Ask Konsti if you're confused or think you need transactions.
|
||||
*/
|
||||
export async function retryTransaction<T>(client: Omit<PrismaClient, "$on">, fn: (tx: PrismaClientTransaction) => Promise<T>, options: { level?: "default" | "serializable" } = {}): Promise<T> {
|
||||
export async function retryTransaction<T>(client: Omit<PrismaClient, "$on">, fn: (tx: PrismaClientTransaction) => Promise<T>, options: { level?: "default" | "serializable", timeout?: number } = {}): Promise<T> {
|
||||
// serializable transactions are currently off by default, later we may turn them on
|
||||
const enableSerializable = options.level === "serializable";
|
||||
|
||||
@ -524,6 +524,7 @@ export async function retryTransaction<T>(client: Omit<PrismaClient, "$on">, fn:
|
||||
return res;
|
||||
}, {
|
||||
isolationLevel: enableSerializable ? Prisma.TransactionIsolationLevel.Serializable : undefined,
|
||||
...(options.timeout != null ? { timeout: options.timeout } : {}),
|
||||
}));
|
||||
} catch (e) {
|
||||
// we don't want to retry too aggressively here, because the error may have been thrown after the transaction was already committed
|
||||
|
||||
@ -634,7 +634,7 @@ it("rejects batch when analytics event quota is exhausted", async ({ expect }) =
|
||||
expect(res.body.code).toBe("ITEM_QUANTITY_INSUFFICIENT_AMOUNT");
|
||||
});
|
||||
|
||||
it("accepts batch and debits event quota correctly", async ({ expect }) => {
|
||||
it("accepts batch and debits event quota correctly", { timeout: 120_000 }, async ({ expect }) => {
|
||||
const { ownerTeamId } = await setupProjectWithPlan("free");
|
||||
await Auth.Otp.signIn();
|
||||
|
||||
@ -673,7 +673,7 @@ it("accepts batch and debits event quota correctly", async ({ expect }) => {
|
||||
// We don't support metered pricing or partial batches for now, so the entire
|
||||
// batch is rejected when remaining quota is less than the batch size, and
|
||||
// the quota must remain unchanged (no partial debit).
|
||||
it("rejects batch when remaining quota is less than batch size and does not debit", async ({ expect }) => {
|
||||
it("rejects batch when remaining quota is less than batch size and does not debit", { timeout: 120_000 }, async ({ expect }) => {
|
||||
const { ownerTeamId } = await setupProjectWithPlan("free");
|
||||
await Auth.Otp.signIn();
|
||||
|
||||
|
||||
@ -214,7 +214,7 @@ it("cannot read events from other projects", async ({ expect }) => {
|
||||
`);
|
||||
});
|
||||
|
||||
it("filters analytics events by user within a project", async ({ expect }) => {
|
||||
it("filters analytics events by user within a project", { timeout: 120_000 }, async ({ expect }) => {
|
||||
await Project.createAndSwitch({ config: { magic_link_enabled: true } });
|
||||
const { userId: userA } = await Auth.Otp.signIn();
|
||||
await bumpEmailAddress();
|
||||
|
||||
@ -43,7 +43,7 @@ async function ensureAnonymousUsersAreStillExcluded(metricsResponse: NiceRespons
|
||||
|
||||
// ClickHouse ingestion is async; poll until anonymous users are excluded again.
|
||||
let response!: NiceResponse;
|
||||
for (let i = 0; i < 10; i++) {
|
||||
for (let i = 0; i < 30; i++) {
|
||||
await wait(2_000);
|
||||
response = await niceBackendFetch("/api/v1/internal/metrics", { accessType: 'admin' });
|
||||
const noAnonymousInRecentlyRegistered = (response.body.recently_registered as MetricsUser[]).every((user) => !user.is_anonymous);
|
||||
@ -72,7 +72,7 @@ async function ensureAnonymousUsersAreStillExcluded(metricsResponse: NiceRespons
|
||||
|
||||
async function waitForMetricsToIncludeUsersByCountry(options: { countryCode: string, expectedCount: number }): Promise<NiceResponse> {
|
||||
let response!: NiceResponse;
|
||||
for (let i = 0; i < 15; i++) {
|
||||
for (let i = 0; i < 30; i++) {
|
||||
response = await niceBackendFetch("/api/v1/internal/metrics", { accessType: 'admin' });
|
||||
if (response.body?.users_by_country?.[options.countryCode] === options.expectedCount) {
|
||||
return response;
|
||||
@ -88,7 +88,7 @@ async function waitForMetricsMatch(
|
||||
): Promise<NiceResponse> {
|
||||
let response!: NiceResponse;
|
||||
const suffix = includeAnonymous ? "?include_anonymous=true" : "";
|
||||
for (let i = 0; i < 20; i++) {
|
||||
for (let i = 0; i < 60; i++) {
|
||||
response = await niceBackendFetch(`/api/v1/internal/metrics${suffix}`, { accessType: 'admin' });
|
||||
if (predicate(response)) {
|
||||
return response;
|
||||
@ -123,7 +123,7 @@ async function waitForAnalyticsRowsForSessionReplaySegment(
|
||||
throw new Error(`Timed out waiting for ${expectedCount} analytics rows for session replay segment ${sessionReplaySegmentId}`);
|
||||
}
|
||||
|
||||
it("should return metrics data", async ({ expect }) => {
|
||||
it("should return metrics data", { timeout: 120_000 }, async ({ expect }) => {
|
||||
await Project.createAndSwitch({
|
||||
config: {
|
||||
magic_link_enabled: true,
|
||||
|
||||
@ -189,7 +189,7 @@ it("anonymous signup creates exactly one $token-refresh event", async ({ expect
|
||||
});
|
||||
});
|
||||
|
||||
it("OAuth signup creates exactly one $token-refresh event", async ({ expect }) => {
|
||||
it("OAuth signup creates exactly one $token-refresh event", { timeout: 120_000 }, async ({ expect }) => {
|
||||
const { projectId } = await Project.createAndSwitch({
|
||||
config: {
|
||||
oauth_providers: [{
|
||||
@ -223,7 +223,7 @@ it("OAuth signup creates exactly one $token-refresh event", async ({ expect }) =
|
||||
// Signin Tests
|
||||
// ============================================================================
|
||||
|
||||
it("password signin (existing user) creates exactly one additional $token-refresh event", async ({ expect }) => {
|
||||
it("password signin (existing user) creates exactly one additional $token-refresh event", { timeout: 120_000 }, async ({ expect }) => {
|
||||
const { projectId } = await Project.createAndSwitch({
|
||||
config: { credential_enabled: true },
|
||||
});
|
||||
@ -246,7 +246,7 @@ it("password signin (existing user) creates exactly one additional $token-refres
|
||||
expect(events.every((e: AnalyticsEvent) => e.user_id === userId)).toBe(true);
|
||||
});
|
||||
|
||||
it("OTP signin (existing user) creates exactly one additional $token-refresh event", async ({ expect }) => {
|
||||
it("OTP signin (existing user) creates exactly one additional $token-refresh event", { timeout: 120_000 }, async ({ expect }) => {
|
||||
const { projectId } = await Project.createAndSwitch({
|
||||
config: { magic_link_enabled: true },
|
||||
});
|
||||
@ -267,7 +267,7 @@ it("OTP signin (existing user) creates exactly one additional $token-refresh eve
|
||||
expect(events.every((e: AnalyticsEvent) => e.user_id === userId)).toBe(true);
|
||||
});
|
||||
|
||||
it("OAuth signin (existing user) creates exactly one additional $token-refresh event", async ({ expect }) => {
|
||||
it("OAuth signin (existing user) creates exactly one additional $token-refresh event", { timeout: 120_000 }, async ({ expect }) => {
|
||||
const { projectId } = await Project.createAndSwitch({
|
||||
config: {
|
||||
oauth_providers: [{
|
||||
@ -299,7 +299,7 @@ it("OAuth signin (existing user) creates exactly one additional $token-refresh e
|
||||
// Session Refresh Tests
|
||||
// ============================================================================
|
||||
|
||||
it("session refresh endpoint creates exactly one additional $token-refresh event", async ({ expect }) => {
|
||||
it("session refresh endpoint creates exactly one additional $token-refresh event", { timeout: 120_000 }, async ({ expect }) => {
|
||||
const { projectId } = await Project.createAndSwitch({
|
||||
config: { magic_link_enabled: true },
|
||||
});
|
||||
@ -317,7 +317,7 @@ it("session refresh endpoint creates exactly one additional $token-refresh event
|
||||
expect(events.every((e: AnalyticsEvent) => e.user_id === userId)).toBe(true);
|
||||
});
|
||||
|
||||
it("multiple session refreshes create one event each", async ({ expect }) => {
|
||||
it("multiple session refreshes create one event each", { timeout: 180_000 }, async ({ expect }) => {
|
||||
const { projectId } = await Project.createAndSwitch({
|
||||
config: { magic_link_enabled: true },
|
||||
});
|
||||
@ -344,7 +344,7 @@ it("multiple session refreshes create one event each", async ({ expect }) => {
|
||||
// OAuth Refresh Token Grant Tests
|
||||
// ============================================================================
|
||||
|
||||
it("OAuth refresh token grant creates exactly one additional $token-refresh event", async ({ expect }) => {
|
||||
it("OAuth refresh token grant creates exactly one additional $token-refresh event", { timeout: 120_000 }, async ({ expect }) => {
|
||||
const { projectId } = await Project.createAndSwitch({
|
||||
config: {
|
||||
oauth_providers: [{
|
||||
@ -391,7 +391,7 @@ it("OAuth refresh token grant creates exactly one additional $token-refresh even
|
||||
expect(events.every((e: AnalyticsEvent) => e.user_id === userId)).toBe(true);
|
||||
});
|
||||
|
||||
it("multiple OAuth refresh token grants create one event each", async ({ expect }) => {
|
||||
it("multiple OAuth refresh token grants create one event each", { timeout: 180_000 }, async ({ expect }) => {
|
||||
const { projectId } = await Project.createAndSwitch({
|
||||
config: {
|
||||
oauth_providers: [{
|
||||
|
||||
@ -198,7 +198,7 @@ it("should list invitations from multiple teams", async ({ expect }) => {
|
||||
});
|
||||
|
||||
|
||||
it("should accept a team invitation via the client SDK", async ({ expect }) => {
|
||||
it("should accept a team invitation via the client SDK", { timeout: 120_000 }, async ({ expect }) => {
|
||||
const { clientApp, serverApp } = await createApp({ config: { clientTeamCreationEnabled: true } });
|
||||
|
||||
// Create a team
|
||||
|
||||
Loading…
Reference in New Issue
Block a user