import * as NodeCryptoLayer from "@effect/platform-node/NodeCrypto";import { describe, expect, it } from "@effect/vitest";import * as Alchemy from "alchemy";import * as Cloudflare from "alchemy/Cloudflare";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Redacted from "effect/Redacted"; import * as RelayConfiguration from "../Config.ts";import * as ApnsDeliveryQueue from "./ApnsDeliveryQueue.ts"; const config: RelayConfiguration.RelayConfiguration["Service"] = { relayIssuer: "https://relay.example.com", apns: { teamId: "team-1", keyId: "key-1", privateKey: Redacted.make("apns-private-key"), bundleId: "com.t3tools.test", environment: "sandbox", }, clerkSecretKey: Redacted.make("clerk-secret"), clerkPublishableKey: "pk_test_test", clerkJwtAudience: "t3-code-relay", apnsDeliveryJobSigningSecret: Redacted.make("apns-job-secret"), cloudMintPrivateKey: Redacted.make("cloud-private-key"), cloudMintPublicKey: "cloud-public-key", managedEndpointBaseDomain: undefined, managedEndpointNamespace: undefined,}; describe("ApnsDeliveryQueue", () => { it.effect("does not require the deployment RuntimeContext when building the Worker layer", () => { const sent: unknown[] = []; const sender: Cloudflare.Queues.WriteQueueClient = { raw: Effect.die("raw queue binding is not used"), send: (body) => Effect.sync(() => { sent.push(body); }), sendBatch: () => Effect.die("batch queue binding is not used"), }; const runtimeContext = {} as Alchemy.BaseRuntimeContext; const layer = ApnsDeliveryQueue.layerCloudflareQueues(sender, runtimeContext).pipe( Layer.provide(NodeCryptoLayer.layer), Layer.provide(RelayConfiguration.layer(config)), ); return Effect.gen(function* () { const queue = yield* ApnsDeliveryQueue.ApnsDeliveryQueue; yield* queue.enqueuePushNotification({ userId: "user-1", deviceId: "device-1", token: "push-token", notification: { title: "Thread", body: "Input: Project", environmentId: "env-1", threadId: "thread-1", deepLink: "/threads/env-1/thread-1", }, }); expect(sent).toHaveLength(1); }).pipe(Effect.provide(layer)); }); it.effect("preserves job identity and the queue sender cause", () => { const cause = new Error("queue unavailable"); const senderCause = new Cloudflare.Queues.SendError({ message: cause.message, cause, }); const layer = ApnsDeliveryQueue.layer.pipe( Layer.provide(NodeCryptoLayer.layer), Layer.provide(RelayConfiguration.layer(config)), Layer.provide( Layer.succeed(ApnsDeliveryQueue.ApnsDeliveryQueueSender, { send: () => Effect.fail(senderCause), }), ), ); return Effect.gen(function* () { const queue = yield* ApnsDeliveryQueue.ApnsDeliveryQueue; const error = yield* Effect.flip( queue.enqueuePushNotification({ userId: "user-1", deviceId: "device-1", token: "push-token", notification: { title: "Thread", body: "Input: Project", environmentId: "env-1", threadId: "thread-1", deepLink: "/threads/env-1/thread-1", }, }), ); expect(error).toMatchObject({ _tag: "ApnsDeliveryQueueSendError", operation: "send", jobId: expect.any(String), kind: "push_notification", userId: "user-1", deviceId: "device-1", cause: senderCause, }); expect(senderCause.cause).toBe(cause); expect(error.message).toBe( "Failed to enqueue APNs push notification delivery during send for device device-1.", ); }).pipe(Effect.provide(layer)); });});
import * as NodeCryptoLayer from "@effect/platform-node/NodeCrypto";
import { describe, expect, it } from "@effect/vitest";
import * as Alchemy from "alchemy";
import * as Cloudflare from "alchemy/Cloudflare";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Redacted from "effect/Redacted";
import * as RelayConfiguration from "../Config.ts";
import * as ApnsDeliveryQueue from "./ApnsDeliveryQueue.ts";
const config: RelayConfiguration.RelayConfiguration["Service"] = {
relayIssuer: "https://relay.example.com",
apns: {
teamId: "team-1",
keyId: "key-1",
privateKey: Redacted.make("apns-private-key"),
bundleId: "com.t3tools.test",
environment: "sandbox",
},
clerkSecretKey: Redacted.make("clerk-secret"),
clerkPublishableKey: "pk_test_test",
clerkJwtAudience: "t3-code-relay",
apnsDeliveryJobSigningSecret: Redacted.make("apns-job-secret"),
cloudMintPrivateKey: Redacted.make("cloud-private-key"),
cloudMintPublicKey: "cloud-public-key",
managedEndpointBaseDomain: undefined,
managedEndpointNamespace: undefined,
};
describe("ApnsDeliveryQueue", () => {
it.effect("does not require the deployment RuntimeContext when building the Worker layer", () => {
const sent: unknown[] = [];
const sender: Cloudflare.Queues.WriteQueueClient = {
raw: Effect.die("raw queue binding is not used"),
send: (body) =>
Effect.sync(() => {
sent.push(body);
}),
sendBatch: () => Effect.die("batch queue binding is not used"),
};
const runtimeContext = {} as Alchemy.BaseRuntimeContext;
const layer = ApnsDeliveryQueue.layerCloudflareQueues(sender, runtimeContext).pipe(
Layer.provide(NodeCryptoLayer.layer),
Layer.provide(RelayConfiguration.layer(config)),
);
return Effect.gen(function* () {
const queue = yield* ApnsDeliveryQueue.ApnsDeliveryQueue;
yield* queue.enqueuePushNotification({
userId: "user-1",
deviceId: "device-1",
token: "push-token",
notification: {
title: "Thread",
body: "Input: Project",
environmentId: "env-1",
threadId: "thread-1",
deepLink: "/threads/env-1/thread-1",
},
});
expect(sent).toHaveLength(1);
}).pipe(Effect.provide(layer));
});
it.effect("preserves job identity and the queue sender cause", () => {
const cause = new Error("queue unavailable");
const senderCause = new Cloudflare.Queues.SendError({
message: cause.message,
cause,
});
const layer = ApnsDeliveryQueue.layer.pipe(
Layer.provide(NodeCryptoLayer.layer),
Layer.provide(RelayConfiguration.layer(config)),
Layer.provide(
Layer.succeed(ApnsDeliveryQueue.ApnsDeliveryQueueSender, {
send: () => Effect.fail(senderCause),
}),
),
);
return Effect.gen(function* () {
const queue = yield* ApnsDeliveryQueue.ApnsDeliveryQueue;
const error = yield* Effect.flip(
queue.enqueuePushNotification({
userId: "user-1",
deviceId: "device-1",
token: "push-token",
notification: {
title: "Thread",
body: "Input: Project",
environmentId: "env-1",
threadId: "thread-1",
deepLink: "/threads/env-1/thread-1",
},
}),
);
expect(error).toMatchObject({
_tag: "ApnsDeliveryQueueSendError",
operation: "send",
jobId: expect.any(String),
kind: "push_notification",
userId: "user-1",
deviceId: "device-1",
cause: senderCause,
});
expect(senderCause.cause).toBe(cause);
expect(error.message).toBe(
"Failed to enqueue APNs push notification delivery during send for device device-1.",
);
}).pipe(Effect.provide(layer));
});
});