import fs from "fs/promises"
import path from "path"
import { pathToFileURL } from "url"
import { describe, expect } from "bun:test"
import { Effect, Exit, Layer } from "effect"
import { AppFileSystem } from "@opencode-ai/core/filesystem"
import { Location } from "@opencode-ai/core/location"
import { LocationFileSystem } from "@opencode-ai/core/location-filesystem"
import { AbsolutePath, RelativePath } from "@opencode-ai/core/schema"
import { tmpdir } from "./fixture/tmpdir"
import { location } from "./fixture/location"
import { it } from "./lib/effect"
function provide(directory: string) {
return Effect.provide(
LocationFileSystem.locationLayer.pipe(
Layer.provide(
Layer.mergeAll(
AppFileSystem.defaultLayer,
Layer.succeed(Location.Service, Location.Service.of(location({ directory: AbsolutePath.make(directory) }))),
),
),
),
)
}
function withTmp(f: (directory: string) => Effect.Effect) {
return Effect.acquireRelease(
Effect.promise(() => tmpdir()),
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
).pipe(Effect.flatMap((tmp) => f(tmp.path).pipe(provide(tmp.path))))
}
describe("LocationFileSystem", () => {
it.live("reads text and binary files", () =>
withTmp((directory) =>
Effect.gen(function* () {
yield* Effect.promise(() => fs.writeFile(path.join(directory, "hello.txt"), "hello"))
yield* Effect.promise(() => fs.writeFile(path.join(directory, "data.bin"), Buffer.from([0, 1, 2])))
const service = yield* LocationFileSystem.Service
expect(yield* service.read({ path: RelativePath.make("hello.txt") })).toEqual({
type: "text",
content: "hello",
mime: "text/plain",
})
expect(yield* service.read({ path: RelativePath.make("data.bin") })).toEqual({
type: "binary",
content: "AAEC",
encoding: "base64",
mime: "application/octet-stream",
})
}),
),
)
it.live("lists direct children with relative paths and resolved URIs", () =>
withTmp((directory) =>
Effect.gen(function* () {
yield* Effect.promise(() => fs.mkdir(path.join(directory, "src")))
yield* Effect.promise(() => fs.writeFile(path.join(directory, "README.md"), "# Test"))
const service = yield* LocationFileSystem.Service
expect(yield* service.list()).toEqual([
{
path: RelativePath.make("src"),
uri: pathToFileURL(path.join(directory, "src")).href,
type: "directory",
mime: "application/x-directory",
},
{
path: RelativePath.make("README.md"),
uri: pathToFileURL(path.join(directory, "README.md")).href,
type: "file",
mime: "text/markdown",
},
])
}),
),
)
it.live("rejects paths outside the location", () =>
withTmp((directory) =>
Effect.gen(function* () {
const service = yield* LocationFileSystem.Service
expect(
Exit.isFailure(yield* service.read({ path: RelativePath.make("../outside.txt") }).pipe(Effect.exit)),
).toBe(true)
}),
),
)
})