Skip to content

Commit 63bca7d

Browse files
committed
refactor(test): reorganize tests into unit and integration folders
- Create test/unit/ for existing unit tests (25 files) - Update import paths from './utils/' to '../utils/' in all unit tests - Update vitest.config.mts exclude paths for new structure - Update isolated-tests.json with test/unit/ prefix Benefits: - Clear separation between test types - Easier test discovery and navigation - Scalable structure for future tests
1 parent 2da3c8e commit 63bca7d

27 files changed

+6983
-14
lines changed

.config/isolated-tests.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
22
"tests": [
3-
"test/quota-utils-error-handling.test.mts",
4-
"test/json-parsing-edge-cases.test.mts",
5-
"test/getapi-sendapi-methods.test.mts",
6-
"test/socket-sdk-retry.test.mts",
7-
"test/entitlements.test.mts",
8-
"test/socket-sdk-batch.test.mts",
9-
"test/socket-sdk-json-parsing-errors.test.mts",
10-
"test/socket-sdk-strict-types.test.mts"
3+
"test/unit/quota-utils-error-handling.test.mts",
4+
"test/unit/json-parsing-edge-cases.test.mts",
5+
"test/unit/getapi-sendapi-methods.test.mts",
6+
"test/unit/socket-sdk-retry.test.mts",
7+
"test/unit/entitlements.test.mts",
8+
"test/unit/socket-sdk-batch.test.mts",
9+
"test/unit/socket-sdk-json-parsing-errors.test.mts",
10+
"test/unit/socket-sdk-strict-types.test.mts"
1111
]
1212
}

.config/vitest.config.mts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ export default defineConfig({
3030
exclude: [
3131
'**/node_modules/**',
3232
'**/dist/**',
33-
'test/quota-utils-error-handling.test.mts',
34-
'test/json-parsing-edge-cases.test.mts',
35-
'test/getapi-sendapi-methods.test.mts',
36-
'test/socket-sdk-retry.test.mts',
37-
'test/entitlements.test.mts',
38-
'test/socket-sdk-batch.test.mts',
33+
'test/unit/quota-utils-error-handling.test.mts',
34+
'test/unit/json-parsing-edge-cases.test.mts',
35+
'test/unit/getapi-sendapi-methods.test.mts',
36+
'test/unit/socket-sdk-retry.test.mts',
37+
'test/unit/entitlements.test.mts',
38+
'test/unit/socket-sdk-batch.test.mts',
3939
],
4040
reporters:
4141
process.env.TEST_REPORTER === 'json' ? ['json', 'default'] : ['default'],
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
/**
2+
* @fileoverview Bundle validation tests to ensure build output quality.
3+
* Verifies that dist files don't contain absolute paths or external dependencies.
4+
*/
5+
6+
import { promises as fs } from 'node:fs'
7+
import path from 'node:path'
8+
import { fileURLToPath } from 'node:url'
9+
10+
import { parse } from '@babel/parser'
11+
import traverse from '@babel/traverse'
12+
import { describe, expect, it } from 'vitest'
13+
14+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
15+
const packagePath = path.resolve(__dirname, '..')
16+
const distPath = path.join(packagePath, 'dist')
17+
18+
/**
19+
* Check if content contains absolute paths.
20+
* Detects paths like /Users/, C:\, /home/, etc.
21+
*/
22+
function hasAbsolutePaths(content: string): {
23+
hasIssue: boolean
24+
matches: string[]
25+
} {
26+
// Match absolute paths but exclude URLs and node: protocol.
27+
const patterns = [
28+
// Match require('/abs/path') or require('C:\\path').
29+
/require\(["'](?:\/[^"'\n]+|[A-Z]:\\[^"'\n]+)["']\)/g,
30+
// Match import from '/abs/path'.
31+
/import\s+.*?from\s+["'](?:\/[^"'\n]+|[A-Z]:\\[^"'\n]+)["']/g,
32+
]
33+
34+
const matches: string[] = []
35+
for (const pattern of patterns) {
36+
const found = content.match(pattern)
37+
if (found) {
38+
matches.push(...found)
39+
}
40+
}
41+
42+
return {
43+
hasIssue: matches.length > 0,
44+
matches,
45+
}
46+
}
47+
48+
/**
49+
* Check if bundle contains inlined dependencies using AST analysis.
50+
* Reads package.json dependencies and ensures they are NOT bundled inline.
51+
*/
52+
async function checkBundledDependencies(content: string): Promise<{
53+
bundledDeps: string[]
54+
hasNoBundledDeps: boolean
55+
}> {
56+
// Read package.json to get runtime dependencies.
57+
const pkgJsonPath = path.join(packagePath, 'package.json')
58+
const pkgJson = JSON.parse(await fs.readFile(pkgJsonPath, 'utf8'))
59+
const dependencies = pkgJson.dependencies || {}
60+
61+
const bundledDeps: string[] = []
62+
63+
// Parse the bundle into an AST.
64+
const file = parse(content, {
65+
sourceType: 'module',
66+
plugins: ['typescript'],
67+
})
68+
69+
// Collect all import sources from the AST.
70+
const importSources = new Set<string>()
71+
72+
traverse(file as any, {
73+
ImportDeclaration(path: any) {
74+
const source = path.node.source.value
75+
importSources.add(source)
76+
},
77+
CallExpression(path: any) {
78+
// Handle require() calls
79+
if (
80+
path.node.callee.name === 'require' &&
81+
path.node.arguments.length > 0 &&
82+
path.node.arguments[0].type === 'StringLiteral'
83+
) {
84+
const source = path.node.arguments[0].value
85+
importSources.add(source)
86+
}
87+
},
88+
})
89+
90+
// Packages that should always be external (never bundled).
91+
const socketPackagePatterns = [
92+
/@socketsecurity\/lib/,
93+
/@socketregistry\/packageurl-js/,
94+
/@socketsecurity\/sdk/,
95+
/@socketsecurity\/registry/,
96+
]
97+
98+
// Check if we have runtime dependencies.
99+
if (Object.keys(dependencies).length === 0) {
100+
// No runtime dependencies - check that Socket packages aren't bundled.
101+
for (const pattern of socketPackagePatterns) {
102+
const hasExternalImport = Array.from(importSources).some(source =>
103+
pattern.test(source),
104+
)
105+
106+
if (!hasExternalImport) {
107+
// Check if this package name appears in the content at all.
108+
// If it's just in string literals (like constants), that's fine.
109+
// Use AST to check if it appears in any meaningful way.
110+
let foundInCode = false
111+
112+
traverse(file as any, {
113+
StringLiteral(path: any) {
114+
// Skip string literals - these are fine
115+
if (pattern.test(path.node.value)) {
116+
// It's in a string literal, which is fine
117+
}
118+
},
119+
120+
Identifier(path: any) {
121+
// Check if the package name appears in identifiers or other code
122+
if (
123+
pattern.test(path.node.name) ||
124+
(path.node.name.includes('socketsecurity') &&
125+
pattern.test(path.node.name))
126+
) {
127+
foundInCode = true
128+
}
129+
},
130+
})
131+
132+
// Only flag if we found it in actual code, not just string literals
133+
if (foundInCode) {
134+
bundledDeps.push(pattern.source)
135+
}
136+
}
137+
}
138+
} else {
139+
// We have runtime dependencies - check that they remain external.
140+
for (const dep of Object.keys(dependencies)) {
141+
// Check for exact match or subpath imports (e.g., '@socketsecurity/lib/path')
142+
const hasExternalImport = Array.from(importSources).some(
143+
source => source === dep || source.startsWith(`${dep}/`),
144+
)
145+
146+
if (!hasExternalImport) {
147+
// Dependency isn't imported externally - it might be bundled
148+
bundledDeps.push(dep)
149+
}
150+
}
151+
}
152+
153+
return {
154+
bundledDeps,
155+
hasNoBundledDeps: bundledDeps.length === 0,
156+
}
157+
}
158+
159+
describe('Bundle validation', () => {
160+
it('should not contain absolute paths in dist/index.js', async () => {
161+
const indexPath = path.join(distPath, 'index.js')
162+
const content = await fs.readFile(indexPath, 'utf8')
163+
164+
const result = hasAbsolutePaths(content)
165+
166+
if (result.hasIssue) {
167+
console.error('Found absolute paths in bundle:')
168+
for (const match of result.matches) {
169+
console.error(` - ${match}`)
170+
}
171+
}
172+
173+
expect(result.hasIssue, 'Bundle should not contain absolute paths').toBe(
174+
false,
175+
)
176+
})
177+
178+
it('should not bundle dependencies inline (validate against package.json dependencies)', async () => {
179+
const indexPath = path.join(distPath, 'index.js')
180+
const content = await fs.readFile(indexPath, 'utf8')
181+
182+
const result = await checkBundledDependencies(content)
183+
184+
if (!result.hasNoBundledDeps) {
185+
console.error('Found bundled dependencies (should be external):')
186+
for (const dep of result.bundledDeps) {
187+
console.error(` - ${dep}`)
188+
}
189+
}
190+
191+
expect(
192+
result.hasNoBundledDeps,
193+
'Dependencies from package.json should be external, not bundled inline',
194+
).toBe(true)
195+
})
196+
})
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/** @fileoverview Tests for JSON request body creation utilities. */
2+
import { describe, expect, it } from 'vitest'
3+
4+
import { createRequestBodyForJson } from '../src/index'
5+
6+
describe('JSON Request Body Creation', () => {
7+
describe('createRequestBodyForJson', () => {
8+
it('should create request body for JSON data with default basename', () => {
9+
const jsonData = { test: 'data', number: 42 }
10+
const result = createRequestBodyForJson(jsonData)
11+
12+
expect(result).toHaveLength(3)
13+
expect(result[0]).toContain('name="data"')
14+
expect(result[0]).toContain('filename="data.json"')
15+
expect(result[0]).toContain('Content-Type: application/json')
16+
expect(result[2]).toBe('\r\n')
17+
})
18+
19+
it('should create request body for JSON data with custom basename', () => {
20+
const jsonData = { custom: true }
21+
const result = createRequestBodyForJson(jsonData, 'custom-file.json')
22+
23+
expect(result).toHaveLength(3)
24+
expect(result[0]).toContain('name="custom-file"')
25+
expect(result[0]).toContain('filename="custom-file.json"')
26+
expect(result[0]).toContain('Content-Type: application/json')
27+
})
28+
29+
it('should handle basename without extension', () => {
30+
const jsonData = { test: 'no-ext' }
31+
const result = createRequestBodyForJson(jsonData, 'noextension')
32+
33+
expect(result).toHaveLength(3)
34+
expect(result[0]).toContain('name="noextension"')
35+
expect(result[0]).toContain('filename="noextension"')
36+
expect(result[0]).toContain('Content-Type: application/json')
37+
})
38+
39+
it('should handle complex JSON data', () => {
40+
const jsonData = {
41+
nested: { object: true },
42+
array: [1, 2, 3],
43+
string: 'test',
44+
number: 123.45,
45+
boolean: false,
46+
null: null,
47+
}
48+
const result = createRequestBodyForJson(jsonData, 'complex.json')
49+
50+
expect(result).toHaveLength(3)
51+
expect(result[0]).toContain('name="complex"')
52+
expect(result[0]).toContain('filename="complex.json"')
53+
expect(result[0]).toContain('Content-Type: application/json')
54+
})
55+
56+
it('should handle empty object', () => {
57+
const jsonData = {}
58+
const result = createRequestBodyForJson(jsonData, 'empty.json')
59+
60+
expect(result).toHaveLength(3)
61+
expect(result[0]).toContain('name="empty"')
62+
expect(result[0]).toContain('filename="empty.json"')
63+
})
64+
65+
it('should handle null data', () => {
66+
const jsonData = null
67+
const result = createRequestBodyForJson(jsonData, 'null.json')
68+
69+
expect(result).toHaveLength(3)
70+
expect(result[0]).toContain('name="null"')
71+
expect(result[0]).toContain('filename="null.json"')
72+
})
73+
74+
it('should handle different file extensions', () => {
75+
const jsonData = { test: true }
76+
const result = createRequestBodyForJson(jsonData, 'data.manifest')
77+
78+
expect(result).toHaveLength(3)
79+
expect(result[0]).toContain('name="data"')
80+
expect(result[0]).toContain('filename="data.manifest"')
81+
})
82+
})
83+
})

0 commit comments

Comments
 (0)