File
Blob: tests/worker/helpers/seeds.ts
| 1 | import { ulid } from "ulid"; |
| 2 | import { and, eq } from "drizzle-orm"; |
| 3 | import { |
| 4 | invites, |
| 5 | memberships, |
| 6 | pageShares, |
| 7 | pages, |
| 8 | publishedPages, |
| 9 | tesseraIdentities, |
| 10 | uploads, |
| 11 | users, |
| 12 | workspaces, |
| 13 | workspaceSites, |
| 14 | } from "@/worker/db/d1/schema"; |
| 15 | import { getDb } from "@tests/worker/helpers/db"; |
| 16 | import { TEST_TIMESTAMP } from "@tests/worker/helpers/fixtures"; |
| 17 | |
| 18 | export interface SeedUser { |
| 19 | id: string; |
| 20 | email: string; |
| 21 | name: string; |
| 22 | } |
| 23 | |
| 24 | export interface SeedUserOptions { |
| 25 | id?: string; |
| 26 | email?: string; |
| 27 | name?: string; |
| 28 | avatar_url?: string | null; |
| 29 | } |
| 30 | |
| 31 | export async function seedUser(opts: SeedUserOptions = {}): Promise<SeedUser> { |
| 32 | const id = opts.id ?? `user_${ulid()}`; |
| 33 | const email = opts.email ?? `${id}@example.com`; |
| 34 | const name = opts.name ?? "Test User"; |
| 35 | |
| 36 | await getDb() |
| 37 | .insert(users) |
| 38 | .values({ |
| 39 | id, |
| 40 | email, |
| 41 | name, |
| 42 | avatar_url: opts.avatar_url ?? null, |
| 43 | created_at: TEST_TIMESTAMP, |
| 44 | updated_at: TEST_TIMESTAMP, |
| 45 | }); |
| 46 | |
| 47 | return { id, email, name }; |
| 48 | } |
| 49 | |
| 50 | export interface SeedTesseraIdentityOptions { |
| 51 | sub: string; |
| 52 | user_id: string; |
| 53 | created_at?: string; |
| 54 | last_seen_at?: string | null; |
| 55 | } |
| 56 | |
| 57 | export async function seedTesseraIdentity( |
| 58 | opts: SeedTesseraIdentityOptions, |
| 59 | ): Promise<typeof tesseraIdentities.$inferSelect> { |
| 60 | const row: typeof tesseraIdentities.$inferSelect = { |
| 61 | sub: opts.sub, |
| 62 | user_id: opts.user_id, |
| 63 | created_at: opts.created_at ?? TEST_TIMESTAMP, |
| 64 | last_seen_at: opts.last_seen_at ?? null, |
| 65 | }; |
| 66 | await getDb().insert(tesseraIdentities).values(row); |
| 67 | return row; |
| 68 | } |
| 69 | |
| 70 | export interface SeedWorkspace { |
| 71 | id: string; |
| 72 | slug: string; |
| 73 | name: string; |
| 74 | owner_id: string; |
| 75 | } |
| 76 | |
| 77 | export interface SeedWorkspaceOptions { |
| 78 | id?: string; |
| 79 | slug?: string; |
| 80 | name?: string; |
| 81 | owner_id: string; |
| 82 | icon?: string | null; |
| 83 | seedOwnerMembership?: boolean; |
| 84 | } |
| 85 | |
| 86 | export async function seedWorkspace(opts: SeedWorkspaceOptions): Promise<SeedWorkspace> { |
| 87 | const id = opts.id ?? `ws_${ulid()}`; |
| 88 | const slug = opts.slug ?? id.toLowerCase(); |
| 89 | const name = opts.name ?? "Test Workspace"; |
| 90 | |
| 91 | await getDb() |
| 92 | .insert(workspaces) |
| 93 | .values({ |
| 94 | id, |
| 95 | slug, |
| 96 | name, |
| 97 | icon: opts.icon ?? null, |
| 98 | owner_id: opts.owner_id, |
| 99 | created_at: TEST_TIMESTAMP, |
| 100 | }); |
| 101 | |
| 102 | if (opts.seedOwnerMembership !== false) { |
| 103 | await seedMembership({ user_id: opts.owner_id, workspace_id: id, role: "owner" }); |
| 104 | } |
| 105 | |
| 106 | return { id, slug, name, owner_id: opts.owner_id }; |
| 107 | } |
| 108 | |
| 109 | export interface SeedMembershipOptions { |
| 110 | user_id: string; |
| 111 | workspace_id: string; |
| 112 | role: typeof memberships.$inferSelect.role; |
| 113 | joined_at?: string; |
| 114 | } |
| 115 | |
| 116 | export async function seedMembership(opts: SeedMembershipOptions): Promise<typeof memberships.$inferSelect> { |
| 117 | const row = { |
| 118 | user_id: opts.user_id, |
| 119 | workspace_id: opts.workspace_id, |
| 120 | role: opts.role, |
| 121 | joined_at: opts.joined_at ?? TEST_TIMESTAMP, |
| 122 | }; |
| 123 | await getDb().insert(memberships).values(row); |
| 124 | return row; |
| 125 | } |
| 126 | |
| 127 | export interface SeedPageOptions { |
| 128 | id?: string; |
| 129 | workspace_id: string; |
| 130 | created_by: string; |
| 131 | parent_id?: string | null; |
| 132 | kind?: "doc" | "canvas"; |
| 133 | title?: string; |
| 134 | icon?: string | null; |
| 135 | cover_url?: string | null; |
| 136 | position?: number; |
| 137 | archived_at?: string | null; |
| 138 | archive_root_id?: string | null; |
| 139 | created_at?: string; |
| 140 | updated_at?: string; |
| 141 | } |
| 142 | |
| 143 | export async function seedPage(opts: SeedPageOptions): Promise<typeof pages.$inferSelect> { |
| 144 | // ULID (26 chars) fits the PresignRequest page_id.max(26) contract without prefix. |
| 145 | const id = opts.id ?? ulid(); |
| 146 | const row: typeof pages.$inferSelect = { |
| 147 | id, |
| 148 | workspace_id: opts.workspace_id, |
| 149 | parent_id: opts.parent_id ?? null, |
| 150 | kind: opts.kind ?? "doc", |
| 151 | title: opts.title ?? "Test Page", |
| 152 | icon: opts.icon ?? null, |
| 153 | cover_url: opts.cover_url ?? null, |
| 154 | position: opts.position ?? 1, |
| 155 | created_by: opts.created_by, |
| 156 | created_at: opts.created_at ?? TEST_TIMESTAMP, |
| 157 | updated_at: opts.updated_at ?? TEST_TIMESTAMP, |
| 158 | archived_at: opts.archived_at ?? null, |
| 159 | archive_root_id: opts.archive_root_id ?? null, |
| 160 | }; |
| 161 | await getDb().insert(pages).values(row); |
| 162 | return row; |
| 163 | } |
| 164 | |
| 165 | export interface SeedPageShareOptions { |
| 166 | id?: string; |
| 167 | page_id: string; |
| 168 | created_by: string; |
| 169 | permission: "view" | "edit"; |
| 170 | grantee_type: "user" | "link"; |
| 171 | grantee_id?: string | null; |
| 172 | link_token?: string | null; |
| 173 | } |
| 174 | |
| 175 | export async function seedPageShare(opts: SeedPageShareOptions): Promise<typeof pageShares.$inferSelect> { |
| 176 | const row: typeof pageShares.$inferSelect = { |
| 177 | id: opts.id ?? `share_${ulid()}`, |
| 178 | page_id: opts.page_id, |
| 179 | grantee_type: opts.grantee_type, |
| 180 | grantee_id: opts.grantee_id ?? null, |
| 181 | permission: opts.permission, |
| 182 | link_token: opts.link_token ?? null, |
| 183 | created_by: opts.created_by, |
| 184 | created_at: TEST_TIMESTAMP, |
| 185 | }; |
| 186 | await getDb().insert(pageShares).values(row); |
| 187 | return row; |
| 188 | } |
| 189 | |
| 190 | export interface SeedWorkspaceSiteOptions { |
| 191 | workspace_id: string; |
| 192 | slug?: string; |
| 193 | home_page_id?: string | null; |
| 194 | published?: boolean; |
| 195 | published_at?: string | null; |
| 196 | created_at?: string; |
| 197 | updated_at?: string; |
| 198 | } |
| 199 | |
| 200 | export async function seedWorkspaceSite(opts: SeedWorkspaceSiteOptions): Promise<typeof workspaceSites.$inferSelect> { |
| 201 | const createdAt = opts.created_at ?? TEST_TIMESTAMP; |
| 202 | const publishedAt = |
| 203 | typeof opts.published_at === "undefined" ? (opts.published === false ? null : createdAt) : opts.published_at; |
| 204 | const row: typeof workspaceSites.$inferSelect = { |
| 205 | workspace_id: opts.workspace_id, |
| 206 | slug: opts.slug ?? "acme", |
| 207 | home_page_id: opts.home_page_id ?? null, |
| 208 | published_at: publishedAt, |
| 209 | created_at: createdAt, |
| 210 | updated_at: opts.updated_at ?? createdAt, |
| 211 | }; |
| 212 | |
| 213 | await getDb().insert(workspaceSites).values(row); |
| 214 | return row; |
| 215 | } |
| 216 | |
| 217 | export interface SeedPublishedPageOptions { |
| 218 | workspace_id: string; |
| 219 | page_id: string; |
| 220 | published_by: string; |
| 221 | published_at?: string; |
| 222 | } |
| 223 | |
| 224 | export async function seedPublishedPage(opts: SeedPublishedPageOptions): Promise<typeof publishedPages.$inferSelect> { |
| 225 | const row: typeof publishedPages.$inferSelect = { |
| 226 | workspace_id: opts.workspace_id, |
| 227 | page_id: opts.page_id, |
| 228 | published_by: opts.published_by, |
| 229 | published_at: opts.published_at ?? TEST_TIMESTAMP, |
| 230 | }; |
| 231 | |
| 232 | await getDb().insert(publishedPages).values(row).onConflictDoNothing(); |
| 233 | return row; |
| 234 | } |
| 235 | |
| 236 | export async function deletePublishedPage(workspaceId: string, pageId: string): Promise<void> { |
| 237 | await getDb() |
| 238 | .delete(publishedPages) |
| 239 | .where(and(eq(publishedPages.workspace_id, workspaceId), eq(publishedPages.page_id, pageId))); |
| 240 | } |
| 241 | |
| 242 | export interface SeedInviteOptions { |
| 243 | id?: string; |
| 244 | workspace_id: string; |
| 245 | invited_by: string; |
| 246 | role?: "admin" | "member" | "guest"; |
| 247 | email?: string | null; |
| 248 | token?: string; |
| 249 | expires_at?: string; |
| 250 | accepted_at?: string | null; |
| 251 | accepted_by?: string | null; |
| 252 | revoked_at?: string | null; |
| 253 | } |
| 254 | |
| 255 | export async function seedInvite(opts: SeedInviteOptions): Promise<typeof invites.$inferSelect> { |
| 256 | const row: typeof invites.$inferSelect = { |
| 257 | id: opts.id ?? `invite_${ulid()}`, |
| 258 | email: opts.email ?? null, |
| 259 | workspace_id: opts.workspace_id, |
| 260 | invited_by: opts.invited_by, |
| 261 | role: opts.role ?? "member", |
| 262 | token: opts.token ?? `inv_${ulid()}`, |
| 263 | accepted_at: opts.accepted_at ?? null, |
| 264 | accepted_by: opts.accepted_by ?? null, |
| 265 | revoked_at: opts.revoked_at ?? null, |
| 266 | expires_at: opts.expires_at ?? "2099-01-01T00:00:00.000Z", |
| 267 | created_at: TEST_TIMESTAMP, |
| 268 | }; |
| 269 | await getDb().insert(invites).values(row); |
| 270 | return row; |
| 271 | } |
| 272 | |
| 273 | export interface SeedUploadOptions { |
| 274 | id?: string; |
| 275 | workspace_id: string; |
| 276 | uploaded_by: string; |
| 277 | page_id?: string | null; |
| 278 | filename?: string; |
| 279 | content_type?: string; |
| 280 | size_bytes?: number; |
| 281 | r2_key?: string; |
| 282 | } |
| 283 | |
| 284 | export async function seedUpload(opts: SeedUploadOptions): Promise<typeof uploads.$inferSelect> { |
| 285 | const row: typeof uploads.$inferSelect = { |
| 286 | id: opts.id ?? `upload_${ulid()}`, |
| 287 | workspace_id: opts.workspace_id, |
| 288 | page_id: opts.page_id ?? null, |
| 289 | uploaded_by: opts.uploaded_by, |
| 290 | filename: opts.filename ?? "test.png", |
| 291 | content_type: opts.content_type ?? "image/png", |
| 292 | size_bytes: opts.size_bytes ?? 128, |
| 293 | r2_key: opts.r2_key ?? `uploads/${opts.workspace_id}/${ulid()}.png`, |
| 294 | created_at: TEST_TIMESTAMP, |
| 295 | }; |
| 296 | await getDb().insert(uploads).values(row); |
| 297 | return row; |
| 298 | } |