test(core): serve skill fixtures locally (#35429)

This commit is contained in:
Aiden Cline 2026-07-05 10:40:55 -05:00 committed by GitHub
commit 391aa38281
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,36 +1,42 @@
import fs from "fs/promises" import fs from "fs/promises"
import path from "path" import path from "path"
import { describe, expect, test } from "bun:test" import { describe, expect, test } from "bun:test"
import { Effect, Layer } from "effect" import { Effect } from "effect"
import { HttpClient, HttpClientResponse } from "effect/unstable/http"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { LayerNodePlatform } from "@opencode-ai/core/effect/app-node-platform"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { Global } from "@opencode-ai/core/global" import { Global } from "@opencode-ai/core/global"
import { SkillDiscovery } from "@opencode-ai/core/skill/discovery" import { SkillDiscovery } from "@opencode-ai/core/skill/discovery"
import { tmpdir } from "./fixture/tmpdir" import { tmpdir } from "./fixture/tmpdir"
const base = "https://skills.example.test/catalog/" type Fixture = {
tmp: Awaited<ReturnType<typeof tmpdir>>
server: Bun.Server<undefined>
state: {
skills: unknown[]
files: Record<string, string>
requests: string[]
}
base: string
}
async function pull(skills: unknown[], files: Record<string, string> = {}, cache?: Awaited<ReturnType<typeof tmpdir>>) { async function pull(skills: unknown[], files: Record<string, string> = {}, fixture?: Fixture) {
const tmp = cache ?? (await tmpdir()) const state = fixture?.state ?? { skills, files, requests: [] }
const requests: string[] = [] state.skills = skills
const http = Layer.succeed( state.files = files
HttpClient.HttpClient, state.requests = []
HttpClient.make((request) => const server =
Effect.sync(() => requests.push(request.url)).pipe( fixture?.server ??
Effect.map(() => { Bun.serve({
const body = request.url === `${base}index.json` ? JSON.stringify({ skills }) : files[request.url] port: 0,
return HttpClientResponse.fromWeb( fetch(request) {
request, state.requests.push(request.url)
new Response(body ?? "Not Found", { status: body === undefined ? 404 : 200 }), const pathname = new URL(request.url).pathname
) const body = pathname === "/catalog/index.json" ? JSON.stringify({ skills: state.skills }) : state.files[pathname]
}), return new Response(body ?? "Not Found", { status: body === undefined ? 404 : 200 })
), },
), })
) const tmp = fixture?.tmp ?? (await tmpdir())
const base = fixture?.base ?? new URL("/catalog/", server.url).href
const skillDiscoveryLayer = AppNodeBuilder.build(SkillDiscovery.node, [ const skillDiscoveryLayer = AppNodeBuilder.build(SkillDiscovery.node, [
[LayerNodePlatform.httpClient, http],
[Global.node, Global.layerWith({ cache: tmp.path })], [Global.node, Global.layerWith({ cache: tmp.path })],
]) ])
const directories = await Effect.runPromise( const directories = await Effect.runPromise(
@ -38,7 +44,12 @@ async function pull(skills: unknown[], files: Record<string, string> = {}, cache
return yield* (yield* SkillDiscovery.Service).pull(base) return yield* (yield* SkillDiscovery.Service).pull(base)
}).pipe(Effect.provide(skillDiscoveryLayer)), }).pipe(Effect.provide(skillDiscoveryLayer)),
) )
return { tmp, requests, directories } return { tmp, server, state, base, requests: state.requests, directories }
}
async function dispose(fixture: Fixture) {
await fixture.server.stop(true)
await fixture.tmp[Symbol.asyncDispose]()
} }
describe("SkillDiscovery.pull", () => { describe("SkillDiscovery.pull", () => {
@ -46,10 +57,10 @@ describe("SkillDiscovery.pull", () => {
const result = await pull([{ name: "../outside", files: ["SKILL.md"] }]) const result = await pull([{ name: "../outside", files: ["SKILL.md"] }])
try { try {
expect(result.directories).toEqual([]) expect(result.directories).toEqual([])
expect(result.requests).toEqual([`${base}index.json`]) expect(result.requests).toEqual([`${result.base}index.json`])
expect(await fs.readdir(result.tmp.path)).toEqual([]) expect(await fs.readdir(result.tmp.path)).toEqual([])
} finally { } finally {
await result.tmp[Symbol.asyncDispose]() await dispose(result)
} }
}) })
@ -57,10 +68,10 @@ describe("SkillDiscovery.pull", () => {
const result = await pull([{ name: "deploy", files: ["SKILL.md", "../outside.md"] }]) const result = await pull([{ name: "deploy", files: ["SKILL.md", "../outside.md"] }])
try { try {
expect(result.directories).toEqual([]) expect(result.directories).toEqual([])
expect(result.requests).toEqual([`${base}index.json`]) expect(result.requests).toEqual([`${result.base}index.json`])
expect(await fs.readdir(result.tmp.path)).toEqual([]) expect(await fs.readdir(result.tmp.path)).toEqual([])
} finally { } finally {
await result.tmp[Symbol.asyncDispose]() await dispose(result)
} }
}) })
@ -68,10 +79,10 @@ describe("SkillDiscovery.pull", () => {
const result = await pull([{ name: "deploy", files: ["SKILL.md", "/tmp/outside.md"] }]) const result = await pull([{ name: "deploy", files: ["SKILL.md", "/tmp/outside.md"] }])
try { try {
expect(result.directories).toEqual([]) expect(result.directories).toEqual([])
expect(result.requests).toEqual([`${base}index.json`]) expect(result.requests).toEqual([`${result.base}index.json`])
expect(await fs.readdir(result.tmp.path)).toEqual([]) expect(await fs.readdir(result.tmp.path)).toEqual([])
} finally { } finally {
await result.tmp[Symbol.asyncDispose]() await dispose(result)
} }
}) })
@ -79,87 +90,87 @@ describe("SkillDiscovery.pull", () => {
const result = await pull([{ name: "deploy", files: ["SKILL.md", "https://evil.example.test/outside.md"] }]) const result = await pull([{ name: "deploy", files: ["SKILL.md", "https://evil.example.test/outside.md"] }])
try { try {
expect(result.directories).toEqual([]) expect(result.directories).toEqual([])
expect(result.requests).toEqual([`${base}index.json`]) expect(result.requests).toEqual([`${result.base}index.json`])
expect(await fs.readdir(result.tmp.path)).toEqual([]) expect(await fs.readdir(result.tmp.path)).toEqual([])
} finally { } finally {
await result.tmp[Symbol.asyncDispose]() await dispose(result)
} }
}) })
test("downloads safe nested files under the skill root", async () => { test("downloads safe nested files under the skill root", async () => {
const result = await pull([{ name: "deploy", files: ["SKILL.md", "references/guide.md"] }], { const result = await pull([{ name: "deploy", files: ["SKILL.md", "references/guide.md"] }], {
[`${base}deploy/SKILL.md`]: "# Deploy", "/catalog/deploy/SKILL.md": "# Deploy",
[`${base}deploy/references/guide.md`]: "# Guide", "/catalog/deploy/references/guide.md": "# Guide",
}) })
try { try {
expect(result.directories).toHaveLength(1) expect(result.directories).toHaveLength(1)
expect(result.requests.toSorted()).toEqual( expect(result.requests.toSorted()).toEqual(
[`${base}index.json`, `${base}deploy/SKILL.md`, `${base}deploy/references/guide.md`].toSorted(), [
`${result.base}index.json`,
`${result.base}deploy/SKILL.md`,
`${result.base}deploy/references/guide.md`,
].toSorted(),
) )
expect(await fs.readFile(path.join(result.directories[0], "SKILL.md"), "utf8")).toBe("# Deploy") expect(await fs.readFile(path.join(result.directories[0], "SKILL.md"), "utf8")).toBe("# Deploy")
expect(await fs.readFile(path.join(result.directories[0], "references", "guide.md"), "utf8")).toBe("# Guide") expect(await fs.readFile(path.join(result.directories[0], "references", "guide.md"), "utf8")).toBe("# Guide")
} finally { } finally {
await result.tmp[Symbol.asyncDispose]() await dispose(result)
} }
}) })
test("refreshes cached files when the version changes", async () => { test("refreshes cached files when the version changes", async () => {
const tmp = await tmpdir() const first = await pull(
[{ name: "deploy", version: "1", files: ["SKILL.md"] }],
{ "/catalog/deploy/SKILL.md": "# Old" },
)
try { try {
const first = await pull(
[{ name: "deploy", version: "1", files: ["SKILL.md"] }],
{
[`${base}deploy/SKILL.md`]: "# Old",
},
tmp,
)
const second = await pull( const second = await pull(
[{ name: "deploy", version: "2", files: ["SKILL.md"] }], [{ name: "deploy", version: "2", files: ["SKILL.md"] }],
{ { "/catalog/deploy/SKILL.md": "# New" },
[`${base}deploy/SKILL.md`]: "# New", first,
},
tmp,
) )
expect(await fs.readFile(path.join(first.directories[0], "SKILL.md"), "utf8")).toBe("# New") expect(await fs.readFile(path.join(first.directories[0], "SKILL.md"), "utf8")).toBe("# New")
expect(second.requests).toContain(`${base}deploy/SKILL.md`) expect(second.requests).toContain(`${first.base}deploy/SKILL.md`)
const third = await pull( const third = await pull(
[{ name: "deploy", version: "2", files: ["SKILL.md"] }], [{ name: "deploy", version: "2", files: ["SKILL.md"] }],
{ [`${base}deploy/SKILL.md`]: "# Ignored" }, { "/catalog/deploy/SKILL.md": "# Ignored" },
tmp, first,
) )
expect(third.requests).toEqual([`${base}index.json`]) expect(third.requests).toEqual([`${first.base}index.json`])
} finally { } finally {
await tmp[Symbol.asyncDispose]() await dispose(first)
} }
}) })
test("publishes complete updates and removes stale files", async () => { test("publishes complete updates and removes stale files", async () => {
const tmp = await tmpdir() const first = await pull(
[{ name: "deploy", version: "1", files: ["SKILL.md", "old.md"] }],
{
"/catalog/deploy/SKILL.md": "# Old",
"/catalog/deploy/old.md": "old reference",
},
)
try { try {
const first = await pull(
[{ name: "deploy", version: "1", files: ["SKILL.md", "old.md"] }],
{
[`${base}deploy/SKILL.md`]: "# Old",
[`${base}deploy/old.md`]: "old reference",
},
tmp,
)
const root = first.directories[0] const root = first.directories[0]
await pull( await pull(
[{ name: "deploy", version: "2", files: ["SKILL.md", "missing.md"] }], [{ name: "deploy", version: "2", files: ["SKILL.md", "missing.md"] }],
{ [`${base}deploy/SKILL.md`]: "# Partial" }, { "/catalog/deploy/SKILL.md": "# Partial" },
tmp, first,
) )
expect(await fs.readFile(path.join(root, "SKILL.md"), "utf8")).toBe("# Old") expect(await fs.readFile(path.join(root, "SKILL.md"), "utf8")).toBe("# Old")
expect(await fs.readFile(path.join(root, "old.md"), "utf8")).toBe("old reference") expect(await fs.readFile(path.join(root, "old.md"), "utf8")).toBe("old reference")
await pull([{ name: "deploy", version: "3", files: ["SKILL.md"] }], { [`${base}deploy/SKILL.md`]: "# New" }, tmp) await pull(
[{ name: "deploy", version: "3", files: ["SKILL.md"] }],
{ "/catalog/deploy/SKILL.md": "# New" },
first,
)
expect(await fs.readFile(path.join(root, "SKILL.md"), "utf8")).toBe("# New") expect(await fs.readFile(path.join(root, "SKILL.md"), "utf8")).toBe("# New")
expect(await Bun.file(path.join(root, "old.md")).exists()).toBe(false) expect(await Bun.file(path.join(root, "old.md")).exists()).toBe(false)
} finally { } finally {
await tmp[Symbol.asyncDispose]() await dispose(first)
} }
}) })
}) })