import { Asset, Resource } from "@noya-app/noya-schemas"; import { describe, expect, it } from "bun:test"; import { getResourceUrl, resolveResource } from "../getResourceUrl"; import { NoyaManager } from "../NoyaManager"; const baseResource = { accessibleByFileId: null, accessibleByFileVersionId: null, createdAt: "2026-01-01T00:00:00.000Z", updatedAt: "2026-01-01T00:00:00.000Z", }; const asset = { id: "asset-id", stableId: "asset-stable-id", url: "blob:http://localhost/image", } as Asset; const resource = { ...baseResource, id: "resource-id", stableId: "resource-stable-id", path: "image.png", type: "asset", assetId: asset.stableId, url: "/api/resources/resource-id", } satisfies Resource; describe("getResourceUrl", () => { it("resolves resource IDs and stable IDs to the same URL", () => { expect(getResourceUrl(resource.id, [resource], [asset])).toBe(asset.url); expect(getResourceUrl(resource.stableId, [resource], [asset])).toBe( asset.url ); }); it("falls back to the resource URL when its asset is unavailable", () => { expect(getResourceUrl(resource.id, [resource], [])).toBe(resource.url); }); it("prefers a signed resource transform over the linked asset URL", () => { const transformUrl = "https://www.noya.io/api/transform/resource-id?token=signed-token"; const imageUrl = `${transformUrl}&format=image`; const signedResource = { ...resource, transformUrl, url: imageUrl, }; expect(getResourceUrl(resource.id, [signedResource], [asset])).toBe( imageUrl ); }); it("selects the image format when only a transform capability is present", () => { const transformUrl = "https://www.noya.io/api/transform/resource-id?token=signed-token"; const signedResource = { ...resource, url: undefined, transformUrl, }; expect(getResourceUrl(resource.id, [signedResource], [asset])).toBe( `${transformUrl}&format=image` ); }); it("adds shape-specific inputs to signed image transforms", () => { const transformUrl = "https://www.noya.io/api/transform/resource-id?token=signed-token"; const signedResource = { ...resource, transformUrl, url: `${transformUrl}&format=image`, }; const url = getResourceUrl(resource.id, [signedResource], [asset], { inputs: { width: 640, background: "blue" }, }); const parsed = new URL(url!); expect(parsed.searchParams.get("token")).toBe("signed-token"); expect(parsed.searchParams.get("format")).toBe("image"); expect(JSON.parse(parsed.searchParams.get("inputs")!)).toEqual({ width: 640, background: "blue", }); }); it("returns undefined when the resource does not exist", () => { expect(getResourceUrl("missing-resource", [resource], [asset])).toBe( undefined ); }); }); describe("resolveResource", () => { it("returns the resource, asset, and URL for an asset resource", async () => { const manager = new NoyaManager(null, { initialAssets: [asset], initialResources: [resource], }); const resolved = await resolveResource(resource.stableId, manager); expect(resolved.resource).toEqual(resource); expect(resolved.asset?.id).toBe(asset.stableId); expect(resolved.url).toBe(asset.url); }); it("refreshes resources when the selected resource has not synced yet", async () => { const manager = new NoyaManager(null, { initialAssets: [asset], initialResources: [], }); manager.resourceManager.listResources = async () => [resource]; const resolved = await resolveResource(resource.stableId, manager); expect(resolved.resource).toEqual(resource); expect(manager.resourceManager.resources$.get()).toEqual([resource]); }); });