Sfoglia il codice sorgente

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

Aiden Cline 1 mese fa
parent
commit
391aa38281
1 ha cambiato i file con 77 aggiunte e 66 eliminazioni
  1. 77 66
      packages/core/test/skill-discovery.test.ts

+ 77 - 66
packages/core/test/skill-discovery.test.ts

@@ -1,36 +1,42 @@
 import fs from "fs/promises"
 import path from "path"
 import { describe, expect, test } from "bun:test"
-import { Effect, Layer } from "effect"
-import { HttpClient, HttpClientResponse } from "effect/unstable/http"
+import { Effect } from "effect"
 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 { SkillDiscovery } from "@opencode-ai/core/skill/discovery"
 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>>) {
-  const tmp = cache ?? (await tmpdir())
-  const requests: string[] = []
-  const http = Layer.succeed(
-    HttpClient.HttpClient,
-    HttpClient.make((request) =>
-      Effect.sync(() => requests.push(request.url)).pipe(
-        Effect.map(() => {
-          const body = request.url === `${base}index.json` ? JSON.stringify({ skills }) : files[request.url]
-          return HttpClientResponse.fromWeb(
-            request,
-            new Response(body ?? "Not Found", { status: body === undefined ? 404 : 200 }),
-          )
-        }),
-      ),
-    ),
-  )
+async function pull(skills: unknown[], files: Record<string, string> = {}, fixture?: Fixture) {
+  const state = fixture?.state ?? { skills, files, requests: [] }
+  state.skills = skills
+  state.files = files
+  state.requests = []
+  const server =
+    fixture?.server ??
+    Bun.serve({
+      port: 0,
+      fetch(request) {
+        state.requests.push(request.url)
+        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, [
-    [LayerNodePlatform.httpClient, http],
     [Global.node, Global.layerWith({ cache: tmp.path })],
   ])
   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)
     }).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", () => {
@@ -46,10 +57,10 @@ describe("SkillDiscovery.pull", () => {
     const result = await pull([{ name: "../outside", files: ["SKILL.md"] }])
     try {
       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([])
     } 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"] }])
     try {
       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([])
     } 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"] }])
     try {
       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([])
     } 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"] }])
     try {
       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([])
     } finally {
-      await result.tmp[Symbol.asyncDispose]()
+      await dispose(result)
     }
   })
 
   test("downloads safe nested files under the skill root", async () => {
     const result = await pull([{ name: "deploy", files: ["SKILL.md", "references/guide.md"] }], {
-      [`${base}deploy/SKILL.md`]: "# Deploy",
-      [`${base}deploy/references/guide.md`]: "# Guide",
+      "/catalog/deploy/SKILL.md": "# Deploy",
+      "/catalog/deploy/references/guide.md": "# Guide",
     })
     try {
       expect(result.directories).toHaveLength(1)
       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], "references", "guide.md"), "utf8")).toBe("# Guide")
     } finally {
-      await result.tmp[Symbol.asyncDispose]()
+      await dispose(result)
     }
   })
 
   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 {
-      const first = await pull(
-        [{ name: "deploy", version: "1", files: ["SKILL.md"] }],
-        {
-          [`${base}deploy/SKILL.md`]: "# Old",
-        },
-        tmp,
-      )
       const second = await pull(
         [{ name: "deploy", version: "2", files: ["SKILL.md"] }],
-        {
-          [`${base}deploy/SKILL.md`]: "# New",
-        },
-        tmp,
+        { "/catalog/deploy/SKILL.md": "# New" },
+        first,
       )
 
       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(
         [{ name: "deploy", version: "2", files: ["SKILL.md"] }],
-        { [`${base}deploy/SKILL.md`]: "# Ignored" },
-        tmp,
+        { "/catalog/deploy/SKILL.md": "# Ignored" },
+        first,
       )
-      expect(third.requests).toEqual([`${base}index.json`])
+      expect(third.requests).toEqual([`${first.base}index.json`])
     } finally {
-      await tmp[Symbol.asyncDispose]()
+      await dispose(first)
     }
   })
 
   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 {
-      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]
 
       await pull(
         [{ name: "deploy", version: "2", files: ["SKILL.md", "missing.md"] }],
-        { [`${base}deploy/SKILL.md`]: "# Partial" },
-        tmp,
+        { "/catalog/deploy/SKILL.md": "# Partial" },
+        first,
       )
       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")
 
-      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 Bun.file(path.join(root, "old.md")).exists()).toBe(false)
     } finally {
-      await tmp[Symbol.asyncDispose]()
+      await dispose(first)
     }
   })
 })