refactor: extract shared test fixtures and secret helpers
This commit is contained in:
parent
26203cca59
commit
c64862f604
10 changed files with 174 additions and 394 deletions
|
|
@ -15,27 +15,33 @@ vi.mock("@tanstack/react-router", async (importOriginal) => {
|
|||
|
||||
import { EditBackupPage } from "../edit-backup";
|
||||
|
||||
afterEach(() => {
|
||||
navigateMock.mockClear();
|
||||
cleanup();
|
||||
});
|
||||
const repository = { shortId: "repo-1", name: "Repo 1", type: "local" };
|
||||
const volume = {
|
||||
id: "volume-1",
|
||||
shortId: "vol-1",
|
||||
name: "Volume 1",
|
||||
config: { backend: "directory", path: "/mnt" },
|
||||
};
|
||||
const volumeFilesResponse = {
|
||||
files: [{ name: "project", path: "/project", type: "directory" }],
|
||||
path: "/",
|
||||
offset: 0,
|
||||
limit: 100,
|
||||
total: 1,
|
||||
hasMore: false,
|
||||
};
|
||||
|
||||
test("submits the computed cron expression when saving a daily schedule", async () => {
|
||||
const renderEditBackupPage = ({ enabled, cronExpression }: { enabled: boolean; cronExpression: string }) => {
|
||||
const submittedBody = new Promise<Record<string, unknown>>((resolve) => {
|
||||
server.use(
|
||||
http.get("/api/v1/backups/:shortId", () => {
|
||||
return HttpResponse.json({
|
||||
shortId: "backup-1",
|
||||
name: "Backup 1",
|
||||
enabled: true,
|
||||
repository: { shortId: "repo-1", name: "Repo 1", type: "local" },
|
||||
volume: {
|
||||
id: "volume-1",
|
||||
shortId: "vol-1",
|
||||
name: "Volume 1",
|
||||
config: { backend: "directory", path: "/mnt" },
|
||||
},
|
||||
cronExpression: "0 2 * * *",
|
||||
enabled,
|
||||
repository,
|
||||
volume,
|
||||
cronExpression,
|
||||
retentionPolicy: null,
|
||||
includePaths: ["/project"],
|
||||
includePatterns: [],
|
||||
|
|
@ -46,17 +52,10 @@ test("submits the computed cron expression when saving a daily schedule", async
|
|||
});
|
||||
}),
|
||||
http.get("/api/v1/repositories", () => {
|
||||
return HttpResponse.json([{ shortId: "repo-1", name: "Repo 1", type: "local" }]);
|
||||
return HttpResponse.json([repository]);
|
||||
}),
|
||||
http.get("/api/v1/volumes/:shortId/files", () => {
|
||||
return HttpResponse.json({
|
||||
files: [{ name: "project", path: "/project", type: "directory" }],
|
||||
path: "/",
|
||||
offset: 0,
|
||||
limit: 100,
|
||||
total: 1,
|
||||
hasMore: false,
|
||||
});
|
||||
return HttpResponse.json(volumeFilesResponse);
|
||||
}),
|
||||
http.patch("/api/v1/backups/:shortId", async ({ request }) => {
|
||||
const body = (await request.json()) as Record<string, unknown>;
|
||||
|
|
@ -64,13 +63,8 @@ test("submits the computed cron expression when saving a daily schedule", async
|
|||
|
||||
return HttpResponse.json({
|
||||
shortId: "backup-1",
|
||||
volume: {
|
||||
id: "volume-1",
|
||||
shortId: "vol-1",
|
||||
name: "Volume 1",
|
||||
config: { backend: "directory", path: "/mnt" },
|
||||
},
|
||||
repository: { shortId: "repo-1", name: "Repo 1", type: "local" },
|
||||
volume,
|
||||
repository,
|
||||
...body,
|
||||
});
|
||||
}),
|
||||
|
|
@ -79,6 +73,17 @@ test("submits the computed cron expression when saving a daily schedule", async
|
|||
|
||||
render(<EditBackupPage backupId="backup-1" />, { withSuspense: true });
|
||||
|
||||
return { submittedBody };
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
navigateMock.mockClear();
|
||||
cleanup();
|
||||
});
|
||||
|
||||
test("submits the computed cron expression when saving a daily schedule", async () => {
|
||||
const { submittedBody } = renderEditBackupPage({ enabled: true, cronExpression: "0 2 * * *" });
|
||||
|
||||
await userEvent.click(await screen.findByRole("button", { name: "Update schedule" }));
|
||||
|
||||
await expect(submittedBody).resolves.toMatchObject({
|
||||
|
|
@ -89,63 +94,7 @@ test("submits the computed cron expression when saving a daily schedule", async
|
|||
});
|
||||
|
||||
test("disables the schedule when switching an enabled custom cron schedule to manual only", async () => {
|
||||
const submittedBody = new Promise<Record<string, unknown>>((resolve) => {
|
||||
server.use(
|
||||
http.get("/api/v1/backups/:shortId", () => {
|
||||
return HttpResponse.json({
|
||||
shortId: "backup-1",
|
||||
name: "Backup 1",
|
||||
enabled: true,
|
||||
repository: { shortId: "repo-1", name: "Repo 1", type: "local" },
|
||||
volume: {
|
||||
id: "volume-1",
|
||||
shortId: "vol-1",
|
||||
name: "Volume 1",
|
||||
config: { backend: "directory", path: "/mnt" },
|
||||
},
|
||||
cronExpression: "*/13 * * * *",
|
||||
retentionPolicy: null,
|
||||
includePaths: ["/project"],
|
||||
includePatterns: [],
|
||||
excludePatterns: [],
|
||||
excludeIfPresent: [],
|
||||
oneFileSystem: false,
|
||||
customResticParams: [],
|
||||
});
|
||||
}),
|
||||
http.get("/api/v1/repositories", () => {
|
||||
return HttpResponse.json([{ shortId: "repo-1", name: "Repo 1", type: "local" }]);
|
||||
}),
|
||||
http.get("/api/v1/volumes/:shortId/files", () => {
|
||||
return HttpResponse.json({
|
||||
files: [{ name: "project", path: "/project", type: "directory" }],
|
||||
path: "/",
|
||||
offset: 0,
|
||||
limit: 100,
|
||||
total: 1,
|
||||
hasMore: false,
|
||||
});
|
||||
}),
|
||||
http.patch("/api/v1/backups/:shortId", async ({ request }) => {
|
||||
const body = (await request.json()) as Record<string, unknown>;
|
||||
resolve(body);
|
||||
|
||||
return HttpResponse.json({
|
||||
shortId: "backup-1",
|
||||
volume: {
|
||||
id: "volume-1",
|
||||
shortId: "vol-1",
|
||||
name: "Volume 1",
|
||||
config: { backend: "directory", path: "/mnt" },
|
||||
},
|
||||
repository: { shortId: "repo-1", name: "Repo 1", type: "local" },
|
||||
...body,
|
||||
});
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
render(<EditBackupPage backupId="backup-1" />, { withSuspense: true });
|
||||
const { submittedBody } = renderEditBackupPage({ enabled: true, cronExpression: "*/13 * * * *" });
|
||||
|
||||
const nativeFrequencySelect = (await screen.findAllByRole("combobox"))
|
||||
.at(1)
|
||||
|
|
@ -165,63 +114,7 @@ test("disables the schedule when switching an enabled custom cron schedule to ma
|
|||
});
|
||||
|
||||
test("preserves a disabled schedule when saving a non-manual frequency", async () => {
|
||||
const submittedBody = new Promise<Record<string, unknown>>((resolve) => {
|
||||
server.use(
|
||||
http.get("/api/v1/backups/:shortId", () => {
|
||||
return HttpResponse.json({
|
||||
shortId: "backup-1",
|
||||
name: "Backup 1",
|
||||
enabled: false,
|
||||
repository: { shortId: "repo-1", name: "Repo 1", type: "local" },
|
||||
volume: {
|
||||
id: "volume-1",
|
||||
shortId: "vol-1",
|
||||
name: "Volume 1",
|
||||
config: { backend: "directory", path: "/mnt" },
|
||||
},
|
||||
cronExpression: "0 2 * * *",
|
||||
retentionPolicy: null,
|
||||
includePaths: ["/project"],
|
||||
includePatterns: [],
|
||||
excludePatterns: [],
|
||||
excludeIfPresent: [],
|
||||
oneFileSystem: false,
|
||||
customResticParams: [],
|
||||
});
|
||||
}),
|
||||
http.get("/api/v1/repositories", () => {
|
||||
return HttpResponse.json([{ shortId: "repo-1", name: "Repo 1", type: "local" }]);
|
||||
}),
|
||||
http.get("/api/v1/volumes/:shortId/files", () => {
|
||||
return HttpResponse.json({
|
||||
files: [{ name: "project", path: "/project", type: "directory" }],
|
||||
path: "/",
|
||||
offset: 0,
|
||||
limit: 100,
|
||||
total: 1,
|
||||
hasMore: false,
|
||||
});
|
||||
}),
|
||||
http.patch("/api/v1/backups/:shortId", async ({ request }) => {
|
||||
const body = (await request.json()) as Record<string, unknown>;
|
||||
resolve(body);
|
||||
|
||||
return HttpResponse.json({
|
||||
shortId: "backup-1",
|
||||
volume: {
|
||||
id: "volume-1",
|
||||
shortId: "vol-1",
|
||||
name: "Volume 1",
|
||||
config: { backend: "directory", path: "/mnt" },
|
||||
},
|
||||
repository: { shortId: "repo-1", name: "Repo 1", type: "local" },
|
||||
...body,
|
||||
});
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
render(<EditBackupPage backupId="backup-1" />, { withSuspense: true });
|
||||
const { submittedBody } = renderEditBackupPage({ enabled: false, cronExpression: "0 2 * * *" });
|
||||
|
||||
await userEvent.click(await screen.findByRole("button", { name: "Update schedule" }));
|
||||
|
||||
|
|
|
|||
|
|
@ -1,83 +1,21 @@
|
|||
import { beforeEach, describe, expect, test } from "vitest";
|
||||
import { db, sqlite } from "~/server/db/db";
|
||||
import { account, member, organization, sessionsTable, usersTable } from "~/server/db/schema";
|
||||
import {
|
||||
createMembership,
|
||||
createOrganization,
|
||||
createSession,
|
||||
createUser,
|
||||
dropTrigger,
|
||||
escapeSqlLiteral,
|
||||
randomId,
|
||||
randomSlug,
|
||||
} from "~/test/helpers/user-org";
|
||||
import { authService } from "../auth.service";
|
||||
|
||||
const DELETE_USER_ACCOUNT_ROLLBACK_TRIGGER = "delete_user_account_abort";
|
||||
const REMOVE_ORG_MEMBER_ROLLBACK_TRIGGER = "remove_org_member_reassign_abort";
|
||||
|
||||
function randomId() {
|
||||
return Bun.randomUUIDv7();
|
||||
}
|
||||
|
||||
function randomSlug(prefix: string) {
|
||||
return `${prefix}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
}
|
||||
|
||||
function escapeSqlLiteral(value: string) {
|
||||
return value.replaceAll("'", "''");
|
||||
}
|
||||
|
||||
function dropTrigger(name: string) {
|
||||
sqlite.exec(`DROP TRIGGER IF EXISTS ${name};`);
|
||||
}
|
||||
|
||||
async function createUser(email: string) {
|
||||
const id = randomId();
|
||||
|
||||
await db.insert(usersTable).values({
|
||||
id,
|
||||
email,
|
||||
name: email.split("@")[0],
|
||||
username: randomSlug("user"),
|
||||
});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
async function createOrganization(name: string) {
|
||||
const id = randomId();
|
||||
|
||||
await db.insert(organization).values({
|
||||
id,
|
||||
name,
|
||||
slug: randomSlug("org"),
|
||||
createdAt: new Date(),
|
||||
});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
async function createMembership({
|
||||
userId,
|
||||
organizationId,
|
||||
role,
|
||||
}: {
|
||||
userId: string;
|
||||
organizationId: string;
|
||||
role: "owner" | "admin" | "member";
|
||||
}) {
|
||||
const id = randomId();
|
||||
await db.insert(member).values({ id, userId, organizationId, role, createdAt: new Date() });
|
||||
return id;
|
||||
}
|
||||
|
||||
async function createSession({
|
||||
userId,
|
||||
activeOrganizationId,
|
||||
}: {
|
||||
userId: string;
|
||||
activeOrganizationId: string | null;
|
||||
}) {
|
||||
await db.insert(sessionsTable).values({
|
||||
id: randomId(),
|
||||
userId,
|
||||
token: randomSlug("token"),
|
||||
expiresAt: new Date(Date.now() + 60_000),
|
||||
activeOrganizationId,
|
||||
});
|
||||
}
|
||||
|
||||
async function createAccount({ userId, providerId }: { userId: string; providerId: string }) {
|
||||
const id = randomId();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,90 +1,19 @@
|
|||
import { beforeEach, describe, expect, test } from "vitest";
|
||||
import { db, sqlite } from "~/server/db/db";
|
||||
import { member, organization, sessionsTable, usersTable } from "~/server/db/schema";
|
||||
import {
|
||||
createMembership,
|
||||
createOrganization,
|
||||
createSession,
|
||||
createUser,
|
||||
dropTrigger,
|
||||
escapeSqlLiteral,
|
||||
randomSlug,
|
||||
} from "~/test/helpers/user-org";
|
||||
import { authService } from "../auth.service";
|
||||
|
||||
const CLEANUP_USER_ORGS_ROLLBACK_TRIGGER = "cleanup_user_orgs_final_session_abort";
|
||||
|
||||
function randomId() {
|
||||
return Bun.randomUUIDv7();
|
||||
}
|
||||
|
||||
function randomSlug(prefix: string) {
|
||||
return `${prefix}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
}
|
||||
|
||||
function escapeSqlLiteral(value: string) {
|
||||
return value.replaceAll("'", "''");
|
||||
}
|
||||
|
||||
function dropTrigger(name: string) {
|
||||
sqlite.exec(`DROP TRIGGER IF EXISTS ${name};`);
|
||||
}
|
||||
|
||||
async function createUser(email: string) {
|
||||
const id = randomId();
|
||||
|
||||
await db.insert(usersTable).values({
|
||||
id,
|
||||
email,
|
||||
name: email.split("@")[0],
|
||||
username: randomSlug("user"),
|
||||
});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
async function createOrganization(name: string) {
|
||||
const id = randomId();
|
||||
|
||||
await db.insert(organization).values({
|
||||
id,
|
||||
name,
|
||||
slug: randomSlug("org"),
|
||||
createdAt: new Date(),
|
||||
});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
async function createMembership({
|
||||
userId,
|
||||
organizationId,
|
||||
role,
|
||||
}: {
|
||||
userId: string;
|
||||
organizationId: string;
|
||||
role: "owner" | "admin" | "member";
|
||||
}) {
|
||||
await db.insert(member).values({
|
||||
id: randomId(),
|
||||
userId,
|
||||
organizationId,
|
||||
role,
|
||||
createdAt: new Date(),
|
||||
});
|
||||
}
|
||||
|
||||
async function createSession({
|
||||
userId,
|
||||
activeOrganizationId,
|
||||
}: {
|
||||
userId: string;
|
||||
activeOrganizationId: string | null;
|
||||
}) {
|
||||
const id = randomId();
|
||||
|
||||
await db.insert(sessionsTable).values({
|
||||
id,
|
||||
userId,
|
||||
token: randomSlug("token"),
|
||||
expiresAt: new Date(Date.now() + 60_000),
|
||||
activeOrganizationId,
|
||||
});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
describe("authService.cleanupUserOrganizations", () => {
|
||||
beforeEach(async () => {
|
||||
dropTrigger(CLEANUP_USER_ORGS_ROLLBACK_TRIGGER);
|
||||
|
|
|
|||
|
|
@ -1,16 +1,6 @@
|
|||
import { cryptoUtils } from "~/server/utils/crypto";
|
||||
import { cryptoUtils, transformOptionalSecret, type SecretTransformer } from "~/server/utils/crypto";
|
||||
import type { NotificationConfig } from "~/schemas/notifications";
|
||||
|
||||
type SecretTransformer = (value: string) => Promise<string>;
|
||||
|
||||
const transformOptionalSecret = async (value: string | undefined, transformSecret: SecretTransformer) => {
|
||||
if (!value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return await transformSecret(value);
|
||||
};
|
||||
|
||||
export const mapNotificationConfigSecrets = async (config: NotificationConfig, transformSecret: SecretTransformer) => {
|
||||
switch (config.type) {
|
||||
case "email":
|
||||
|
|
|
|||
|
|
@ -1,18 +1,5 @@
|
|||
import type { RepositoryConfig } from "@zerobyte/core/restic";
|
||||
import { cryptoUtils } from "~/server/utils/crypto";
|
||||
|
||||
type SecretTransformer = (value: string) => Promise<string>;
|
||||
|
||||
const transformOptionalSecret = async (
|
||||
value: string | undefined,
|
||||
transformSecret: SecretTransformer,
|
||||
): Promise<string | undefined> => {
|
||||
if (!value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return await transformSecret(value);
|
||||
};
|
||||
import { cryptoUtils, transformOptionalSecret, type SecretTransformer } from "~/server/utils/crypto";
|
||||
|
||||
export const mapRepositoryConfigSecrets = async (
|
||||
config: RepositoryConfig,
|
||||
|
|
|
|||
|
|
@ -1,61 +1,19 @@
|
|||
import { beforeEach, describe, expect, test } from "vitest";
|
||||
import { db, sqlite } from "~/server/db/db";
|
||||
import { account, invitation, member, organization, sessionsTable, ssoProvider, usersTable } from "~/server/db/schema";
|
||||
import {
|
||||
createOrganization,
|
||||
createSession,
|
||||
createUser,
|
||||
dropTrigger,
|
||||
escapeSqlLiteral,
|
||||
randomId,
|
||||
randomSlug,
|
||||
} from "~/test/helpers/user-org";
|
||||
import { ssoService } from "../sso.service";
|
||||
|
||||
const DELETE_SSO_PROVIDER_ROLLBACK_TRIGGER = "delete_sso_provider_sessions_abort";
|
||||
|
||||
function randomId() {
|
||||
return Bun.randomUUIDv7();
|
||||
}
|
||||
|
||||
function randomSlug(prefix: string) {
|
||||
return `${prefix}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
}
|
||||
|
||||
function escapeSqlLiteral(value: string) {
|
||||
return value.replaceAll("'", "''");
|
||||
}
|
||||
|
||||
function dropTrigger(name: string) {
|
||||
sqlite.exec(`DROP TRIGGER IF EXISTS ${name};`);
|
||||
}
|
||||
|
||||
async function createUser(email: string) {
|
||||
const id = randomId();
|
||||
|
||||
await db.insert(usersTable).values({
|
||||
id,
|
||||
email,
|
||||
name: email.split("@")[0],
|
||||
username: randomSlug("user"),
|
||||
});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
async function createOrganization(name: string) {
|
||||
const id = randomId();
|
||||
|
||||
await db.insert(organization).values({
|
||||
id,
|
||||
name,
|
||||
slug: randomSlug("org"),
|
||||
createdAt: new Date(),
|
||||
});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
async function createSession(userId: string) {
|
||||
await db.insert(sessionsTable).values({
|
||||
id: randomId(),
|
||||
userId,
|
||||
token: randomSlug("token"),
|
||||
expiresAt: new Date(Date.now() + 60_000),
|
||||
});
|
||||
}
|
||||
|
||||
describe("ssoService.deleteSsoProvider", () => {
|
||||
beforeEach(async () => {
|
||||
dropTrigger(DELETE_SSO_PROVIDER_ROLLBACK_TRIGGER);
|
||||
|
|
@ -140,8 +98,8 @@ describe("ssoService.deleteSsoProvider", () => {
|
|||
userId: accountUserB,
|
||||
},
|
||||
]);
|
||||
await createSession(accountUserA);
|
||||
await createSession(accountUserB);
|
||||
await createSession({ userId: accountUserA });
|
||||
await createSession({ userId: accountUserB });
|
||||
|
||||
const deleted = await ssoService.deleteSsoProvider(providerId, org);
|
||||
|
||||
|
|
@ -225,7 +183,7 @@ describe("ssoService.deleteSsoProvider", () => {
|
|||
providerId,
|
||||
userId: accountUser,
|
||||
});
|
||||
await createSession(accountUser);
|
||||
await createSession({ userId: accountUser });
|
||||
|
||||
sqlite.exec(`
|
||||
CREATE TRIGGER ${DELETE_SSO_PROVIDER_ROLLBACK_TRIGGER}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,5 @@
|
|||
import type { BackendConfig } from "~/schemas/volumes";
|
||||
import { cryptoUtils } from "~/server/utils/crypto";
|
||||
|
||||
type SecretTransformer = (value: string) => Promise<string>;
|
||||
|
||||
const transformOptionalSecret = async (
|
||||
value: string | undefined,
|
||||
transformSecret: SecretTransformer,
|
||||
): Promise<string | undefined> => {
|
||||
if (!value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return await transformSecret(value);
|
||||
};
|
||||
import { cryptoUtils, transformOptionalSecret, type SecretTransformer } from "~/server/utils/crypto";
|
||||
|
||||
export const mapVolumeConfigSecrets = async (
|
||||
config: BackendConfig,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,19 @@ const algorithm = "aes-256-gcm" as const;
|
|||
const keyLength = 32;
|
||||
const encryptionPrefix = "encv1:";
|
||||
|
||||
export type SecretTransformer = (value: string) => Promise<string>;
|
||||
|
||||
export const transformOptionalSecret = async (
|
||||
value: string | undefined,
|
||||
transformSecret: SecretTransformer,
|
||||
): Promise<string | undefined> => {
|
||||
if (!value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return await transformSecret(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if a given string is encrypted by looking for the encryption prefix.
|
||||
*/
|
||||
|
|
@ -90,11 +103,7 @@ const sealSecret = async (value: string): Promise<string> => {
|
|||
};
|
||||
|
||||
const sealOptionalSecret = async (value?: string): Promise<string | undefined> => {
|
||||
if (typeof value === "undefined") {
|
||||
return value;
|
||||
}
|
||||
|
||||
return sealSecret(value);
|
||||
return transformOptionalSecret(value, sealSecret);
|
||||
};
|
||||
|
||||
async function deriveSecret(label: string) {
|
||||
|
|
|
|||
88
app/test/helpers/user-org.ts
Normal file
88
app/test/helpers/user-org.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import { db, sqlite } from "~/server/db/db";
|
||||
import { member, organization, sessionsTable, usersTable } from "~/server/db/schema";
|
||||
|
||||
type MembershipRole = "owner" | "admin" | "member";
|
||||
|
||||
export function randomId() {
|
||||
return Bun.randomUUIDv7();
|
||||
}
|
||||
|
||||
export function randomSlug(prefix: string) {
|
||||
return `${prefix}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
}
|
||||
|
||||
export function escapeSqlLiteral(value: string) {
|
||||
return value.replaceAll("'", "''");
|
||||
}
|
||||
|
||||
export function dropTrigger(name: string) {
|
||||
sqlite.exec(`DROP TRIGGER IF EXISTS ${name};`);
|
||||
}
|
||||
|
||||
export async function createUser(email: string) {
|
||||
const id = randomId();
|
||||
|
||||
await db.insert(usersTable).values({
|
||||
id,
|
||||
email,
|
||||
name: email.split("@")[0],
|
||||
username: randomSlug("user"),
|
||||
});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
export async function createOrganization(name: string) {
|
||||
const id = randomId();
|
||||
|
||||
await db.insert(organization).values({
|
||||
id,
|
||||
name,
|
||||
slug: randomSlug("org"),
|
||||
createdAt: new Date(),
|
||||
});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
export async function createMembership({
|
||||
userId,
|
||||
organizationId,
|
||||
role,
|
||||
}: {
|
||||
userId: string;
|
||||
organizationId: string;
|
||||
role: MembershipRole;
|
||||
}) {
|
||||
const id = randomId();
|
||||
|
||||
await db.insert(member).values({
|
||||
id,
|
||||
userId,
|
||||
organizationId,
|
||||
role,
|
||||
createdAt: new Date(),
|
||||
});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
export async function createSession({
|
||||
userId,
|
||||
activeOrganizationId = null,
|
||||
}: {
|
||||
userId: string;
|
||||
activeOrganizationId?: string | null;
|
||||
}) {
|
||||
const id = randomId();
|
||||
|
||||
await db.insert(sessionsTable).values({
|
||||
id,
|
||||
userId,
|
||||
token: randomSlug("token"),
|
||||
expiresAt: new Date(Date.now() + 60_000),
|
||||
activeOrganizationId,
|
||||
});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ vi.mock(import("~/server/utils/crypto"), async () => {
|
|||
const cryptoModule = await vi.importActual<typeof import("~/server/utils/crypto")>("~/server/utils/crypto");
|
||||
|
||||
return {
|
||||
...cryptoModule,
|
||||
cryptoUtils: {
|
||||
...cryptoModule.cryptoUtils,
|
||||
deriveSecret: async () => "test-secret",
|
||||
|
|
|
|||
Loading…
Reference in a new issue