discovery.test.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import { describe, test, expect, beforeAll, afterAll } from "bun:test"
  2. import { Effect } from "effect"
  3. import { Discovery } from "../../src/skill/discovery"
  4. import { Global } from "../../src/global"
  5. import { Filesystem } from "../../src/util/filesystem"
  6. import { rm } from "fs/promises"
  7. import path from "path"
  8. let CLOUDFLARE_SKILLS_URL: string
  9. let server: ReturnType<typeof Bun.serve>
  10. let downloadCount = 0
  11. const fixturePath = path.join(import.meta.dir, "../fixture/skills")
  12. const cacheDir = path.join(Global.Path.cache, "skills")
  13. beforeAll(async () => {
  14. await rm(cacheDir, { recursive: true, force: true })
  15. server = Bun.serve({
  16. port: 0,
  17. async fetch(req) {
  18. const url = new URL(req.url)
  19. // route /.well-known/skills/* to the fixture directory
  20. if (url.pathname.startsWith("/.well-known/skills/")) {
  21. const filePath = url.pathname.replace("/.well-known/skills/", "")
  22. const fullPath = path.join(fixturePath, filePath)
  23. if (await Filesystem.exists(fullPath)) {
  24. if (!fullPath.endsWith("index.json")) {
  25. downloadCount++
  26. }
  27. return new Response(Bun.file(fullPath))
  28. }
  29. }
  30. return new Response("Not Found", { status: 404 })
  31. },
  32. })
  33. CLOUDFLARE_SKILLS_URL = `http://localhost:${server.port}/.well-known/skills/`
  34. })
  35. afterAll(async () => {
  36. server?.stop()
  37. await rm(cacheDir, { recursive: true, force: true })
  38. })
  39. describe("Discovery.pull", () => {
  40. const pull = (url: string) =>
  41. Effect.runPromise(Discovery.Service.use((s) => s.pull(url)).pipe(Effect.provide(Discovery.defaultLayer)))
  42. test("downloads skills from cloudflare url", async () => {
  43. const dirs = await pull(CLOUDFLARE_SKILLS_URL)
  44. expect(dirs.length).toBeGreaterThan(0)
  45. for (const dir of dirs) {
  46. expect(dir).toStartWith(cacheDir)
  47. const md = path.join(dir, "SKILL.md")
  48. expect(await Filesystem.exists(md)).toBe(true)
  49. }
  50. })
  51. test("url without trailing slash works", async () => {
  52. const dirs = await pull(CLOUDFLARE_SKILLS_URL.replace(/\/$/, ""))
  53. expect(dirs.length).toBeGreaterThan(0)
  54. for (const dir of dirs) {
  55. const md = path.join(dir, "SKILL.md")
  56. expect(await Filesystem.exists(md)).toBe(true)
  57. }
  58. })
  59. test("returns empty array for invalid url", async () => {
  60. const dirs = await pull(`http://localhost:${server.port}/invalid-url/`)
  61. expect(dirs).toEqual([])
  62. })
  63. test("returns empty array for non-json response", async () => {
  64. // any url not explicitly handled in server returns 404 text "Not Found"
  65. const dirs = await pull(`http://localhost:${server.port}/some-other-path/`)
  66. expect(dirs).toEqual([])
  67. })
  68. test("downloads reference files alongside SKILL.md", async () => {
  69. const dirs = await pull(CLOUDFLARE_SKILLS_URL)
  70. // find a skill dir that should have reference files (e.g. agents-sdk)
  71. const agentsSdk = dirs.find((d) => d.endsWith(path.sep + "agents-sdk"))
  72. expect(agentsSdk).toBeDefined()
  73. if (agentsSdk) {
  74. const refs = path.join(agentsSdk, "references")
  75. expect(await Filesystem.exists(path.join(agentsSdk, "SKILL.md"))).toBe(true)
  76. // agents-sdk has reference files per the index
  77. const refDir = await Array.fromAsync(new Bun.Glob("**/*.md").scan({ cwd: refs, onlyFiles: true }))
  78. expect(refDir.length).toBeGreaterThan(0)
  79. }
  80. })
  81. test("caches downloaded files on second pull", async () => {
  82. // clear dir and downloadCount
  83. await rm(cacheDir, { recursive: true, force: true })
  84. downloadCount = 0
  85. // first pull to populate cache
  86. const first = await pull(CLOUDFLARE_SKILLS_URL)
  87. expect(first.length).toBeGreaterThan(0)
  88. const firstCount = downloadCount
  89. expect(firstCount).toBeGreaterThan(0)
  90. // second pull should return same results from cache
  91. const second = await pull(CLOUDFLARE_SKILLS_URL)
  92. expect(second.length).toBe(first.length)
  93. expect(second.sort()).toEqual(first.sort())
  94. // second pull should NOT increment download count
  95. expect(downloadCount).toBe(firstCount)
  96. })
  97. })