summaryrefslogtreecommitdiffhomepage
path: root/packages/core/test
diff options
context:
space:
mode:
authorDax <[email protected]>2026-04-26 23:54:59 -0400
committerGitHub <[email protected]>2026-04-27 03:54:59 +0000
commita9b62d67df08e6b984c51ead12339c845db49e93 (patch)
tree30890d600d6cc10369e1d57f1e8972c16cfb7d90 /packages/core/test
parent3525e619069069db10f13cc31959de879d7830eb (diff)
downloadopencode-a9b62d67df08e6b984c51ead12339c845db49e93.tar.gz
opencode-a9b62d67df08e6b984c51ead12339c845db49e93.zip
Refactor npm config handling (#24565)
Diffstat (limited to 'packages/core/test')
-rw-r--r--packages/core/test/fixture/tmpdir.ts13
-rw-r--r--packages/core/test/npm-config.test.ts51
-rw-r--r--packages/core/test/npm.test.ts56
3 files changed, 120 insertions, 0 deletions
diff --git a/packages/core/test/fixture/tmpdir.ts b/packages/core/test/fixture/tmpdir.ts
new file mode 100644
index 000000000..950b1401b
--- /dev/null
+++ b/packages/core/test/fixture/tmpdir.ts
@@ -0,0 +1,13 @@
+import fs from "fs/promises"
+import { tmpdir as osTmpdir } from "os"
+import path from "path"
+
+export const tmpdir = async () => {
+ const dir = await fs.mkdtemp(path.join(osTmpdir(), "opencode-core-test-"))
+ return {
+ path: dir,
+ async [Symbol.asyncDispose]() {
+ await fs.rm(dir, { recursive: true, force: true })
+ },
+ }
+}
diff --git a/packages/core/test/npm-config.test.ts b/packages/core/test/npm-config.test.ts
new file mode 100644
index 000000000..895b35db5
--- /dev/null
+++ b/packages/core/test/npm-config.test.ts
@@ -0,0 +1,51 @@
+import path from "path"
+import { describe, expect, test } from "bun:test"
+import { Effect } from "effect"
+import { NpmConfig } from "@opencode-ai/core/npm-config"
+import { tmpdir } from "./fixture/tmpdir"
+
+describe("NpmConfig.load", () => {
+ test("reads registry from project .npmrc", async () => {
+ await using tmp = await tmpdir()
+ await Bun.write(path.join(tmp.path, ".npmrc"), "registry=https://registry.example.test/\n")
+
+ const config = await Effect.runPromise(NpmConfig.load(tmp.path))
+
+ expect(config.registry).toBe("https://registry.example.test/")
+ })
+
+ test("reads scoped registries from project .npmrc", async () => {
+ await using tmp = await tmpdir()
+ await Bun.write(path.join(tmp.path, ".npmrc"), "@acme:registry=https://npm.acme.test/\n")
+
+ const config = await Effect.runPromise(NpmConfig.load(tmp.path))
+
+ expect(config["@acme:registry"]).toBe("https://npm.acme.test/")
+ })
+
+ test("flattens boolean and list options", async () => {
+ await using tmp = await tmpdir()
+ await Bun.write(path.join(tmp.path, ".npmrc"), "ignore-scripts=true\nomit[]=dev\nomit[]=optional\n")
+
+ const config = await Effect.runPromise(NpmConfig.load(tmp.path))
+
+ expect(config.ignoreScripts).toBe(true)
+ expect(config.omit).toEqual(["dev", "optional"])
+ })
+})
+
+describe("NpmConfig.registry", () => {
+ test("normalizes configured registry without trailing slash", async () => {
+ await using tmp = await tmpdir()
+ await Bun.write(path.join(tmp.path, ".npmrc"), "registry=https://registry.example.test/\n")
+
+ await expect(Effect.runPromise(NpmConfig.registry(tmp.path))).resolves.toBe("https://registry.example.test")
+ })
+
+ test("leaves configured registry without trailing slash unchanged", async () => {
+ await using tmp = await tmpdir()
+ await Bun.write(path.join(tmp.path, ".npmrc"), "registry=https://registry.example.test\n")
+
+ await expect(Effect.runPromise(NpmConfig.registry(tmp.path))).resolves.toBe("https://registry.example.test")
+ })
+})
diff --git a/packages/core/test/npm.test.ts b/packages/core/test/npm.test.ts
new file mode 100644
index 000000000..3e94a0869
--- /dev/null
+++ b/packages/core/test/npm.test.ts
@@ -0,0 +1,56 @@
+import fs from "fs/promises"
+import path from "path"
+import { describe, expect, test } from "bun:test"
+import { Npm } from "@opencode-ai/core/npm"
+import { tmpdir } from "./fixture/tmpdir"
+
+const win = process.platform === "win32"
+
+const writePackage = (dir: string, pkg: Record<string, unknown>) =>
+ Bun.write(
+ path.join(dir, "package.json"),
+ JSON.stringify({
+ version: "1.0.0",
+ ...pkg,
+ }),
+ )
+
+describe("Npm.sanitize", () => {
+ test("keeps normal scoped package specs unchanged", () => {
+ expect(Npm.sanitize("@opencode/acme")).toBe("@opencode/acme")
+ expect(Npm.sanitize("@opencode/[email protected]")).toBe("@opencode/[email protected]")
+ expect(Npm.sanitize("prettier")).toBe("prettier")
+ })
+
+ test("handles git https specs", () => {
+ const spec = "acme@git+https://github.com/opencode/acme.git"
+ const expected = win ? "acme@git+https_//github.com/opencode/acme.git" : spec
+ expect(Npm.sanitize(spec)).toBe(expected)
+ })
+})
+
+describe("Npm.install", () => {
+ test("respects omit from project .npmrc", async () => {
+ await using tmp = await tmpdir()
+
+ await writePackage(tmp.path, {
+ name: "fixture",
+ dependencies: {
+ "prod-pkg": "file:./prod-pkg",
+ },
+ devDependencies: {
+ "dev-pkg": "file:./dev-pkg",
+ },
+ })
+ await Bun.write(path.join(tmp.path, ".npmrc"), "omit=dev\n")
+ await fs.mkdir(path.join(tmp.path, "prod-pkg"))
+ await fs.mkdir(path.join(tmp.path, "dev-pkg"))
+ await writePackage(path.join(tmp.path, "prod-pkg"), { name: "prod-pkg" })
+ await writePackage(path.join(tmp.path, "dev-pkg"), { name: "dev-pkg" })
+
+ await Npm.install(tmp.path)
+
+ await expect(fs.stat(path.join(tmp.path, "node_modules", "prod-pkg"))).resolves.toBeDefined()
+ await expect(fs.stat(path.join(tmp.path, "node_modules", "dev-pkg"))).rejects.toThrow()
+ })
+})