Skip to content

Commit 6187c80

Browse files
committed
test: add comprehensive tests for package normalization
Add 41 tests covering package.json normalization utilities: - normalizePackageJson: version handling, dependencies, scripts, preserve options - resolveEscapedScope: Socket registry scope extraction - unescapeScope: convert escaped scopes to npm format - resolveOriginalPackageName: resolve Socket registry to original names - Integration tests and edge cases Coverage improved: packages/normalize.ts from 51.61% to higher coverage All tests pass (102 test files, 4720 tests) Cumulative coverage: 85.26% (up from 85.05%)
1 parent 3f7322f commit 6187c80

1 file changed

Lines changed: 338 additions & 0 deletions

File tree

test/packages/normalize.test.ts

Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
/**
2+
* @fileoverview Unit tests for package.json normalization utilities.
3+
*/
4+
5+
import { describe, expect, it } from 'vitest'
6+
7+
import {
8+
normalizePackageJson,
9+
resolveEscapedScope,
10+
resolveOriginalPackageName,
11+
unescapeScope,
12+
} from '@socketsecurity/lib/packages/normalize'
13+
import type { PackageJson } from '@socketsecurity/lib/packages'
14+
15+
describe('packages/normalize', () => {
16+
describe('normalizePackageJson', () => {
17+
it('should export normalizePackageJson function', () => {
18+
expect(typeof normalizePackageJson).toBe('function')
19+
})
20+
21+
it('should add default version if not present', () => {
22+
const pkg: PackageJson = {
23+
name: 'test-package',
24+
}
25+
const result = normalizePackageJson(pkg)
26+
expect(result.version).toBe('0.0.0')
27+
})
28+
29+
it('should preserve existing version', () => {
30+
const pkg: PackageJson = {
31+
name: 'test-package',
32+
version: '1.2.3',
33+
}
34+
const result = normalizePackageJson(pkg)
35+
expect(result.version).toBe('1.2.3')
36+
})
37+
38+
it('should normalize package.json data', () => {
39+
const pkg: PackageJson = {
40+
name: 'test-package',
41+
version: '1.0.0',
42+
description: 'Test package',
43+
}
44+
const result = normalizePackageJson(pkg)
45+
expect(result).toHaveProperty('name')
46+
expect(result).toHaveProperty('version')
47+
expect(result).toHaveProperty('description')
48+
})
49+
50+
it('should handle minimal package.json', () => {
51+
const pkg: PackageJson = {}
52+
const result = normalizePackageJson(pkg)
53+
expect(result.version).toBe('0.0.0')
54+
})
55+
56+
it('should preserve specified fields', () => {
57+
const pkg: PackageJson = {
58+
name: 'test-package',
59+
version: '1.0.0',
60+
custom: 'value',
61+
} as PackageJson
62+
const result = normalizePackageJson(pkg, {
63+
preserve: ['custom'],
64+
})
65+
expect(result).toHaveProperty('custom', 'value')
66+
})
67+
68+
it('should handle preserve option with multiple fields', () => {
69+
const pkg: PackageJson = {
70+
name: 'test-package',
71+
version: '1.0.0',
72+
custom1: 'value1',
73+
custom2: 'value2',
74+
} as PackageJson
75+
const result = normalizePackageJson(pkg, {
76+
preserve: ['custom1', 'custom2'],
77+
})
78+
expect(result).toHaveProperty('custom1', 'value1')
79+
expect(result).toHaveProperty('custom2', 'value2')
80+
})
81+
82+
it('should handle preserve option with non-existent fields', () => {
83+
const pkg: PackageJson = {
84+
name: 'test-package',
85+
version: '1.0.0',
86+
}
87+
const result = normalizePackageJson(pkg, {
88+
preserve: ['nonexistent'],
89+
})
90+
expect(result).toBeDefined()
91+
})
92+
93+
it('should handle package with dependencies', () => {
94+
const pkg: PackageJson = {
95+
name: 'test-package',
96+
version: '1.0.0',
97+
dependencies: {
98+
lodash: '^4.17.21',
99+
},
100+
}
101+
const result = normalizePackageJson(pkg)
102+
expect(result.dependencies).toEqual({ lodash: '^4.17.21' })
103+
})
104+
105+
it('should handle package with devDependencies', () => {
106+
const pkg: PackageJson = {
107+
name: 'test-package',
108+
version: '1.0.0',
109+
devDependencies: {
110+
vitest: '^1.0.0',
111+
},
112+
}
113+
const result = normalizePackageJson(pkg)
114+
expect(result.devDependencies).toEqual({ vitest: '^1.0.0' })
115+
})
116+
117+
it('should handle package with scripts', () => {
118+
const pkg: PackageJson = {
119+
name: 'test-package',
120+
version: '1.0.0',
121+
scripts: {
122+
test: 'vitest',
123+
build: 'tsc',
124+
},
125+
}
126+
const result = normalizePackageJson(pkg)
127+
expect(result.scripts).toEqual({ test: 'vitest', build: 'tsc' })
128+
})
129+
130+
it('should handle package with existing bugs field', () => {
131+
const pkg: PackageJson = {
132+
name: 'test-package',
133+
version: '1.0.0',
134+
bugs: {
135+
url: 'https://github.com/test/issues',
136+
},
137+
}
138+
const result = normalizePackageJson(pkg)
139+
expect(result.bugs).toEqual({ url: 'https://github.com/test/issues' })
140+
})
141+
142+
it('should handle package with existing homepage field', () => {
143+
const pkg: PackageJson = {
144+
name: 'test-package',
145+
version: '1.0.0',
146+
homepage: 'https://github.com/test',
147+
}
148+
const result = normalizePackageJson(pkg)
149+
expect(result.homepage).toBe('https://github.com/test')
150+
})
151+
152+
it('should handle scoped package names', () => {
153+
const pkg: PackageJson = {
154+
name: '@scope/test-package',
155+
version: '1.0.0',
156+
}
157+
const result = normalizePackageJson(pkg)
158+
expect(result.name).toBe('@scope/test-package')
159+
})
160+
})
161+
162+
describe('resolveEscapedScope', () => {
163+
it('should export resolveEscapedScope function', () => {
164+
expect(typeof resolveEscapedScope).toBe('function')
165+
})
166+
167+
it('should return undefined for non-scoped package', () => {
168+
const result = resolveEscapedScope('lodash')
169+
expect(result).toBeUndefined()
170+
})
171+
172+
it('should extract escaped scope with delimiter', () => {
173+
const result = resolveEscapedScope('scope__pkg')
174+
expect(result).toBe('scope__')
175+
})
176+
177+
it('should return undefined for standard npm scope', () => {
178+
const result = resolveEscapedScope('@scope/pkg')
179+
expect(result).toBeUndefined()
180+
})
181+
182+
it('should handle complex escaped scope names', () => {
183+
const result = resolveEscapedScope('my-org__my-package')
184+
expect(result).toBe('my-org__')
185+
})
186+
187+
it('should return undefined for packages without delimiter', () => {
188+
const result = resolveEscapedScope('simple-package')
189+
expect(result).toBeUndefined()
190+
})
191+
})
192+
193+
describe('unescapeScope', () => {
194+
it('should export unescapeScope function', () => {
195+
expect(typeof unescapeScope).toBe('function')
196+
})
197+
198+
it('should convert escaped scope to npm scope format', () => {
199+
const result = unescapeScope('scope__')
200+
expect(result).toBe('@scope')
201+
})
202+
203+
it('should handle multi-part scope names', () => {
204+
const result = unescapeScope('my-org__')
205+
expect(result).toBe('@my-org')
206+
})
207+
208+
it('should handle single character scopes', () => {
209+
const result = unescapeScope('s__')
210+
expect(result).toBe('@s')
211+
})
212+
213+
it('should handle scope with hyphens', () => {
214+
const result = unescapeScope('my-long-scope__')
215+
expect(result).toBe('@my-long-scope')
216+
})
217+
})
218+
219+
describe('resolveOriginalPackageName', () => {
220+
it('should export resolveOriginalPackageName function', () => {
221+
expect(typeof resolveOriginalPackageName).toBe('function')
222+
})
223+
224+
it('should return unchanged for non-Socket registry packages', () => {
225+
const result = resolveOriginalPackageName('lodash')
226+
expect(result).toBe('lodash')
227+
})
228+
229+
it('should resolve Socket registry package without scope', () => {
230+
const result = resolveOriginalPackageName('@socketregistry/lodash')
231+
expect(result).toBe('lodash')
232+
})
233+
234+
it('should resolve Socket registry package with escaped scope', () => {
235+
const result = resolveOriginalPackageName('@socketregistry/scope__pkg')
236+
expect(result).toBe('@scope/pkg')
237+
})
238+
239+
it('should handle Socket registry package with complex scope', () => {
240+
const result = resolveOriginalPackageName('@socketregistry/my-org__pkg')
241+
expect(result).toBe('@my-org/pkg')
242+
})
243+
244+
it('should handle non-Socket scoped packages', () => {
245+
const result = resolveOriginalPackageName('@babel/core')
246+
expect(result).toBe('@babel/core')
247+
})
248+
249+
it('should handle Socket registry with hyphenated package names', () => {
250+
const result = resolveOriginalPackageName(
251+
'@socketregistry/my-package-name',
252+
)
253+
expect(result).toBe('my-package-name')
254+
})
255+
256+
it('should handle Socket registry with scope and hyphenated package', () => {
257+
const result = resolveOriginalPackageName(
258+
'@socketregistry/my-scope__my-package',
259+
)
260+
expect(result).toBe('@my-scope/my-package')
261+
})
262+
})
263+
264+
describe('integration', () => {
265+
it('should work together: unescape and resolve', () => {
266+
const escaped = 'scope__'
267+
const unescaped = unescapeScope(escaped)
268+
expect(unescaped).toBe('@scope')
269+
270+
const socketPkg = '@socketregistry/scope__pkg'
271+
const original = resolveOriginalPackageName(socketPkg)
272+
expect(original).toBe('@scope/pkg')
273+
})
274+
275+
it('should handle round-trip normalization', () => {
276+
const original: PackageJson = {
277+
name: 'test-package',
278+
description: 'Test',
279+
}
280+
const normalized = normalizePackageJson(original)
281+
expect(normalized.name).toBe('test-package')
282+
expect(normalized.version).toBe('0.0.0')
283+
expect(normalized.description).toBe('Test')
284+
})
285+
286+
it('should preserve custom fields during normalization', () => {
287+
const original: PackageJson = {
288+
name: 'test-package',
289+
version: '1.0.0',
290+
socketSecurity: { enabled: true },
291+
} as PackageJson
292+
const normalized = normalizePackageJson(original, {
293+
preserve: ['socketSecurity'],
294+
})
295+
expect(normalized).toHaveProperty('socketSecurity')
296+
})
297+
})
298+
299+
describe('edge cases', () => {
300+
it('should handle empty string package name', () => {
301+
const result = resolveOriginalPackageName('')
302+
expect(result).toBe('')
303+
})
304+
305+
it('should handle package name with multiple delimiters', () => {
306+
const result = resolveOriginalPackageName(
307+
'@socketregistry/scope__sub__pkg',
308+
)
309+
// First delimiter creates the scope, rest remain in package name
310+
expect(result).toContain('/')
311+
})
312+
313+
it('should handle normalization with empty dependencies', () => {
314+
const pkg: PackageJson = {
315+
name: 'test-package',
316+
version: '1.0.0',
317+
dependencies: {},
318+
}
319+
const result = normalizePackageJson(pkg)
320+
expect(result.dependencies).toEqual({})
321+
})
322+
323+
it('should handle normalization with null values', () => {
324+
const pkg: PackageJson = {
325+
name: 'test-package',
326+
version: '1.0.0',
327+
description: undefined,
328+
}
329+
const result = normalizePackageJson(pkg)
330+
expect(result.name).toBe('test-package')
331+
})
332+
333+
it('should handle Socket registry package at root level', () => {
334+
const result = resolveOriginalPackageName('@socketregistry/')
335+
expect(result).toBe('')
336+
})
337+
})
338+
})

0 commit comments

Comments
 (0)