Skip to content

Commit 52b641c

Browse files
committed
test(dlx/binary-resolution): cover findBinaryPath fallback ladder (7 tests)
1 parent 3ee74c3 commit 52b641c

1 file changed

Lines changed: 121 additions & 0 deletions

File tree

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
2+
import os from 'node:os'
3+
import path from 'node:path'
4+
import { afterEach, beforeEach, describe, expect, test } from 'vitest'
5+
6+
import { findBinaryPath } from '../../../src/dlx/binary-resolution'
7+
8+
let tmpRoot: string
9+
10+
function makePackage(opts: {
11+
packageDir: string
12+
packageName: string
13+
bin?: string | Record<string, string>
14+
version?: string
15+
}): void {
16+
const installedDir = path.join(
17+
opts.packageDir,
18+
'node_modules',
19+
opts.packageName,
20+
)
21+
mkdirSync(installedDir, { recursive: true })
22+
const pkg: Record<string, unknown> = {
23+
name: opts.packageName,
24+
version: opts.version ?? '1.0.0',
25+
}
26+
if (opts.bin !== undefined) {
27+
pkg['bin'] = opts.bin
28+
}
29+
writeFileSync(path.join(installedDir, 'package.json'), JSON.stringify(pkg))
30+
}
31+
32+
beforeEach(() => {
33+
tmpRoot = mkdtempSync(path.join(os.tmpdir(), 'dlx-binres-test-'))
34+
})
35+
36+
afterEach(() => {
37+
rmSync(tmpRoot, { force: true, recursive: true })
38+
})
39+
40+
describe.sequential('dlx/binary-resolution — findBinaryPath', () => {
41+
test('returns the string bin field verbatim when bin is a string', () => {
42+
makePackage({
43+
packageDir: tmpRoot,
44+
packageName: 'tool',
45+
bin: './bin/tool.js',
46+
})
47+
const result = findBinaryPath(tmpRoot, 'tool')
48+
// The path is normalized + joined under node_modules/tool/.
49+
expect(result).toContain('tool/bin/tool.js')
50+
})
51+
52+
test('returns the single entry of a single-key bin object', () => {
53+
makePackage({
54+
packageDir: tmpRoot,
55+
packageName: 'tool',
56+
bin: { somename: './bin/cli.js' },
57+
})
58+
const result = findBinaryPath(tmpRoot, 'tool')
59+
expect(result).toContain('tool/bin/cli.js')
60+
})
61+
62+
test('matches binaryName when bin has multiple keys', () => {
63+
makePackage({
64+
packageDir: tmpRoot,
65+
packageName: 'tool',
66+
bin: {
67+
primary: './bin/primary.js',
68+
secondary: './bin/secondary.js',
69+
},
70+
})
71+
const result = findBinaryPath(tmpRoot, 'tool', 'secondary')
72+
expect(result).toContain('bin/secondary.js')
73+
})
74+
75+
test('falls back to last segment of package name when binaryName is absent', () => {
76+
makePackage({
77+
packageDir: tmpRoot,
78+
packageName: '@scope/tool',
79+
bin: {
80+
tool: './bin/tool.js',
81+
other: './bin/other.js',
82+
},
83+
})
84+
const result = findBinaryPath(tmpRoot, '@scope/tool')
85+
expect(result).toContain('bin/tool.js')
86+
})
87+
88+
test('falls back to first bin entry when no candidate matches', () => {
89+
makePackage({
90+
packageDir: tmpRoot,
91+
packageName: 'mismatch',
92+
bin: {
93+
somethingelse: './bin/first.js',
94+
another: './bin/another.js',
95+
},
96+
})
97+
const result = findBinaryPath(tmpRoot, 'mismatch', 'definitely-not-there')
98+
// The first key in insertion order is `somethingelse`.
99+
expect(result).toContain('bin/first.js')
100+
})
101+
102+
test('throws when bin entry is missing from package.json', () => {
103+
makePackage({
104+
packageDir: tmpRoot,
105+
packageName: 'no-bin',
106+
})
107+
expect(() => findBinaryPath(tmpRoot, 'no-bin')).toThrow(
108+
/No binary found for package/,
109+
)
110+
})
111+
112+
test('handles bin object with scoped name in path', () => {
113+
makePackage({
114+
packageDir: tmpRoot,
115+
packageName: '@scope/mypkg',
116+
bin: { mypkg: './cli.js' },
117+
})
118+
const result = findBinaryPath(tmpRoot, '@scope/mypkg')
119+
expect(result).toContain('@scope/mypkg/cli.js')
120+
})
121+
})

0 commit comments

Comments
 (0)