test(reference): cover configured reference contracts (#28170)
Some checks are pending
containers / build (push) Waiting to run
deploy / deploy (push) Waiting to run
generate / generate (push) Waiting to run
nix-eval / nix-eval (push) Waiting to run
nix-hashes / update-hashes (push) Blocked by required conditions
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404, x86_64-linux) (push) Waiting to run
nix-hashes / compute-hash (blacksmith-4vcpu-ubuntu-2404-arm, aarch64-linux) (push) Waiting to run
nix-hashes / compute-hash (macos-15-intel, x86_64-darwin) (push) Waiting to run
nix-hashes / compute-hash (macos-latest, aarch64-darwin) (push) Waiting to run
publish / version (push) Waiting to run
publish / build-cli (push) Blocked by required conditions
publish / sign-cli-windows (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=arm64 host:macos-26 platform_flag:--mac --arm64 target:aarch64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[bun_install_flags:--os=darwin --cpu=x64 host:macos-26-intel platform_flag:--mac --x64 target:x86_64-apple-darwin]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404 platform_flag:--linux target:x86_64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-ubuntu-2404-arm platform_flag:--linux --arm64 target:aarch64-unknown-linux-gnu]) (push) Blocked by required conditions
publish / build-electron (map[host:blacksmith-4vcpu-windows-2025 platform_flag:--win target:x86_64-pc-windows-msvc]) (push) Blocked by required conditions
publish / build-electron (map[host:windows-2025 platform_flag:--win --arm64 target:aarch64-pc-windows-msvc]) (push) Blocked by required conditions
publish / publish (push) Blocked by required conditions
storybook / storybook build (push) Waiting to run
test / unit (linux) (push) Waiting to run
test / unit (windows) (push) Waiting to run
test / e2e (linux) (push) Waiting to run
test / e2e (windows) (push) Waiting to run
typecheck / typecheck (push) Waiting to run

This commit is contained in:
Shoubhit Dash 2026-05-18 20:09:27 +05:30 committed by GitHub
parent e56999fd36
commit 54ff0a669b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 258 additions and 11 deletions

View file

@ -81,7 +81,7 @@ const waitForContent = (
})
describe("reference", () => {
it.live("resolves local and git references", () =>
it.live("resolves supported local and git config forms", () =>
Effect.gen(function* () {
const root = path.resolve("opencode-reference-root")
const local = Reference.resolve({
@ -96,18 +96,63 @@ describe("reference", () => {
directory: path.join(root, "packages", "app"),
worktree: root,
})
const localString = Reference.resolve({
name: "notes",
reference: "./notes",
directory: path.join(root, "packages", "app"),
worktree: root,
})
const repoString = Reference.resolve({
name: "repo",
reference: "owner/repo",
directory: path.join(root, "packages", "app"),
worktree: root,
})
expect(local.kind).toBe("local")
if (local.kind === "local") expect(local.path).toBe(path.resolve(root, "../docs"))
expect(localString.kind).toBe("local")
if (localString.kind === "local") expect(localString.path).toBe(path.resolve(root, "notes"))
expect(repo.kind).toBe("git")
if (repo.kind === "git") {
expect(repo.repository).toBe("Effect-TS/effect")
expect(repo.branch).toBe("main")
expect(repo.path).toBe(path.join(Global.Path.repos, "github.com", "Effect-TS", "effect"))
}
expect(repoString.kind).toBe("git")
if (repoString.kind === "git") {
expect(repoString.repository).toBe("owner/repo")
expect(repoString.path).toBe(path.join(Global.Path.repos, "github.com", "owner", "repo"))
}
}),
)
it.live("keeps invalid repository references visible without materializing", () =>
provideTmpdirInstance(
(_dir) =>
Effect.gen(function* () {
const reference = yield* Reference.Service
const references = yield* reference.list()
const invalid = yield* reference.get("bad")
expect(references.map((item) => item.name)).toEqual(["bad"])
expect(invalid).toMatchObject({
name: "bad",
kind: "invalid",
repository: "not-a-repo",
})
if (invalid?.kind === "invalid") expect(invalid.message).toContain("Repository must be a git URL")
}),
{
config: {
reference: {
bad: "not-a-repo",
},
},
},
),
)
it.live("marks same-cache references with different branches invalid", () =>
Effect.gen(function* () {
const root = path.resolve("opencode-reference-root")

View file

@ -6,22 +6,39 @@ import { SessionID, MessageID } from "../../src/session/schema"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { Ripgrep } from "../../src/file/ripgrep"
import { AppFileSystem } from "@opencode-ai/core/filesystem"
import { Global } from "@opencode-ai/core/global"
import { Truncate } from "@/tool/truncate"
import { Agent } from "../../src/agent/agent"
import { TestInstance } from "../fixture/fixture"
import { TestInstance, tmpdirScoped } from "../fixture/fixture"
import { testEffect } from "../lib/effect"
import { Reference } from "@/reference/reference"
import { Config } from "@/config/config"
import { RuntimeFlags } from "@/effect/runtime-flags"
import { Git } from "@/git"
import { Permission } from "../../src/permission"
import type * as Tool from "../../src/tool/tool"
const it = testEffect(
const referenceLayer = (flags: Partial<RuntimeFlags.Info> = {}) =>
Reference.layer.pipe(
Layer.provide(Config.defaultLayer),
Layer.provide(AppFileSystem.defaultLayer),
Layer.provide(Git.defaultLayer),
Layer.provide(RuntimeFlags.layer(flags)),
)
const toolLayer = (flags: Partial<RuntimeFlags.Info> = {}) =>
Layer.mergeAll(
CrossSpawnSpawner.defaultLayer,
AppFileSystem.defaultLayer,
Ripgrep.defaultLayer,
Truncate.defaultLayer,
Agent.defaultLayer,
Reference.defaultLayer,
),
)
Git.defaultLayer,
referenceLayer(flags),
)
const it = testEffect(toolLayer())
const scout = testEffect(toolLayer({ experimentalScout: true }))
const ctx = {
sessionID: SessionID.make("ses_test"),
@ -34,6 +51,52 @@ const ctx = {
ask: () => Effect.void,
}
const asks = () => {
const items: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
return {
items,
next: {
...ctx,
ask: (req: Omit<Permission.Request, "id" | "sessionID" | "tool">) =>
Effect.sync(() => {
items.push(req)
}),
} satisfies Tool.Context,
}
}
const githubBase = <A, E, R>(url: string, self: Effect.Effect<A, E, R>) =>
Effect.acquireUseRelease(
Effect.sync(() => {
const previous = process.env.OPENCODE_REPO_CLONE_GITHUB_BASE_URL
process.env.OPENCODE_REPO_CLONE_GITHUB_BASE_URL = url
return previous
}),
() => self,
(previous) =>
Effect.sync(() => {
if (previous) process.env.OPENCODE_REPO_CLONE_GITHUB_BASE_URL = previous
else delete process.env.OPENCODE_REPO_CLONE_GITHUB_BASE_URL
}),
)
const git = Effect.fn("GlobToolTest.git")(function* (cwd: string, args: string[]) {
return yield* Effect.promise(async () => {
const proc = Bun.spawn(["git", ...args], {
cwd,
stdout: "pipe",
stderr: "pipe",
})
const [stdout, stderr, code] = await Promise.all([
new Response(proc.stdout).text(),
new Response(proc.stderr).text(),
proc.exited,
])
if (code !== 0) throw new Error(stderr.trim() || stdout.trim() || `git ${args.join(" ")} failed`)
return stdout.trim()
})
})
describe("tool.glob", () => {
it.instance("matches files from a directory path", () =>
Effect.gen(function* () {
@ -78,4 +141,45 @@ describe("tool.glob", () => {
}
}),
)
scout.instance(
"does not ask for external_directory permission inside configured git references",
() =>
Effect.gen(function* () {
yield* TestInstance
const fs = yield* AppFileSystem.Service
const cache = path.join(Global.Path.repos, "github.com", "opencode-glob-reference", "repo")
yield* fs.remove(cache, { recursive: true }).pipe(Effect.ignore)
yield* Effect.addFinalizer(() => fs.remove(cache, { recursive: true }).pipe(Effect.ignore))
const source = yield* tmpdirScoped({ git: true })
const remoteRoot = yield* tmpdirScoped()
const remoteDir = path.join(remoteRoot, "opencode-glob-reference")
const remoteRepo = path.join(remoteDir, "repo.git")
yield* fs.writeWithDirs(path.join(source, "src", "index.ts"), "export const value = 1\n")
yield* git(source, ["add", "."])
yield* git(source, ["commit", "-m", "add source"])
yield* fs.makeDirectory(remoteDir, { recursive: true }).pipe(Effect.orDie)
yield* git(remoteRoot, ["clone", "--bare", source, remoteRepo])
const { items, next } = asks()
const info = yield* GlobTool
const glob = yield* info.init()
const result = yield* githubBase(
`file://${remoteRoot}/`,
glob.execute({ pattern: "*.ts", path: path.join(cache, "src") }, next),
)
expect(result.metadata.count).toBe(1)
expect(result.output).toContain(path.join(cache, "src", "index.ts"))
expect(items.find((item) => item.permission === "external_directory")).toBeUndefined()
}),
{
config: {
reference: {
docs: "opencode-glob-reference/repo",
},
},
},
)
})

View file

@ -4,9 +4,10 @@ import os from "os"
import path from "path"
import { Effect, Layer } from "effect"
import { GrepTool } from "../../src/tool/grep"
import { provideInstance, TestInstance } from "../fixture/fixture"
import { provideInstance, TestInstance, tmpdirScoped } from "../fixture/fixture"
import { SessionID, MessageID } from "../../src/session/schema"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { Global } from "@opencode-ai/core/global"
import { Truncate } from "@/tool/truncate"
import { Agent } from "../../src/agent/agent"
import { Ripgrep } from "../../src/file/ripgrep"
@ -15,17 +16,32 @@ import { testEffect } from "../lib/effect"
import { Reference } from "@/reference/reference"
import { Permission } from "../../src/permission"
import type * as Tool from "../../src/tool/tool"
import { Config } from "@/config/config"
import { RuntimeFlags } from "@/effect/runtime-flags"
import { Git } from "@/git"
import { Filesystem } from "@/util/filesystem"
const it = testEffect(
const referenceLayer = (flags: Partial<RuntimeFlags.Info> = {}) =>
Reference.layer.pipe(
Layer.provide(Config.defaultLayer),
Layer.provide(AppFileSystem.defaultLayer),
Layer.provide(Git.defaultLayer),
Layer.provide(RuntimeFlags.layer(flags)),
)
const toolLayer = (flags: Partial<RuntimeFlags.Info> = {}) =>
Layer.mergeAll(
CrossSpawnSpawner.defaultLayer,
AppFileSystem.defaultLayer,
Ripgrep.defaultLayer,
Truncate.defaultLayer,
Agent.defaultLayer,
Reference.defaultLayer,
),
)
Git.defaultLayer,
referenceLayer(flags),
)
const it = testEffect(toolLayer())
const scout = testEffect(toolLayer({ experimentalScout: true }))
const ctx = {
sessionID: SessionID.make("ses_test"),
@ -39,6 +55,39 @@ const ctx = {
}
const root = path.join(__dirname, "../..")
const full = (p: string) => (process.platform === "win32" ? Filesystem.normalizePath(p) : p)
const githubBase = <A, E, R>(url: string, self: Effect.Effect<A, E, R>) =>
Effect.acquireUseRelease(
Effect.sync(() => {
const previous = process.env.OPENCODE_REPO_CLONE_GITHUB_BASE_URL
process.env.OPENCODE_REPO_CLONE_GITHUB_BASE_URL = url
return previous
}),
() => self,
(previous) =>
Effect.sync(() => {
if (previous) process.env.OPENCODE_REPO_CLONE_GITHUB_BASE_URL = previous
else delete process.env.OPENCODE_REPO_CLONE_GITHUB_BASE_URL
}),
)
const git = Effect.fn("GrepToolTest.git")(function* (cwd: string, args: string[]) {
return yield* Effect.promise(async () => {
const proc = Bun.spawn(["git", ...args], {
cwd,
stdout: "pipe",
stderr: "pipe",
})
const [stdout, stderr, code] = await Promise.all([
new Response(proc.stdout).text(),
new Response(proc.stderr).text(),
proc.exited,
])
if (code !== 0) throw new Error(stderr.trim() || stdout.trim() || `git ${args.join(" ")} failed`)
return stdout.trim()
})
})
describe("tool.grep", () => {
it.live("basic search", () =>
@ -163,4 +212,53 @@ describe("tool.grep", () => {
expect(requests.find((req) => req.permission === "external_directory")).toBeUndefined()
}),
)
scout.instance(
"does not ask for external_directory permission inside configured git references",
() =>
Effect.gen(function* () {
yield* TestInstance
const appfs = yield* AppFileSystem.Service
const cache = path.join(Global.Path.repos, "github.com", "opencode-grep-reference", "repo")
yield* appfs.remove(cache, { recursive: true }).pipe(Effect.ignore)
yield* Effect.addFinalizer(() => appfs.remove(cache, { recursive: true }).pipe(Effect.ignore))
const source = yield* tmpdirScoped({ git: true })
const remoteRoot = yield* tmpdirScoped()
const remoteDir = path.join(remoteRoot, "opencode-grep-reference")
const remoteRepo = path.join(remoteDir, "repo.git")
yield* appfs.writeWithDirs(path.join(source, "src", "notes.md"), "needle\n")
yield* git(source, ["add", "."])
yield* git(source, ["commit", "-m", "add notes"])
yield* appfs.makeDirectory(remoteDir, { recursive: true }).pipe(Effect.orDie)
yield* git(remoteRoot, ["clone", "--bare", source, remoteRepo])
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
const next: Tool.Context = {
...ctx,
ask: (req) =>
Effect.sync(() => {
requests.push(req)
}),
}
const info = yield* GrepTool
const grep = yield* info.init()
const result = yield* githubBase(
`file://${remoteRoot}/`,
grep.execute({ pattern: "needle", path: path.join(cache, "src"), include: "*.md" }, next),
)
expect(result.metadata.matches).toBe(1)
expect(full(result.output)).toContain(full(path.join(cache, "src", "notes.md")))
expect(requests.find((req) => req.permission === "external_directory")).toBeUndefined()
}),
{
config: {
reference: {
docs: "opencode-grep-reference/repo",
},
},
},
)
})