///
import * as NodeCrypto from "node:crypto";
import { beforeEach, vi } from "vite-plus/test";
import { describe, expect, it } from "@effect/vitest";
import Constants from "expo-constants";
import * as Effect from "effect/Effect";
import * as Exit from "effect/Exit";
import * as Layer from "effect/Layer";
import { FetchHttpClient } from "effect/unstable/http";
import { ManagedRelay } from "@t3tools/client-runtime/relay";
import type { EnvironmentId } from "@t3tools/contracts";
import { verifyDpopProof } from "@t3tools/shared/dpop";
import type { SavedRemoteConnection } from "../../lib/connection";
import { cryptoLayer } from "../cloud/dpop";
import { managedRelayClientLayer } from "../cloud/managedRelayLayer";
import {
clearAgentAwarenessRegistrationRecord,
loadAgentAwarenessRegistrationRecord,
loadOrCreateAgentAwarenessDeviceId,
loadPreferences,
saveAgentAwarenessRegistrationRecord,
} from "../../persistence/imperative";
import type { Preferences } from "../../persistence/mobile-preferences";
import { makeRelayDeviceRegistrationRequest, resolveApsEnvironment } from "./registrationPayload";
import {
AgentAwarenessOperationError,
__resetAgentAwarenessRemoteRegistrationForTest,
armAgentAwarenessLiveActivityForLocalWork,
getAgentAwarenessRegistrationStatus,
mergeAgentAwarenessRegistrationPreferences,
refreshActiveLiveActivityRemoteRegistration,
refreshAgentAwarenessRegistration,
normalizeAgentAwarenessRelayBaseUrl,
registerAgentAwarenessConnection,
registerLiveActivityPushToken,
releaseAgentAwarenessRelayTokenProvider,
setAgentAwarenessRelayTokenProvider,
shouldRegisterAgentAwarenessDeviceForProvider,
unregisterAgentAwarenessConnection,
} from "./remoteRegistration";
import * as Notifications from "expo-notifications";
const secureStore = vi.hoisted(() => new Map());
const widgetMocks = vi.hoisted(() => ({
getInstances: vi.fn(() => []),
start: vi.fn(() => ({})),
}));
const environmentConfigsMock = vi.hoisted(() => ({
configs: new Map<
string,
{ environment: { capabilities: { agentActivityPublishing?: boolean } } }
>(),
}));
const backgroundRuntime = vi.hoisted(() => ({
pending: [] as Array<{
readonly operation: unknown;
readonly resolve: (exit: Exit.Exit) => void;
}>,
}));
const appStateMock = vi.hoisted(() => ({
listeners: [] as Array<(state: string) => void>,
}));
const registrationRecordStore = vi.hoisted(() => ({
current: null as {
readonly identity: string;
readonly signature: string;
readonly pushToStartToken?: string;
} | null,
}));
vi.mock("expo-constants", () => ({
default: {
expoConfig: {
version: "1.0.0",
extra: {},
},
},
}));
vi.mock("expo-widgets", () => ({
addPushToStartTokenListener: vi.fn(() => ({ remove: vi.fn() })),
}));
vi.mock("../../widgets/AgentActivity", () => ({
default: {
getInstances: widgetMocks.getInstances,
start: widgetMocks.start,
},
}));
// The state modules pull the whole connection stack (and native expo modules)
// into the import graph; the arming gate only needs the configs map.
vi.mock("../../state/atom-registry", () => ({
appAtomRegistry: {
get: () => environmentConfigsMock.configs,
},
}));
vi.mock("../../state/server", () => ({
environmentServerConfigsAtom: Symbol("environmentServerConfigsAtom"),
}));
vi.mock("expo-notifications", () => ({
addPushTokenListener: vi.fn(() => ({ remove: vi.fn() })),
getDevicePushTokenAsync: vi.fn(() => Promise.resolve({ type: "ios", data: "apns-token" })),
getPermissionsAsync: vi.fn(() => Promise.resolve({ granted: true })),
}));
vi.mock("expo-crypto", () => ({
CryptoDigestAlgorithm: {
SHA1: "SHA-1",
SHA256: "SHA-256",
SHA384: "SHA-384",
SHA512: "SHA-512",
},
getRandomBytes: (byteCount: number) => new Uint8Array(NodeCrypto.randomBytes(byteCount)),
getRandomBytesAsync: (byteCount: number) =>
Promise.resolve(new Uint8Array(NodeCrypto.randomBytes(byteCount))),
digest: (algorithm: string, data: unknown) => {
if (!(data instanceof Uint8Array)) {
return Promise.reject(new TypeError("expo-crypto digest data must be a typed array."));
}
return Promise.resolve(
new Uint8Array(NodeCrypto.createHash(algorithm).update(data).digest()).buffer,
);
},
}));
vi.mock("expo-secure-store", () => ({
getItemAsync: (key: string) => Promise.resolve(secureStore.get(key) ?? null),
setItemAsync: (key: string, value: string) => {
secureStore.set(key, value);
return Promise.resolve();
},
}));
vi.mock("react-native", () => ({
Platform: {
OS: "ios",
Version: "18.0",
},
AppState: {
addEventListener: (_event: string, listener: (state: string) => void) => {
appStateMock.listeners.push(listener);
return {
remove: () => {
const index = appStateMock.listeners.indexOf(listener);
if (index >= 0) {
appStateMock.listeners.splice(index, 1);
}
},
};
},
},
}));
vi.mock("../../lib/runtime", () => ({
runtime: {
runPromiseExit: (operation: unknown) =>
new Promise((resolve) => {
backgroundRuntime.pending.push({ operation, resolve });
}),
},
}));
vi.mock("../../persistence/imperative", () => ({
loadAgentAwarenessDeviceId: vi.fn(() => Promise.resolve("device-1")),
loadOrCreateAgentAwarenessDeviceId: vi.fn(() => Promise.resolve("device-1")),
loadPreferences: vi.fn(() => Promise.resolve({ liveActivitiesEnabled: false })),
loadAgentAwarenessRegistrationRecord: vi.fn(() =>
Promise.resolve(registrationRecordStore.current),
),
saveAgentAwarenessRegistrationRecord: vi.fn((record: { identity: string; signature: string }) => {
registrationRecordStore.current = record;
return Promise.resolve();
}),
clearAgentAwarenessRegistrationRecord: vi.fn(() => {
registrationRecordStore.current = null;
return Promise.resolve();
}),
}));
function proofIat(proof: string): number {
const payload = proof.split(".")[1];
if (!payload) {
throw new Error("Missing DPoP payload.");
}
const decoded = JSON.parse(Buffer.from(payload, "base64url").toString("utf8")) as {
readonly iat: number;
};
return decoded.iat;
}
function savedConnection(): SavedRemoteConnection {
return {
environmentId: "env-1" as EnvironmentId,
environmentLabel: "Desktop",
pairingUrl: "https://desktop.example/pair",
displayUrl: "https://desktop.example",
httpBaseUrl: "https://desktop.example",
wsBaseUrl: "wss://desktop.example/ws",
bearerToken: "bearer-token",
};
}
const relayTestLayer = managedRelayClientLayer("https://relay.example.test").pipe(
Layer.provide(Layer.mergeAll(FetchHttpClient.layer, cryptoLayer)),
);
const runBackgroundOperations = Effect.fn("TestRemoteRegistration.runBackgroundOperations")(
function* () {
let idlePasses = 0;
for (;;) {
yield* Effect.promise(() => Promise.resolve());
const pending = backgroundRuntime.pending.shift();
if (!pending) {
idlePasses++;
if (idlePasses >= 3) {
return;
}
continue;
}
idlePasses = 0;
const exit = yield* Effect.exit(
pending.operation as Effect.Effect,
);
yield* Effect.sync(() => {
pending.resolve(exit);
});
}
},
);
describe("makeRelayDeviceRegistrationRequest", () => {
beforeEach(() => {
vi.unstubAllGlobals();
vi.stubGlobal("__DEV__", false);
secureStore.clear();
backgroundRuntime.pending.length = 0;
Constants.expoConfig!.extra = {};
__resetAgentAwarenessRemoteRegistrationForTest();
appStateMock.listeners.length = 0;
registrationRecordStore.current = null;
vi.mocked(saveAgentAwarenessRegistrationRecord).mockClear();
vi.mocked(loadAgentAwarenessRegistrationRecord).mockClear();
vi.mocked(clearAgentAwarenessRegistrationRecord).mockClear();
vi.mocked(loadOrCreateAgentAwarenessDeviceId).mockResolvedValue("device-1");
widgetMocks.getInstances.mockReset();
widgetMocks.getInstances.mockReturnValue([]);
widgetMocks.start.mockClear();
environmentConfigsMock.configs.clear();
});
it("preserves disabled Live Activity preferences in relay registrations", () => {
expect(
makeRelayDeviceRegistrationRequest({
deviceId: "device-1",
label: "Julius's iPhone",
iosMajorVersion: 18,
appVersion: "1.0.0",
pushToken: "apns-token",
pushToStartToken: "push-to-start-token",
notificationsEnabled: true,
preferences: {
liveActivitiesEnabled: false,
},
}),
).toEqual({
deviceId: "device-1",
label: "Julius's iPhone",
platform: "ios",
iosMajorVersion: 18,
appVersion: "1.0.0",
pushToken: "apns-token",
pushToStartToken: "push-to-start-token",
preferences: {
liveActivitiesEnabled: false,
notificationsEnabled: true,
notifyOnApproval: true,
notifyOnInput: true,
notifyOnCompletion: true,
notifyOnFailure: true,
},
});
});
it("registers the app's APNs routing so the relay targets the right bundle", () => {
expect(
makeRelayDeviceRegistrationRequest({
deviceId: "device-1",
label: "Julius's iPhone",
iosMajorVersion: 18,
appVersion: "1.0.0",
bundleId: "com.t3tools.t3code.preview",
apsEnvironment: resolveApsEnvironment("preview"),
notificationsEnabled: true,
preferences: {},
}),
).toMatchObject({
bundleId: "com.t3tools.t3code.preview",
apsEnvironment: "production",
});
});
it("routes development builds to the APNs sandbox", () => {
expect(resolveApsEnvironment("development")).toBe("sandbox");
expect(resolveApsEnvironment("preview")).toBe("production");
expect(resolveApsEnvironment("production")).toBe("production");
expect(resolveApsEnvironment(undefined)).toBe("production");
});
it("disables push features in Personal Team relay registrations", () => {
Constants.expoConfig!.extra = { iosPersonalTeamBuild: true };
expect(
makeRelayDeviceRegistrationRequest({
deviceId: "device-1",
label: "Julius's iPhone",
iosMajorVersion: 18,
appVersion: "1.0.0",
pushToken: "apns-token",
pushToStartToken: "push-to-start-token",
notificationsEnabled: true,
preferences: {},
}).preferences,
).toMatchObject({
liveActivitiesEnabled: false,
notificationsEnabled: false,
});
});
it("marks notification delivery disabled when APNs permission is unavailable", () => {
expect(
makeRelayDeviceRegistrationRequest({
deviceId: "device-1",
label: "Julius's iPhone",
iosMajorVersion: 18,
appVersion: "1.0.0",
pushToStartToken: "push-to-start-token",
notificationsEnabled: false,
preferences: {
liveActivitiesEnabled: true,
},
}),
).toEqual({
deviceId: "device-1",
label: "Julius's iPhone",
platform: "ios",
iosMajorVersion: 18,
appVersion: "1.0.0",
pushToStartToken: "push-to-start-token",
preferences: {
liveActivitiesEnabled: true,
notificationsEnabled: false,
notifyOnApproval: true,
notifyOnInput: true,
notifyOnCompletion: true,
notifyOnFailure: true,
},
});
});
it("normalizes relay base URLs for APNs registration requests", () => {
expect(normalizeAgentAwarenessRelayBaseUrl(" https://relay.example.test/// ")).toBe(
"https://relay.example.test",
);
expect(normalizeAgentAwarenessRelayBaseUrl(" ")).toBeNull();
});
it("overrides persisted preferences for an in-flight registration", () => {
expect(
mergeAgentAwarenessRegistrationPreferences(
{ liveActivitiesEnabled: false, baseFontSize: 18 },
{ liveActivitiesEnabled: true },
),
).toEqual({ liveActivitiesEnabled: true, baseFontSize: 18 });
});
it.effect("registers at most one listener while a Live Activity push token is pending", () => {
registerAgentAwarenessConnection(savedConnection());
const addPushTokenListener = vi.fn();
const activity = {
getPushToken: vi.fn(() => Promise.resolve(null)),
addPushTokenListener,
};
return Effect.gen(function* () {
expect(yield* registerLiveActivityPushToken({ activity: activity as never })).toBe(false);
expect(yield* registerLiveActivityPushToken({ activity: activity as never })).toBe(false);
expect(activity.getPushToken).toHaveBeenCalledTimes(2);
expect(addPushTokenListener).toHaveBeenCalledTimes(1);
}).pipe(Effect.provide(relayTestLayer));
});
it.effect("preserves Live Activity push-token lookup failures", () => {
const cause = new Error("native token lookup failed");
const activity = {
getPushToken: vi.fn(() => Promise.reject(cause)),
addPushTokenListener: vi.fn(),
};
return Effect.gen(function* () {
const error = yield* Effect.flip(
registerLiveActivityPushToken({ activity: activity as never }),
);
expect(error).toBeInstanceOf(AgentAwarenessOperationError);
expect(error).toMatchObject({
_tag: "AgentAwarenessOperationError",
operation: "read-live-activity-push-token",
cause,
message: "Agent awareness operation read-live-activity-push-token failed.",
});
}).pipe(Effect.provide(relayTestLayer));
});
it.effect(
"reports Live Activity token registration as skipped when relay auth is unavailable",
() => {
registerAgentAwarenessConnection(savedConnection());
const activity = {
getPushToken: vi.fn(() => Promise.resolve("activity-token")),
addPushTokenListener: vi.fn(),
};
return Effect.gen(function* () {
expect(yield* registerLiveActivityPushToken({ activity: activity as never })).toBe(false);
}).pipe(Effect.provide(relayTestLayer));
},
);
it.effect(
"registers APNS-started Live Activities for relay updates without mutating them locally",
() => {
const activity = {
getPushToken: vi.fn(() => Promise.resolve("activity-token")),
addPushTokenListener: vi.fn(),
start: vi.fn(),
update: vi.fn(),
end: vi.fn(),
};
widgetMocks.getInstances.mockReturnValue([activity] as never);
setAgentAwarenessRelayTokenProvider(() => Promise.resolve("clerk-token-user-a"));
return Effect.gen(function* () {
yield* refreshActiveLiveActivityRemoteRegistration();
expect(activity.getPushToken).toHaveBeenCalled();
expect(activity.start).not.toHaveBeenCalled();
expect(activity.update).not.toHaveBeenCalled();
expect(activity.end).not.toHaveBeenCalled();
}).pipe(Effect.provide(relayTestLayer));
},
);
it.effect(
"re-registers active Live Activity tokens when the app returns to the foreground",
() => {
const activity = {
getPushToken: vi.fn(() => Promise.resolve("activity-token")),
addPushTokenListener: vi.fn(),
};
widgetMocks.getInstances.mockReturnValue([activity] as never);
setAgentAwarenessRelayTokenProvider(() => Promise.resolve("clerk-token-user-a"));
return Effect.gen(function* () {
yield* runBackgroundOperations();
activity.getPushToken.mockClear();
expect(appStateMock.listeners).toHaveLength(1);
for (const listener of appStateMock.listeners) {
listener("background");
}
yield* runBackgroundOperations();
expect(activity.getPushToken).not.toHaveBeenCalled();
for (const listener of appStateMock.listeners) {
listener("active");
}
yield* runBackgroundOperations();
expect(activity.getPushToken).toHaveBeenCalled();
}).pipe(Effect.provide(relayTestLayer));
},
);
it("ends local Live Activities and stops foreground reconciliation on cloud sign-out", () => {
const end = vi.fn(() => Promise.resolve());
const activity = {
getPushToken: vi.fn(() => Promise.resolve("activity-token")),
addPushTokenListener: vi.fn(),
end,
};
widgetMocks.getInstances.mockReturnValue([activity] as never);
setAgentAwarenessRelayTokenProvider(() => Promise.resolve("clerk-token-user-a"));
expect(appStateMock.listeners).toHaveLength(1);
setAgentAwarenessRelayTokenProvider(null);
expect(end).toHaveBeenCalledWith("immediate");
expect(appStateMock.listeners).toHaveLength(0);
});
it.effect("refreshes APNs registration for connected environments after settings changes", () => {
registerAgentAwarenessConnection(savedConnection());
return Effect.gen(function* () {
yield* runBackgroundOperations();
vi.mocked(Notifications.getDevicePushTokenAsync).mockClear();
yield* refreshAgentAwarenessRegistration();
expect(Notifications.getDevicePushTokenAsync).toHaveBeenCalledTimes(1);
}).pipe(Effect.provide(relayTestLayer));
});
it.effect("registers the APNs device when cloud auth becomes available", () => {
const fetchMock = vi.fn((request: RequestInfo | URL) => {
const url = request instanceof Request ? request.url : String(request);
return Promise.resolve(
Response.json(
url.endsWith("/v1/client/dpop-token")
? {
access_token: "relay-dpop-token",
issued_token_type: "urn:ietf:params:oauth:token-type:access_token",
token_type: "DPoP",
expires_in: 300,
scope: "mobile:registration",
}
: { ok: true },
),
);
});
vi.stubGlobal("fetch", fetchMock);
Constants.expoConfig!.extra = {
relay: {
url: "https://relay.example.test/",
},
};
setAgentAwarenessRelayTokenProvider(() => Promise.resolve("clerk-token-user-a"));
return Effect.gen(function* () {
yield* runBackgroundOperations();
expect(fetchMock).toHaveBeenCalledTimes(2);
const [request, init] = fetchMock.mock.calls[1] as unknown as [
unknown,
RequestInit | undefined,
];
const url = request instanceof Request ? request.url : String(request);
const method = request instanceof Request ? request.method : init?.method;
const headers = request instanceof Request ? request.headers : new Headers(init?.headers);
const dpop = headers.get("dpop");
expect(url).toBe("https://relay.example.test/v1/mobile/devices");
expect(method).toBe("POST");
expect(headers.get("authorization")).toBe("DPoP relay-dpop-token");
expect(dpop).toEqual(expect.any(String));
if (!dpop) {
throw new Error("Missing DPoP header.");
}
expect(
verifyDpopProof({
proof: dpop,
method: "POST",
url: "https://relay.example.test/v1/mobile/devices",
expectedAccessToken: "relay-dpop-token",
nowEpochSeconds: proofIat(dpop),
}),
).toMatchObject({ ok: true });
expect(getAgentAwarenessRegistrationStatus()).toBe("registered");
}).pipe(Effect.provide(relayTestLayer));
});
it.effect("marks registration failed when device registration cannot complete", () => {
Constants.expoConfig!.extra = {
relay: {
url: "https://relay.example.test/",
},
};
vi.mocked(loadOrCreateAgentAwarenessDeviceId).mockRejectedValueOnce(
new Error("registration failed"),
);
setAgentAwarenessRelayTokenProvider(() => Promise.resolve("clerk-token-user-a"));
return Effect.gen(function* () {
// Drive the registration directly so the assertion does not depend on the
// background queue draining; refreshAgentAwarenessRegistration swallows the
// error but must record the failed status so the settings toggles cannot
// read as enabled.
yield* refreshAgentAwarenessRegistration();
expect(getAgentAwarenessRegistrationStatus()).toBe("failed");
}).pipe(Effect.provide(relayTestLayer));
});
it("clears registration status on cloud sign-out", () => {
setAgentAwarenessRelayTokenProvider(() => Promise.resolve("clerk-token-user-a"));
setAgentAwarenessRelayTokenProvider(null);
expect(getAgentAwarenessRegistrationStatus()).toBe("unknown");
expect(clearAgentAwarenessRegistrationRecord).toHaveBeenCalled();
});
it("releases the provider without ending activities or clearing the registration", () => {
const end = vi.fn(() => Promise.resolve());
const activity = {
getPushToken: vi.fn(() => Promise.resolve("activity-token")),
addPushTokenListener: vi.fn(),
end,
};
widgetMocks.getInstances.mockReturnValue([activity] as never);
registrationRecordStore.current = { identity: "", signature: "sig" };
setAgentAwarenessRelayTokenProvider(() => Promise.resolve("clerk-token-user-a"));
expect(appStateMock.listeners).toHaveLength(1);
releaseAgentAwarenessRelayTokenProvider();
expect(appStateMock.listeners).toHaveLength(0);
expect(end).not.toHaveBeenCalled();
expect(clearAgentAwarenessRegistrationRecord).not.toHaveBeenCalled();
expect(registrationRecordStore.current).not.toBeNull();
});
it.effect("resets a pending status to unknown when relay config is missing", () => {
// No relay url configured: registration can neither run nor ever succeed,
// so the status must not stick at "pending".
setAgentAwarenessRelayTokenProvider(() => Promise.resolve("clerk-token-user-a"));
return Effect.gen(function* () {
yield* runBackgroundOperations();
expect(getAgentAwarenessRegistrationStatus()).toBe("unknown");
}).pipe(Effect.provide(relayTestLayer));
});
it.effect("keeps a registered status when a later refresh fails", () => {
Constants.expoConfig!.extra = {
relay: {
url: "https://relay.example.test/",
},
};
setAgentAwarenessRelayTokenProvider(() => Promise.resolve("clerk-token-user-a"));
return Effect.gen(function* () {
yield* runBackgroundOperations();
expect(getAgentAwarenessRegistrationStatus()).toBe("registered");
// The relay still holds the accepted registration; a transient refresh
// failure must not flip the settings toggles off.
vi.mocked(loadOrCreateAgentAwarenessDeviceId).mockRejectedValueOnce(
new Error("transient failure"),
);
yield* refreshAgentAwarenessRegistration();
expect(getAgentAwarenessRegistrationStatus()).toBe("registered");
}).pipe(Effect.provide(relayTestLayer));
});
it.effect("does not re-register the same account when nothing has changed", () => {
Constants.expoConfig!.extra = {
relay: {
url: "https://relay.example.test/",
},
};
setAgentAwarenessRelayTokenProvider(() => Promise.resolve("clerk-token-user-a"));
return Effect.gen(function* () {
yield* refreshAgentAwarenessRegistration();
expect(getAgentAwarenessRegistrationStatus()).toBe("registered");
expect(saveAgentAwarenessRegistrationRecord).toHaveBeenCalledTimes(1);
expect(registrationRecordStore.current).not.toBeNull();
// Second attempt with an identical payload must skip the relay entirely,
// so no new registration record is written.
vi.mocked(saveAgentAwarenessRegistrationRecord).mockClear();
yield* refreshAgentAwarenessRegistration();
expect(getAgentAwarenessRegistrationStatus()).toBe("registered");
expect(saveAgentAwarenessRegistrationRecord).not.toHaveBeenCalled();
}).pipe(Effect.provide(relayTestLayer));
});
it.effect("dedupes rapid activity-token re-registrations within the replay window", () => {
// Fetch counts are unreliable here (the module-level relay layer captures
// the first test's fetch), so assert on the flow's own seams: a real
// registration attempt loads the device id, a deduped one short-circuits
// before it.
const fetchMock = vi.fn((request: RequestInfo | URL) => {
const url = request instanceof Request ? request.url : String(request);
return Promise.resolve(
Response.json(
url.endsWith("/v1/client/dpop-token")
? {
access_token: "relay-dpop-token",
issued_token_type: "urn:ietf:params:oauth:token-type:access_token",
token_type: "DPoP",
expires_in: 300,
scope: "mobile:registration",
}
: { ok: true },
),
);
});
vi.stubGlobal("fetch", fetchMock);
Constants.expoConfig!.extra = {
relay: {
url: "https://relay.example.test/",
},
};
const activity = {
getPushToken: vi.fn(() => Promise.resolve("activity-token")),
addPushTokenListener: vi.fn(),
};
widgetMocks.getInstances.mockReturnValue([activity] as never);
setAgentAwarenessRelayTokenProvider(() => Promise.resolve("clerk-token-user-a"));
return Effect.gen(function* () {
// Drains the sign-in refresh, which registers the activity token.
yield* runBackgroundOperations();
expect(activity.getPushToken).toHaveBeenCalled();
// A burst refresh (foreground / connection update seconds later) must
// dedupe: it reads the token but never proceeds to a registration
// attempt (which would load the device id first).
vi.mocked(loadOrCreateAgentAwarenessDeviceId).mockClear();
yield* refreshActiveLiveActivityRemoteRegistration();
expect(loadOrCreateAgentAwarenessDeviceId).not.toHaveBeenCalled();
}).pipe(Effect.provide(relayTestLayer));
});
it.effect("re-registers when the stored account identity differs", () => {
Constants.expoConfig!.extra = {
relay: {
url: "https://relay.example.test/",
},
};
registrationRecordStore.current = { identity: "someone-else", signature: "stale" };
setAgentAwarenessRelayTokenProvider(() => Promise.resolve("clerk-token-user-a"));
return Effect.gen(function* () {
yield* refreshAgentAwarenessRegistration();
expect(saveAgentAwarenessRegistrationRecord).toHaveBeenCalledTimes(1);
}).pipe(Effect.provide(relayTestLayer));
});
it.effect("coalesces simultaneous sign-in and environment connection registrations", () => {
const fetchMock = vi.fn((request: RequestInfo | URL) => {
const url = request instanceof Request ? request.url : String(request);
return Promise.resolve(
Response.json(
url.endsWith("/v1/client/dpop-token")
? {
access_token: "relay-dpop-token",
issued_token_type: "urn:ietf:params:oauth:token-type:access_token",
token_type: "DPoP",
expires_in: 300,
scope: "mobile:registration",
}
: { ok: true },
),
);
});
vi.stubGlobal("fetch", fetchMock);
Constants.expoConfig!.extra = {
relay: {
url: "https://relay.example.test/",
},
};
vi.mocked(Notifications.getPermissionsAsync).mockClear();
setAgentAwarenessRelayTokenProvider(() => Promise.resolve("clerk-token-user-a"));
registerAgentAwarenessConnection(savedConnection());
return Effect.gen(function* () {
yield* runBackgroundOperations();
expect(Notifications.getPermissionsAsync).toHaveBeenCalledTimes(1);
}).pipe(Effect.provide(relayTestLayer));
});
it.effect("continues queued device registration after a failed auth lookup", () => {
Constants.expoConfig!.extra = {
relay: {
url: "https://relay.example.test/",
},
};
const tokenProvider = vi
.fn<() => Promise>()
.mockRejectedValueOnce(new Error("auth unavailable"))
.mockResolvedValue("clerk-token-user-a");
setAgentAwarenessRelayTokenProvider(tokenProvider);
const tokenListener = vi.mocked(Notifications.addPushTokenListener).mock.calls.at(-1)?.[0];
expect(tokenListener).toBeDefined();
tokenListener?.({ type: "ios", data: "rotated-apns-token" } as never);
return Effect.gen(function* () {
yield* runBackgroundOperations();
expect(backgroundRuntime.pending).toHaveLength(0);
expect(tokenProvider).toHaveBeenCalledTimes(2);
}).pipe(Effect.provide(relayTestLayer));
});
it("only registers again when the authenticated identity changes", () => {
expect(shouldRegisterAgentAwarenessDeviceForProvider(null, "user-a")).toBe(true);
expect(shouldRegisterAgentAwarenessDeviceForProvider("user-a", "user-a")).toBe(false);
expect(shouldRegisterAgentAwarenessDeviceForProvider("user-a", "user-b")).toBe(true);
expect(shouldRegisterAgentAwarenessDeviceForProvider("user-a", undefined)).toBe(true);
});
it.effect("registers rotated APNs tokens without rereading the native token", () => {
const fetchMock = vi.fn((request: RequestInfo | URL) => {
const url = request instanceof Request ? request.url : String(request);
return Promise.resolve(
Response.json(
url.endsWith("/v1/client/dpop-token")
? {
access_token: "relay-dpop-token",
issued_token_type: "urn:ietf:params:oauth:token-type:access_token",
token_type: "DPoP",
expires_in: 300,
scope: "mobile:registration",
}
: { ok: true },
),
);
});
vi.stubGlobal("fetch", fetchMock);
Constants.expoConfig!.extra = {
relay: {
url: "https://relay.example.test/",
},
};
vi.mocked(Notifications.getDevicePushTokenAsync).mockClear();
setAgentAwarenessRelayTokenProvider(() => Promise.resolve("clerk-token-user-a"));
const tokenListener = vi.mocked(Notifications.addPushTokenListener).mock.calls.at(-1)?.[0];
expect(tokenListener).toBeDefined();
tokenListener?.({ type: "ios", data: "rotated-apns-token" } as never);
return Effect.gen(function* () {
yield* runBackgroundOperations();
expect(Notifications.getDevicePushTokenAsync).toHaveBeenCalledTimes(1);
}).pipe(Effect.provide(relayTestLayer));
});
it.effect(
"keeps the user-scoped relay APNs device when an environment connection is removed",
() => {
const fetchMock = vi.fn((request: RequestInfo | URL) => {
const url = request instanceof Request ? request.url : String(request);
return Promise.resolve(
Response.json(
url.endsWith("/v1/client/dpop-token")
? {
access_token: "relay-dpop-token",
issued_token_type: "urn:ietf:params:oauth:token-type:access_token",
token_type: "DPoP",
expires_in: 300,
scope: "mobile:registration",
}
: { ok: true },
),
);
});
vi.stubGlobal("fetch", fetchMock);
Constants.expoConfig!.extra = {
relay: {
url: "https://relay.example.test/",
},
};
registerAgentAwarenessConnection(savedConnection());
setAgentAwarenessRelayTokenProvider(() => Promise.resolve("clerk-token-user-a"));
return Effect.gen(function* () {
yield* runBackgroundOperations();
fetchMock.mockClear();
unregisterAgentAwarenessConnection(savedConnection().environmentId);
expect(fetchMock).not.toHaveBeenCalled();
}).pipe(Effect.provide(relayTestLayer));
},
);
it("skips the Live Activity seed when the environment reports publishing disabled", async () => {
setAgentAwarenessRelayTokenProvider(() => Promise.resolve("clerk-token-user-a"));
vi.mocked(loadPreferences).mockResolvedValueOnce({
liveActivitiesEnabled: true,
} as Preferences);
environmentConfigsMock.configs.set("env-1", {
environment: { capabilities: { agentActivityPublishing: false } },
});
armAgentAwarenessLiveActivityForLocalWork({
environmentId: "env-1" as EnvironmentId,
threadTitle: "Fix the flaky test",
projectTitle: "t3code",
});
await new Promise((resolve) => setTimeout(resolve, 0));
expect(widgetMocks.start).not.toHaveBeenCalled();
});
it("seeds the Live Activity for publishing and pre-capability environments", async () => {
setAgentAwarenessRelayTokenProvider(() => Promise.resolve("clerk-token-user-a"));
environmentConfigsMock.configs.set("env-publishing", {
environment: { capabilities: { agentActivityPublishing: true } },
});
vi.mocked(loadPreferences).mockResolvedValueOnce({
liveActivitiesEnabled: true,
} as Preferences);
armAgentAwarenessLiveActivityForLocalWork({
environmentId: "env-publishing" as EnvironmentId,
threadTitle: "Fix the flaky test",
projectTitle: "t3code",
});
await new Promise((resolve) => setTimeout(resolve, 0));
expect(widgetMocks.start).toHaveBeenCalledTimes(1);
// An environment without the capability may run an older server that
// still publishes; only an explicit false skips the seed.
widgetMocks.start.mockClear();
vi.mocked(loadPreferences).mockResolvedValueOnce({
liveActivitiesEnabled: true,
} as Preferences);
armAgentAwarenessLiveActivityForLocalWork({
environmentId: "env-pre-capability" as EnvironmentId,
threadTitle: "Fix the flaky test",
projectTitle: "t3code",
});
await new Promise((resolve) => setTimeout(resolve, 0));
expect(widgetMocks.start).toHaveBeenCalledTimes(1);
});
});