mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-03-30 13:54:01 +00:00
- Rename packages/opencode → packages/tfcode (directory only) - Rename bin/opencode → bin/tfcode (CLI binary) - Rename .opencode → .tfcode (config directory) - Update package.json name and bin field - Update config directory path references (.tfcode) - Keep internal code references as 'opencode' for easy upstream sync - Keep @opencode-ai/* workspace package names This minimal branding approach allows clean merges from upstream opencode repository while providing tfcode branding for users.
101 lines
2.7 KiB
TypeScript
101 lines
2.7 KiB
TypeScript
import { describe, expect, test } from "bun:test"
|
|
import fs from "fs/promises"
|
|
import path from "path"
|
|
import { which } from "../../src/util/which"
|
|
import { tmpdir } from "../fixture/fixture"
|
|
|
|
async function cmd(dir: string, name: string, exec = true) {
|
|
const ext = process.platform === "win32" ? ".cmd" : ""
|
|
const file = path.join(dir, name + ext)
|
|
const body = process.platform === "win32" ? "@echo off\r\n" : "#!/bin/sh\n"
|
|
await fs.writeFile(file, body)
|
|
if (process.platform !== "win32") {
|
|
await fs.chmod(file, exec ? 0o755 : 0o644)
|
|
}
|
|
return file
|
|
}
|
|
|
|
function env(PATH: string): NodeJS.ProcessEnv {
|
|
return {
|
|
PATH,
|
|
PATHEXT: process.env["PATHEXT"],
|
|
}
|
|
}
|
|
|
|
function envPath(Path: string): NodeJS.ProcessEnv {
|
|
return {
|
|
Path,
|
|
PathExt: process.env["PathExt"] ?? process.env["PATHEXT"],
|
|
}
|
|
}
|
|
|
|
function same(a: string | null, b: string) {
|
|
if (process.platform === "win32") {
|
|
expect(a?.toLowerCase()).toBe(b.toLowerCase())
|
|
return
|
|
}
|
|
|
|
expect(a).toBe(b)
|
|
}
|
|
|
|
describe("util.which", () => {
|
|
test("returns null when command is missing", () => {
|
|
expect(which("opencode-missing-command-for-test")).toBeNull()
|
|
})
|
|
|
|
test("finds a command from PATH override", async () => {
|
|
await using tmp = await tmpdir()
|
|
const bin = path.join(tmp.path, "bin")
|
|
await fs.mkdir(bin)
|
|
const file = await cmd(bin, "tool")
|
|
|
|
same(which("tool", env(bin)), file)
|
|
})
|
|
|
|
test("uses first PATH match", async () => {
|
|
await using tmp = await tmpdir()
|
|
const a = path.join(tmp.path, "a")
|
|
const b = path.join(tmp.path, "b")
|
|
await fs.mkdir(a)
|
|
await fs.mkdir(b)
|
|
const first = await cmd(a, "dupe")
|
|
await cmd(b, "dupe")
|
|
|
|
same(which("dupe", env([a, b].join(path.delimiter))), first)
|
|
})
|
|
|
|
test("returns null for non-executable file on unix", async () => {
|
|
if (process.platform === "win32") return
|
|
|
|
await using tmp = await tmpdir()
|
|
const bin = path.join(tmp.path, "bin")
|
|
await fs.mkdir(bin)
|
|
await cmd(bin, "noexec", false)
|
|
|
|
expect(which("noexec", env(bin))).toBeNull()
|
|
})
|
|
|
|
test("uses PATHEXT on windows", async () => {
|
|
if (process.platform !== "win32") return
|
|
|
|
await using tmp = await tmpdir()
|
|
const bin = path.join(tmp.path, "bin")
|
|
await fs.mkdir(bin)
|
|
const file = path.join(bin, "pathext.CMD")
|
|
await fs.writeFile(file, "@echo off\r\n")
|
|
|
|
expect(which("pathext", { PATH: bin, PATHEXT: ".CMD" })).toBe(file)
|
|
})
|
|
|
|
test("uses Windows Path casing fallback", async () => {
|
|
if (process.platform !== "win32") return
|
|
|
|
await using tmp = await tmpdir()
|
|
const bin = path.join(tmp.path, "bin")
|
|
await fs.mkdir(bin)
|
|
const file = await cmd(bin, "mixed")
|
|
|
|
same(which("mixed", envPath(bin)), file)
|
|
})
|
|
})
|