import * as NodeCrypto from "node:crypto"; import { describe, expect, it } from "@effect/vitest"; import * as Effect from "effect/Effect"; import * as Redacted from "effect/Redacted"; import * as Schema from "effect/Schema"; import * as ApnsProviderTokens from "./ApnsProviderTokens.ts"; const { privateKey } = NodeCrypto.generateKeyPairSync("ec", { namedCurve: "prime256v1", privateKeyEncoding: { type: "pkcs8", format: "pem" }, publicKeyEncoding: { type: "spki", format: "pem" }, }); const signingInput = { teamId: "team-1", keyId: "key-1", privateKey: Redacted.make(privateKey), }; const WINDOW = ApnsProviderTokens.APNS_JWT_REUSE_SECONDS; const decodeJwtPayload = Schema.decodeEffect( Schema.fromJsonString(Schema.Struct({ iat: Schema.Number })), ); describe("ApnsProviderTokens", () => { it.effect("derives the byte-identical token across isolates within a window", () => { ApnsProviderTokens.__resetApnsProviderTokenCacheForTest(); return Effect.gen(function* () { const tokens = yield* ApnsProviderTokens.ApnsProviderTokens; const first = yield* tokens.getJwt({ ...signingInput, issuedAtUnixSeconds: WINDOW + 10 }); // A fresh isolate has no cache; deterministic signing plus quantized // iat must still reproduce the exact same JWT for the same window. ApnsProviderTokens.__resetApnsProviderTokenCacheForTest(); const second = yield* tokens.getJwt({ ...signingInput, issuedAtUnixSeconds: WINDOW * 2 - 1, }); expect(second).toBe(first); const payload = yield* decodeJwtPayload( Buffer.from(first.split(".")[1]!, "base64url").toString("utf8"), ); expect(payload.iat).toBe(WINDOW); ApnsProviderTokens.__resetApnsProviderTokenCacheForTest(); }).pipe(Effect.provide(ApnsProviderTokens.layer)); }); it.effect("rolls to a new token at the window boundary", () => { ApnsProviderTokens.__resetApnsProviderTokenCacheForTest(); return Effect.gen(function* () { const tokens = yield* ApnsProviderTokens.ApnsProviderTokens; const first = yield* tokens.getJwt({ ...signingInput, issuedAtUnixSeconds: WINDOW + 10 }); const next = yield* tokens.getJwt({ ...signingInput, issuedAtUnixSeconds: WINDOW * 2 }); expect(next).not.toBe(first); ApnsProviderTokens.__resetApnsProviderTokenCacheForTest(); }).pipe(Effect.provide(ApnsProviderTokens.layer)); }); it.effect("serves repeat pushes from the isolate cache without re-signing", () => { ApnsProviderTokens.__resetApnsProviderTokenCacheForTest(); return Effect.gen(function* () { const tokens = yield* ApnsProviderTokens.ApnsProviderTokens; const first = yield* tokens.getJwt({ ...signingInput, issuedAtUnixSeconds: WINDOW + 10 }); const again = yield* tokens.getJwt({ ...signingInput, issuedAtUnixSeconds: WINDOW + 500 }); // Deterministic signing makes equality hold either way; toBe on the // exact string documents the cache contract. expect(again).toBe(first); ApnsProviderTokens.__resetApnsProviderTokenCacheForTest(); }).pipe(Effect.provide(ApnsProviderTokens.layer)); }); });