From 29ea6ee05b04b1089fd6f02b5a625bf57c2bd867 Mon Sep 17 00:00:00 2001 From: Shoubhit Dash Date: Fri, 25 Sep 2026 22:37:20 +0530 Subject: [PATCH] test(ai): cover media facade selectors and url asset edges (#51376) --- packages/ai/test/exports.test.ts | 31 +++++++++++++++++++ packages/ai/test/media.test.ts | 52 +++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/packages/ai/test/exports.test.ts b/packages/ai/test/exports.test.ts index 2748b933ad9..c924e4e8406 100644 --- a/packages/ai/test/exports.test.ts +++ b/packages/ai/test/exports.test.ts @@ -23,6 +23,7 @@ import { Provider as ProviderSubpath } from "@opencode/ai/provider" import { AssemblyAI, Baseten, + BlackForestLabs, Cartesia, CloudflareAIGateway, CloudflareWorkersAI, @@ -32,14 +33,18 @@ import { Fal, Fireworks, Google, + Meta, OpenCodeZen, OpenAI, OpenAICompatible, OpenRouter, + Replicate, Runway, + Stability, TypeSafeAI, VercelAIGateway, XAI, + ZAI, } from "@opencode/ai/providers" import { OpenAIChat, @@ -151,8 +156,34 @@ describe("public exports", () => { expect(XAI.provider.chat).toBe(XAI.chat) expect(XAI.configure({ apiKey: "fixture" }).responses("grok-4.3").route.id).toBe("openai-responses") expect(XAI.configure({ apiKey: "fixture" }).chat("grok-4.3").route.id).toBe("openai-compatible-chat") + expect(OpenAI.configure({ apiKey: "fixture" }).image("gpt-image-2").route.id).toBe("openai-images") + expect(OpenAI.provider.image).toBe(OpenAI.image) + expect(Google.configure({ apiKey: "fixture" }).image("imagen-4.0-generate-001").route.id).toBe("google-images") + expect(Google.provider.image).toBe(Google.image) + expect(XAI.configure({ apiKey: "fixture" }).image("grok-imagine-image").route.id).toBe("xai-images") + expect(XAI.provider.image).toBe(XAI.image) + expect(Fal.configure({ apiKey: "fixture" }).image("fal-ai/flux/dev").route.id).toBe("fal-images") + expect(Fal.provider.image).toBe(Fal.image) + expect(BlackForestLabs.configure({ apiKey: "fixture" }).image("flux-2-pro").route.id).toBe("bfl-images") + expect(BlackForestLabs.provider.image).toBe(BlackForestLabs.image) + expect(Replicate.configure({ apiKey: "fixture" }).image("black-forest-labs/flux-schnell").route.id).toBe( + "replicate-images", + ) + expect(Replicate.provider.image).toBe(Replicate.image) + expect(Stability.configure({ apiKey: "fixture" }).image("sd3.5-large").route.id).toBe("stability-images") + expect(Stability.provider.image).toBe(Stability.image) + expect(Stability.configure({ apiKey: "fixture" }).upscale().route.id).toBe("stability-upscale") + expect(Stability.provider.upscale).toBe(Stability.upscale) + expect(Meta.configure({ apiKey: "fixture" }).image("muse-image").route.id).toBe("meta-images") + expect(Meta.provider.image).toBe(Meta.image) + expect(ZAI.configure({ apiKey: "fixture" }).image("glm-image").route.id).toBe("zai-images") + expect(ZAI.provider.image).toBe(ZAI.image) expect(XAI.configure({ apiKey: "fixture" }).video("grok-imagine-video-1.5").route.id).toBe("xai-video") + expect(XAI.provider.video).toBe(XAI.video) + expect(Google.configure({ apiKey: "fixture" }).video("veo-3.1-generate-preview").route.id).toBe("google-video") + expect(Google.provider.video).toBe(Google.video) expect(Fal.configure({ apiKey: "fixture" }).video("fal-ai/veo3.1").route.id).toBe("fal-video") + expect(Fal.provider.video).toBe(Fal.video) expect(Runway.configure({ apiKey: "fixture" }).video("gen4.5").route.id).toBe("runway-video") expect(Runway.provider.video).toBe(Runway.video) expect(OpenAI.configure({ apiKey: "fixture" }).speech("gpt-4o-mini-tts").route.id).toBe("openai-speech") diff --git a/packages/ai/test/media.test.ts b/packages/ai/test/media.test.ts index a6480b4adbd..32fc8f65560 100644 --- a/packages/ai/test/media.test.ts +++ b/packages/ai/test/media.test.ts @@ -3,7 +3,7 @@ import { NodeFileSystem } from "@effect/platform-node" import { Effect, Ref, Schema } from "effect" import { FileSystem } from "effect" import { HttpClientRequest } from "effect/unstable/http" -import { Media, Message } from "../src/index.js" +import { AIError, Media, Message } from "../src/index.js" import { it } from "./lib/effect.js" import { dynamicResponse, scriptedResponses } from "./lib/http.js" @@ -161,6 +161,56 @@ describe("Media", () => { }), ) + it.effect("keeps transient url download headers out of toJSON and AssetSchema encoding", () => + Effect.sync(() => { + const asset = Media.url("https://cdn.example.test/video.mp4", { + mediaType: "video/mp4", + expiresAt: 42, + headers: { "x-goog-api-key": "secret" }, + }) + expect(asset.headers).toEqual({ "x-goog-api-key": "secret" }) + const source = { type: "url", url: "https://cdn.example.test/video.mp4", mediaType: "video/mp4", expiresAt: 42 } + + expect(asset.toJSON()).not.toHaveProperty("headers") + expect(JSON.stringify(asset)).not.toContain("secret") + expect(asset.toJSON().source).toEqual(source) + + const encoded = Schema.encodeSync(Media.AssetSchema)(asset) + expect(encoded).not.toHaveProperty("headers") + expect(encoded.source).toEqual(source) + + const codec = Schema.fromJsonString(Media.AssetSchema) + const json = Schema.encodeSync(codec)(asset) + expect(json).not.toContain("secret") + const restored = Schema.decodeSync(codec)(json) + expect(restored).toBeInstanceOf(Media.Asset) + expect(restored.source).toEqual(source) + expect(restored.expiresAt).toBe(42) + expect(restored.headers).toBeUndefined() + }), + ) + + it.effect("fails url downloads with non-2xx status as a typed AIError keeping http and body", () => + Effect.gen(function* () { + const body = JSON.stringify({ error: { message: "file expired" } }) + const error = yield* Media.url("https://cdn.example.test/expired.png") + .bytes() + .pipe( + Effect.flip, + Effect.provide( + dynamicResponse((input) => + Effect.succeed(input.respond(body, { status: 404, headers: { "content-type": "application/json" } })), + ), + ), + ) + expect(error).toBeInstanceOf(AIError) + expect(error.message).toContain("file expired") + expect(error.reason.http?.status).toBe(404) + expect(error.reason.http?.url).toBe("https://cdn.example.test/expired.png") + expect(error.reason.body).toBe(body) + }), + ) + it.effect("reads files with sniffed media types and writes materialized assets", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem