Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions apps/desktop/src/main/services/adeActions/registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,75 @@ describe("ADE_ACTION_ALLOWLIST shape", () => {
});
});

describe("runtime APNs action service", () => {
it("does not read APNs dependencies when resolving an unrelated domain", () => {
const runtime = {
laneService: {
list: vi.fn(),
},
projectConfigService: {
get: vi.fn(),
},
get apnsService() {
throw new Error("apnsService should not be read for lane actions");
},
get apnsKeyStore() {
throw new Error("apnsKeyStore should not be read for lane actions");
},
} as unknown as Parameters<typeof getAdeActionDomainServices>[0];

const services = getAdeActionDomainServices(runtime);
const laneService = services.lane as Record<string, unknown>;

expect(Object.keys(services)).toContain("notifications_apns");
expect(laneService.list).toEqual(expect.any(Function));
});

it("reuses the APNs bridge for repeated lookups on a stable runtime", () => {
const runtime = {
projectConfigService: {
get: vi.fn(() => ({ effective: {}, shared: {}, local: {} })),
},
apnsService: {
isConfigured: vi.fn(() => false),
},
apnsKeyStore: {
has: vi.fn(() => false),
},
} as unknown as Parameters<typeof getAdeActionDomainServices>[0];

const first = getAdeActionDomainServices(runtime).notifications_apns;
const second = getAdeActionDomainServices(runtime).notifications_apns;

expect(second).toBe(first);
});

it("refreshes the cached APNs bridge when late-bound dependencies change", async () => {
const runtime = {
projectConfigService: {
get: vi.fn(() => ({ effective: {}, shared: {}, local: {} })),
},
apnsService: {
isConfigured: vi.fn(() => false),
},
apnsKeyStore: {
has: vi.fn(() => false),
},
} as any as Parameters<typeof getAdeActionDomainServices>[0];

const first = getAdeActionDomainServices(runtime).notifications_apns;
(runtime as any).apnsService = {
isConfigured: vi.fn(() => true),
};
const second = getAdeActionDomainServices(runtime).notifications_apns as {
getStatus: () => Promise<{ configured: boolean }>;
};

expect(second).not.toBe(first);
await expect(second.getStatus()).resolves.toMatchObject({ configured: true });
});
});

describe("runtime Linear issue tracker actions", () => {
it("builds catalog and picker payloads from tracker reads", async () => {
const projects = [{ id: "project-1", name: "ADE" }];
Expand Down
50 changes: 41 additions & 9 deletions apps/desktop/src/main/services/adeActions/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,44 @@ function toService(value: unknown): OpaqueService | null {
return (value ?? null) as OpaqueService | null;
}

type CachedApnsBridgeDomainService = {
projectConfigService: AdeRuntime["projectConfigService"];
apnsService: AdeRuntime["apnsService"];
apnsKeyStore: AdeRuntime["apnsKeyStore"];
service: OpaqueService;
};

const apnsBridgeDomainServices = new WeakMap<AdeRuntime, CachedApnsBridgeDomainService>();

function getApnsBridgeDomainService(runtime: AdeRuntime): OpaqueService {
const projectConfigService = runtime.projectConfigService;
const apnsService = runtime.apnsService;
const apnsKeyStore = runtime.apnsKeyStore;
const cached = apnsBridgeDomainServices.get(runtime);
if (
cached &&
cached.projectConfigService === projectConfigService &&
cached.apnsService === apnsService &&
cached.apnsKeyStore === apnsKeyStore
) {
return cached.service;
}
const service = createApnsBridgeService({
projectConfigService,
apnsService,
apnsKeyStore,
getDeviceRegistryService: () =>
runtime.syncService?.getDeviceRegistryService?.() ?? null,
}) as OpaqueService;
apnsBridgeDomainServices.set(runtime, {
projectConfigService,
apnsService,
apnsKeyStore,
service,
});
return service;
}

const MAX_TEMP_ATTACHMENT_BYTES = 10 * 1024 * 1024;

function agentChatParallelLaunchStateKey(projectRoot: string, parentLaneId: string): string {
Expand Down Expand Up @@ -2723,15 +2761,9 @@ export function getAdeActionDomainServices(
automations: toService(buildAutomationsDomainService(runtime)),
review: toService(runtime.reviewService),
issue: toService(buildIssueDomainService(runtime)),
notifications_apns: toService(
createApnsBridgeService({
projectConfigService: runtime.projectConfigService,
apnsService: runtime.apnsService,
apnsKeyStore: runtime.apnsKeyStore,
getDeviceRegistryService: () =>
runtime.syncService?.getDeviceRegistryService?.() ?? null,
}),
),
get notifications_apns() {
return toService(getApnsBridgeDomainService(runtime));
},
Comment thread
arul28 marked this conversation as resolved.
};
}

Expand Down
Loading