Files
triggerdotdev--trigger.dev/apps/webapp/test/environmentVariablesRepository.test.ts
Katia Bulatova cae3dcb7dd Env vars page performance fix (#3829)
## Summary

This PR improves performance across the Environment Variables page.

## Changes

### Targeted value loading

- load only the non-secret (environmentId, key) pairs required by the
page. Secret values continue to be redacted in the UI.

### SSR windowing + virtualization

- SSR-render only the first 50 rows
- hydrate those rows
- virtualize the remaining dataset client-side
- search is now URL-driven during SSR, ensuring deep links such as
`?search=DATABASE_URL`

### Lightweight 'Create' flow

- 'Create' page no longer loads the full Environment Variables dataset.

## Results

Large projects no longer render thousands of rows during SSR.
Example (~11k rendered rows):

Metric | Before | After
-- | -- | --
Document size | ~150 MB | ~5 MB
SSR rows | ~11k | 50
Browser DOM rows | Thousands | ~26–38
2026-06-04 16:28:55 +02:00

179 lines
6.0 KiB
TypeScript

import { describe, expect, vi } from "vitest";
vi.mock("~/db.server", () => ({
prisma: {},
$replica: {},
$transaction: async (
prismaClient: {
$transaction: (fn: (tx: unknown) => Promise<unknown>) => Promise<unknown>;
},
nameOrFn: string | ((tx: unknown) => Promise<unknown>),
fnOrOptions?: ((tx: unknown) => Promise<unknown>) | unknown
) => {
const fn =
typeof nameOrFn === "string"
? (fnOrOptions as (tx: unknown) => Promise<unknown>)
: nameOrFn;
return prismaClient.$transaction(fn);
},
}));
import { postgresTest } from "@internal/testcontainers";
import { EnvironmentVariablesRepository } from "~/v3/environmentVariables/environmentVariablesRepository.server";
import {
createEnvironmentVariable,
createRuntimeEnvironment,
createTestOrgProjectWithMember,
} from "./fixtures/environmentVariablesFixtures";
vi.setConfig({ testTimeout: 60_000 });
describe("EnvironmentVariablesRepository.getVariableValuesForKeys", () => {
postgresTest("returns an empty map for an empty items array", async ({ prisma }) => {
const { project } = await createTestOrgProjectWithMember(prisma);
const repository = new EnvironmentVariablesRepository(prisma, prisma);
const result = await repository.getVariableValuesForKeys(project.id, []);
expect(result).toBeInstanceOf(Map);
expect(result.size).toBe(0);
});
postgresTest("omits missing keys from the result without throwing", async ({ prisma }) => {
const { organization, project } = await createTestOrgProjectWithMember(prisma);
const environment = await createRuntimeEnvironment(prisma, {
projectId: project.id,
organizationId: organization.id,
type: "PRODUCTION",
});
const repository = new EnvironmentVariablesRepository(prisma, prisma);
const result = await repository.getVariableValuesForKeys(project.id, [
{ environmentId: environment.id, key: "DOES_NOT_EXIST" },
]);
expect(result.size).toBe(0);
expect(result.has(`${environment.id}:DOES_NOT_EXIST`)).toBe(false);
});
postgresTest("returns requested values with correct map keys and decrypted values", async ({ prisma }) => {
const { user, organization, project } = await createTestOrgProjectWithMember(prisma);
const environment = await createRuntimeEnvironment(prisma, {
projectId: project.id,
organizationId: organization.id,
type: "PRODUCTION",
});
const repository = new EnvironmentVariablesRepository(prisma, prisma);
await createEnvironmentVariable(repository, project.id, {
environmentId: environment.id,
key: "VAR_A",
value: "value-a",
userId: user.id,
});
await createEnvironmentVariable(repository, project.id, {
environmentId: environment.id,
key: "VAR_B",
value: "value-b",
userId: user.id,
});
await createEnvironmentVariable(repository, project.id, {
environmentId: environment.id,
key: "VAR_C",
value: "value-c",
userId: user.id,
});
const result = await repository.getVariableValuesForKeys(project.id, [
{ environmentId: environment.id, key: "VAR_A" },
{ environmentId: environment.id, key: "VAR_C" },
]);
expect(result).toBeInstanceOf(Map);
expect(result.size).toBe(2);
expect(result.get(`${environment.id}:VAR_A`)).toBe("value-a");
expect(result.get(`${environment.id}:VAR_C`)).toBe("value-c");
expect(result.has(`${environment.id}:VAR_B`)).toBe(false);
});
postgresTest("deduplicates duplicate environmentId and key requests", async ({ prisma }) => {
const { user, organization, project } = await createTestOrgProjectWithMember(prisma);
const environment = await createRuntimeEnvironment(prisma, {
projectId: project.id,
organizationId: organization.id,
type: "PRODUCTION",
});
const repository = new EnvironmentVariablesRepository(prisma, prisma);
await createEnvironmentVariable(repository, project.id, {
environmentId: environment.id,
key: "DEDUP_KEY",
value: "dedup-value",
userId: user.id,
});
const request = { environmentId: environment.id, key: "DEDUP_KEY" };
const result = await repository.getVariableValuesForKeys(project.id, [request, request, request]);
expect(result.size).toBe(1);
expect(result.get(`${environment.id}:DEDUP_KEY`)).toBe("dedup-value");
});
postgresTest("isolates values by project", async ({ prisma }) => {
const { user, organization, project: projectA } = await createTestOrgProjectWithMember(prisma);
const projectB = await prisma.project.create({
data: {
name: "Project B",
slug: `proj-b-${Date.now()}`,
organizationId: organization.id,
externalRef: `ext-b-${Date.now()}`,
},
});
const envA = await createRuntimeEnvironment(prisma, {
projectId: projectA.id,
organizationId: organization.id,
type: "PRODUCTION",
});
const envB = await createRuntimeEnvironment(prisma, {
projectId: projectB.id,
organizationId: organization.id,
type: "PRODUCTION",
});
const repository = new EnvironmentVariablesRepository(prisma, prisma);
await createEnvironmentVariable(repository, projectA.id, {
environmentId: envA.id,
key: "SHARED_KEY",
value: "project-a-value",
userId: user.id,
});
await createEnvironmentVariable(repository, projectB.id, {
environmentId: envB.id,
key: "SHARED_KEY",
value: "project-b-value",
userId: user.id,
});
const resultForProjectA = await repository.getVariableValuesForKeys(projectA.id, [
{ environmentId: envA.id, key: "SHARED_KEY" },
]);
expect(resultForProjectA.size).toBe(1);
expect(resultForProjectA.get(`${envA.id}:SHARED_KEY`)).toBe("project-a-value");
expect(resultForProjectA.get(`${envB.id}:SHARED_KEY`)).toBeUndefined();
const crossProjectRequest = await repository.getVariableValuesForKeys(projectA.id, [
{ environmentId: envB.id, key: "SHARED_KEY" },
]);
expect(crossProjectRequest.size).toBe(0);
});
});