install-root.test.js
1 'use strict' 2 /* eslint-disable @typescript-eslint/no-require-imports */ 3 4 const test = require('node:test') 5 const assert = require('node:assert/strict') 6 const fs = require('node:fs') 7 const os = require('node:os') 8 const path = require('node:path') 9 10 const { 11 candidateDirsFromArgv1, 12 detectGlobalInstallManagerForRoot, 13 findLocalInstallProjectRoot, 14 resolvePackageRoot, 15 resolveStateHome, 16 } = require('./install-root.js') 17 18 test('candidateDirsFromArgv1 includes the package directory for node_modules/.bin launchers', () => { 19 const launcher = path.join('/tmp', 'example', 'node_modules', '.bin', 'swarmclaw') 20 const candidates = candidateDirsFromArgv1(launcher) 21 assert.deepEqual(candidates, [ 22 path.join('/tmp', 'example', 'node_modules', '.bin'), 23 path.join('/tmp', 'example', 'node_modules', 'swarmclaw'), 24 ]) 25 }) 26 27 test('resolvePackageRoot finds the package root from argv1 candidates', () => { 28 const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-install-root-')) 29 const pkgRoot = path.join(rootDir, 'node_modules', '@swarmclawai', 'swarmclaw') 30 const binPath = path.join(rootDir, 'node_modules', '.bin', 'swarmclaw') 31 const actualBin = path.join(pkgRoot, 'bin', 'swarmclaw.js') 32 33 fs.mkdirSync(path.join(pkgRoot, 'bin'), { recursive: true }) 34 fs.mkdirSync(path.dirname(binPath), { recursive: true }) 35 fs.writeFileSync(path.join(pkgRoot, 'package.json'), JSON.stringify({ name: '@swarmclawai/swarmclaw' }), 'utf8') 36 fs.writeFileSync(actualBin, '#!/usr/bin/env node\n', 'utf8') 37 fs.symlinkSync(actualBin, binPath) 38 39 assert.equal(resolvePackageRoot({ argv1: binPath, cwd: rootDir }), fs.realpathSync(pkgRoot)) 40 41 fs.rmSync(rootDir, { recursive: true, force: true }) 42 }) 43 44 test('detectGlobalInstallManagerForRoot matches the owning global root by realpath', () => { 45 const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-global-root-')) 46 const npmGlobalRoot = path.join(rootDir, 'npm-global') 47 const pnpmGlobalRoot = path.join(rootDir, 'pnpm-global') 48 const pkgRoot = path.join(pnpmGlobalRoot, '@swarmclawai', 'swarmclaw') 49 50 fs.mkdirSync(path.join(npmGlobalRoot, '@swarmclawai'), { recursive: true }) 51 fs.mkdirSync(path.join(pnpmGlobalRoot, '@swarmclawai'), { recursive: true }) 52 fs.mkdirSync(pkgRoot, { recursive: true }) 53 54 const execImpl = (command, args) => { 55 if (command === 'npm' && args.join(' ') === 'root -g') return npmGlobalRoot 56 if (command === 'pnpm' && args.join(' ') === 'root -g') return pnpmGlobalRoot 57 throw new Error(`unexpected command: ${command} ${args.join(' ')}`) 58 } 59 60 assert.equal(detectGlobalInstallManagerForRoot(pkgRoot, execImpl), 'pnpm') 61 62 fs.rmSync(rootDir, { recursive: true, force: true }) 63 }) 64 65 test('findLocalInstallProjectRoot returns the project root for nested pnpm installs', () => { 66 const pkgRoot = path.join( 67 '/tmp', 68 'example', 69 'node_modules', 70 '.pnpm', 71 '@swarmclawai+swarmclaw@1.0.1', 72 'node_modules', 73 '@swarmclawai', 74 'swarmclaw', 75 ) 76 77 assert.equal(findLocalInstallProjectRoot(pkgRoot), path.join('/tmp', 'example')) 78 }) 79 80 test('resolveStateHome prefers the local project .swarmclaw directory for local installs', () => { 81 const projectRoot = path.join('/tmp', 'example') 82 const pkgRoot = path.join(projectRoot, 'node_modules', '@swarmclawai', 'swarmclaw') 83 const execImpl = () => { 84 throw new Error('unexpected global root lookup') 85 } 86 87 assert.equal( 88 resolveStateHome({ 89 pkgRoot, 90 env: {}, 91 execImpl, 92 }), 93 path.join(projectRoot, '.swarmclaw'), 94 ) 95 }) 96 97 test('resolveStateHome keeps global installs under the user home directory', () => { 98 const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-state-home-')) 99 const npmGlobalRoot = path.join(rootDir, 'npm-global') 100 const pkgRoot = path.join(npmGlobalRoot, '@swarmclawai', 'swarmclaw') 101 102 fs.mkdirSync(path.join(npmGlobalRoot, '@swarmclawai'), { recursive: true }) 103 fs.mkdirSync(pkgRoot, { recursive: true }) 104 105 const execImpl = (command, args) => { 106 if (command === 'npm' && args.join(' ') === 'root -g') return npmGlobalRoot 107 if (command === 'pnpm' && args.join(' ') === 'root -g') return path.join(rootDir, 'pnpm-global') 108 throw new Error(`unexpected command: ${command} ${args.join(' ')}`) 109 } 110 111 assert.equal( 112 resolveStateHome({ 113 pkgRoot, 114 env: {}, 115 execImpl, 116 }), 117 path.join(os.homedir(), '.swarmclaw'), 118 ) 119 120 fs.rmSync(rootDir, { recursive: true, force: true }) 121 })