File
Blob: test/pat-last-used.worker.test.ts
| 1 | import { beforeAll, describe, expect, it } from "vitest"; |
| 2 | import { env } from "cloudflare:workers"; |
| 3 | |
| 4 | import { |
| 5 | PAT_LAST_USED_READ_THROTTLE_MS, |
| 6 | generatePatPlaintext, |
| 7 | hashPatPlaintext, |
| 8 | shouldTouchPatLastUsedAt, |
| 9 | } from "@/worker/auth/pat"; |
| 10 | import { newPrefixedId } from "@/worker/common"; |
| 11 | import { createDb } from "@/worker/db/d1/client"; |
| 12 | import { findPatByPrefix, insertPatWithGrants, updatePatLastUsedAt } from "@/worker/db/d1/dal"; |
| 13 | |
| 14 | import { ensureD1Migrations } from "./util/d1Setup"; |
| 15 | import { seedRepo } from "./util/repoSeed"; |
| 16 | |
| 17 | beforeAll(async () => { |
| 18 | await ensureD1Migrations(env); |
| 19 | }); |
| 20 | |
| 21 | describe("shouldTouchPatLastUsedAt", () => { |
| 22 | it("write op always returns true regardless of lastUsedAt", () => { |
| 23 | expect(shouldTouchPatLastUsedAt(null, "write")).toBe(true); |
| 24 | expect(shouldTouchPatLastUsedAt(Date.now(), "write")).toBe(true); |
| 25 | }); |
| 26 | |
| 27 | it("read op returns true when lastUsedAt is null", () => { |
| 28 | expect(shouldTouchPatLastUsedAt(null, "read")).toBe(true); |
| 29 | }); |
| 30 | |
| 31 | it("read op returns false when lastUsedAt is fresh", () => { |
| 32 | const now = Date.now(); |
| 33 | expect(shouldTouchPatLastUsedAt(now - 1000, "read", now)).toBe(false); |
| 34 | expect(shouldTouchPatLastUsedAt(now - PAT_LAST_USED_READ_THROTTLE_MS + 1, "read", now)).toBe( |
| 35 | false |
| 36 | ); |
| 37 | }); |
| 38 | |
| 39 | it("read op returns true when lastUsedAt is older than the throttle window", () => { |
| 40 | const now = Date.now(); |
| 41 | expect(shouldTouchPatLastUsedAt(now - PAT_LAST_USED_READ_THROTTLE_MS, "read", now)).toBe(true); |
| 42 | expect(shouldTouchPatLastUsedAt(now - PAT_LAST_USED_READ_THROTTLE_MS - 1, "read", now)).toBe( |
| 43 | true |
| 44 | ); |
| 45 | }); |
| 46 | }); |
| 47 | |
| 48 | describe("updatePatLastUsedAt", () => { |
| 49 | it("writes the supplied timestamp to the row", async () => { |
| 50 | const seed = await seedRepo(env, { |
| 51 | namespaceSlug: `pat-touch-${Math.random().toString(36).slice(2, 8)}`, |
| 52 | repoSlug: "site", |
| 53 | }); |
| 54 | const db = createDb(env.DB); |
| 55 | const generated = generatePatPlaintext(); |
| 56 | const patId = newPrefixedId("pat"); |
| 57 | await insertPatWithGrants(db, { |
| 58 | pat: { |
| 59 | id: patId, |
| 60 | userId: seed.userId, |
| 61 | name: "ci", |
| 62 | prefix: generated.publicPrefix, |
| 63 | hash: await hashPatPlaintext(generated.plaintext), |
| 64 | createdAt: Date.now(), |
| 65 | expiresAt: null, |
| 66 | revokedAt: null, |
| 67 | lastUsedAt: null, |
| 68 | }, |
| 69 | namespaceGrants: [], |
| 70 | repoGrants: [{ patId, repoId: seed.repositoryId, level: "push" }], |
| 71 | }); |
| 72 | |
| 73 | const before = await findPatByPrefix(db, generated.publicPrefix); |
| 74 | expect(before?.lastUsedAt).toBeNull(); |
| 75 | |
| 76 | const stamp = 1_700_000_000_000; |
| 77 | await updatePatLastUsedAt(db, patId, stamp); |
| 78 | |
| 79 | const after = await findPatByPrefix(db, generated.publicPrefix); |
| 80 | expect(after?.lastUsedAt).toBe(stamp); |
| 81 | }); |
| 82 | }); |