|
| 1 | +import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test" |
| 2 | +import fs from "fs/promises" |
| 3 | +import os from "os" |
| 4 | +import path from "path" |
| 5 | +import { tmpdir } from "../fixture/fixture" |
| 6 | + |
| 7 | +const base = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-npm-")) |
| 8 | +const xdg = path.join(base, "xdg") |
| 9 | +const home = path.join(base, "home") |
| 10 | +const prev = { |
| 11 | + HOME: process.env.HOME, |
| 12 | + NPM_CONFIG_USERCONFIG: process.env.NPM_CONFIG_USERCONFIG, |
| 13 | + XDG_CACHE_HOME: process.env.XDG_CACHE_HOME, |
| 14 | + XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME, |
| 15 | + XDG_DATA_HOME: process.env.XDG_DATA_HOME, |
| 16 | + XDG_STATE_HOME: process.env.XDG_STATE_HOME, |
| 17 | + npm_config_registry: process.env.npm_config_registry, |
| 18 | + npm_config_userconfig: process.env.npm_config_userconfig, |
| 19 | +} |
| 20 | + |
| 21 | +await fs.mkdir(xdg, { recursive: true }) |
| 22 | +await fs.mkdir(home, { recursive: true }) |
| 23 | + |
| 24 | +process.env.HOME = home |
| 25 | +process.env.XDG_CACHE_HOME = path.join(xdg, "cache") |
| 26 | +process.env.XDG_CONFIG_HOME = path.join(xdg, "config") |
| 27 | +process.env.XDG_DATA_HOME = path.join(xdg, "data") |
| 28 | +process.env.XDG_STATE_HOME = path.join(xdg, "state") |
| 29 | + |
| 30 | +const seen: Array<Record<string, unknown>> = [] |
| 31 | + |
| 32 | +mock.module("@npmcli/arborist", () => ({ |
| 33 | + Arborist: class { |
| 34 | + constructor(input: Record<string, unknown>) { |
| 35 | + seen.push(input) |
| 36 | + } |
| 37 | + |
| 38 | + async loadVirtual() { |
| 39 | + return undefined |
| 40 | + } |
| 41 | + |
| 42 | + async reify() { |
| 43 | + return { |
| 44 | + edgesOut: new Map([["pkg", { to: { name: "@tngtech/opencode-skainet", path: base } }]]), |
| 45 | + } |
| 46 | + } |
| 47 | + }, |
| 48 | +})) |
| 49 | + |
| 50 | +const { Global } = await import("../../src/global") |
| 51 | +const { Npm } = await import("../../src/npm") |
| 52 | + |
| 53 | +beforeEach(async () => { |
| 54 | + seen.length = 0 |
| 55 | + delete process.env.NPM_CONFIG_USERCONFIG |
| 56 | + delete process.env.npm_config_registry |
| 57 | + delete process.env.npm_config_userconfig |
| 58 | + await fs.rm(home, { recursive: true, force: true }) |
| 59 | + await fs.mkdir(home, { recursive: true }) |
| 60 | + await fs.rm(Global.Path.cache, { recursive: true, force: true }) |
| 61 | + await fs.mkdir(Global.Path.cache, { recursive: true }) |
| 62 | + await fs.writeFile(path.join(Global.Path.cache, "version"), "21") |
| 63 | +}) |
| 64 | + |
| 65 | +afterAll(async () => { |
| 66 | + for (const [key, value] of Object.entries(prev)) { |
| 67 | + if (value === undefined) delete process.env[key] |
| 68 | + else process.env[key] = value |
| 69 | + } |
| 70 | + await fs.rm(base, { recursive: true, force: true }) |
| 71 | +}) |
| 72 | + |
| 73 | +describe("npm", () => { |
| 74 | + test("add reads scoped registry from user npmrc", async () => { |
| 75 | + await fs.writeFile(path.join(home, ".npmrc"), "@tngtech:registry=https://user.example/\n") |
| 76 | + |
| 77 | + await Npm.add("@tngtech/opencode-skainet@latest") |
| 78 | + |
| 79 | + expect(seen[0]?.["@tngtech:registry"]).toBe("https://user.example/") |
| 80 | + expect(seen[0]?.path).toBe( |
| 81 | + path.join(Global.Path.cache, "packages", Npm.sanitize("@tngtech/opencode-skainet@latest")), |
| 82 | + ) |
| 83 | + }) |
| 84 | + |
| 85 | + test("add keeps cache root npmrc as local config", async () => { |
| 86 | + await fs.writeFile(path.join(Global.Path.cache, ".npmrc"), "@tngtech:registry=https://cache.example/\n") |
| 87 | + |
| 88 | + await Npm.add("@tngtech/opencode-skainet@latest") |
| 89 | + |
| 90 | + expect(seen[0]?.["@tngtech:registry"]).toBe("https://cache.example/") |
| 91 | + }) |
| 92 | + |
| 93 | + test("install reads local npmrc from install dir", async () => { |
| 94 | + await using tmp = await tmpdir() |
| 95 | + await fs.writeFile(path.join(tmp.path, ".npmrc"), "registry=https://dir.example/\n") |
| 96 | + |
| 97 | + await Npm.install(tmp.path) |
| 98 | + |
| 99 | + expect(seen[0]?.registry).toBe("https://dir.example/") |
| 100 | + expect(seen[0]?.path).toBe(tmp.path) |
| 101 | + }) |
| 102 | +}) |
0 commit comments