Skip to content

Commit 73d283a

Browse files
committed
chore: tests
1 parent 3a34ed5 commit 73d283a

File tree

1 file changed

+253
-0
lines changed

1 file changed

+253
-0
lines changed
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
import { describe, expect, it } from 'vitest'
2+
3+
const VERSION: SlimVersion = {
4+
version: '1.0.0',
5+
tags: [],
6+
}
7+
8+
function createPkg(overrides: Partial<SlimPackument> = {}): SlimPackument {
9+
return {
10+
'_id': 'test',
11+
'name': 'test',
12+
'description': 'A test package',
13+
'license': 'MIT',
14+
'repository': { type: 'git', url: 'https://github.com/test/test' },
15+
'dist-tags': { latest: '1.0.0' },
16+
'time': { '1.0.0': new Date().toISOString(), 'created': '2020-01-01' },
17+
'versions': { '1.0.0': VERSION },
18+
'requestedVersion': null,
19+
...overrides,
20+
} as SlimPackument
21+
}
22+
23+
function createAnalysis(overrides: Partial<PackageAnalysisResponse> = {}): PackageAnalysisResponse {
24+
return {
25+
package: 'test',
26+
version: '1.0.0',
27+
moduleFormat: 'esm',
28+
types: { kind: 'included' },
29+
devDependencySuggestion: 'none',
30+
...overrides,
31+
} as PackageAnalysisResponse
32+
}
33+
34+
function createBaseInput() {
35+
return {
36+
pkg: ref<SlimPackument | null>(createPkg()),
37+
resolvedVersion: ref<string | null>('1.0.0'),
38+
readmeHtml: ref(`<h1>Test Package</h1><p>${'x'.repeat(600)}</p>`),
39+
analysis: ref<PackageAnalysisResponse | null>(createAnalysis()),
40+
vulnCounts: ref<{ total: number; critical: number; high: number } | null>({
41+
total: 0,
42+
critical: 0,
43+
high: 0,
44+
}),
45+
vulnStatus: ref('success'),
46+
hasProvenance: ref(true),
47+
}
48+
}
49+
50+
describe('usePackageScore', () => {
51+
it('returns 100% for a perfect package', () => {
52+
const score = usePackageScore(createBaseInput())
53+
54+
expect(score.value.percentage).toBe(100)
55+
expect(score.value.totalPoints).toBe(score.value.maxPoints)
56+
})
57+
58+
describe('documentation', () => {
59+
it('gives 0/2 for missing README', () => {
60+
const input = createBaseInput()
61+
input.readmeHtml.value = ''
62+
63+
const check = usePackageScore(input).value.checks.find(c => c.id === 'has-readme')!
64+
expect(check.points).toBe(0)
65+
expect(check.maxPoints).toBe(2)
66+
})
67+
68+
it('gives 1/2 for a short README', () => {
69+
const input = createBaseInput()
70+
input.readmeHtml.value = '<p>Short</p>'
71+
72+
const check = usePackageScore(input).value.checks.find(c => c.id === 'has-readme')!
73+
expect(check.points).toBe(1)
74+
})
75+
76+
it('gives 2/2 for a substantial README', () => {
77+
const check = usePackageScore(createBaseInput()).value.checks.find(
78+
c => c.id === 'has-readme',
79+
)!
80+
expect(check.points).toBe(2)
81+
})
82+
83+
it('gives 0/1 for missing description', () => {
84+
const input = createBaseInput()
85+
input.pkg.value = createPkg({ description: '' })
86+
87+
const check = usePackageScore(input).value.checks.find(c => c.id === 'has-description')!
88+
expect(check.points).toBe(0)
89+
})
90+
})
91+
92+
describe('maintenance', () => {
93+
it('gives 2/2 for package updated within 1 year', () => {
94+
const check = usePackageScore(createBaseInput()).value.checks.find(
95+
c => c.id === 'update-frequency',
96+
)!
97+
expect(check.points).toBe(2)
98+
})
99+
100+
it('gives 1/2 for package updated within 2 years', () => {
101+
const input = createBaseInput()
102+
const eighteenMonthsAgo = new Date(Date.now() - 18 * 30 * 24 * 60 * 60 * 1000).toISOString()
103+
input.pkg.value = createPkg({ time: { '1.0.0': eighteenMonthsAgo, 'created': '2020-01-01' } })
104+
105+
const check = usePackageScore(input).value.checks.find(c => c.id === 'update-frequency')!
106+
expect(check.points).toBe(1)
107+
})
108+
109+
it('gives 0/2 for package older than 2 years', () => {
110+
const input = createBaseInput()
111+
const threeYearsAgo = new Date(Date.now() - 3 * 365 * 24 * 60 * 60 * 1000).toISOString()
112+
input.pkg.value = createPkg({ time: { '1.0.0': threeYearsAgo, 'created': '2020-01-01' } })
113+
114+
const check = usePackageScore(input).value.checks.find(c => c.id === 'update-frequency')!
115+
expect(check.points).toBe(0)
116+
})
117+
})
118+
119+
describe('types', () => {
120+
it('gives 2/2 for bundled types', () => {
121+
const check = usePackageScore(createBaseInput()).value.checks.find(c => c.id === 'has-types')!
122+
expect(check.points).toBe(2)
123+
})
124+
125+
it('gives 1/2 for @types', () => {
126+
const input = createBaseInput()
127+
input.analysis.value = createAnalysis({
128+
types: { kind: '@types', packageName: '@types/test' },
129+
})
130+
131+
const check = usePackageScore(input).value.checks.find(c => c.id === 'has-types')!
132+
expect(check.points).toBe(1)
133+
})
134+
135+
it('gives 0/2 for no types', () => {
136+
const input = createBaseInput()
137+
input.analysis.value = createAnalysis({ types: { kind: 'none' } })
138+
139+
const check = usePackageScore(input).value.checks.find(c => c.id === 'has-types')!
140+
expect(check.points).toBe(0)
141+
})
142+
})
143+
144+
describe('best practices', () => {
145+
it('gives 0 for missing license', () => {
146+
const input = createBaseInput()
147+
input.pkg.value = createPkg({ license: undefined })
148+
149+
const check = usePackageScore(input).value.checks.find(c => c.id === 'has-license')!
150+
expect(check.points).toBe(0)
151+
})
152+
153+
it('gives 0 for missing provenance', () => {
154+
const input = createBaseInput()
155+
input.hasProvenance.value = false
156+
157+
const check = usePackageScore(input).value.checks.find(c => c.id === 'has-provenance')!
158+
expect(check.points).toBe(0)
159+
})
160+
161+
it('gives 0 for CJS-only', () => {
162+
const input = createBaseInput()
163+
input.analysis.value = createAnalysis({ moduleFormat: 'cjs' })
164+
165+
const check = usePackageScore(input).value.checks.find(c => c.id === 'has-esm')!
166+
expect(check.points).toBe(0)
167+
})
168+
169+
it('gives 1 for dual ESM/CJS', () => {
170+
const input = createBaseInput()
171+
input.analysis.value = createAnalysis({ moduleFormat: 'dual' })
172+
173+
const check = usePackageScore(input).value.checks.find(c => c.id === 'has-esm')!
174+
expect(check.points).toBe(1)
175+
})
176+
})
177+
178+
describe('security', () => {
179+
it('gives 2/2 for no vulnerabilities', () => {
180+
const check = usePackageScore(createBaseInput()).value.checks.find(
181+
c => c.id === 'no-vulnerabilities',
182+
)!
183+
expect(check.points).toBe(2)
184+
})
185+
186+
it('gives 1/2 for only moderate/low vulnerabilities', () => {
187+
const input = createBaseInput()
188+
input.vulnCounts.value = { total: 3, critical: 0, high: 0 }
189+
190+
const check = usePackageScore(input).value.checks.find(c => c.id === 'no-vulnerabilities')!
191+
expect(check.points).toBe(1)
192+
})
193+
194+
it('gives 0/2 for critical/high vulnerabilities', () => {
195+
const input = createBaseInput()
196+
input.vulnCounts.value = { total: 2, critical: 1, high: 0 }
197+
198+
const check = usePackageScore(input).value.checks.find(c => c.id === 'no-vulnerabilities')!
199+
expect(check.points).toBe(0)
200+
})
201+
202+
it('excludes vuln check while loading', () => {
203+
const input = createBaseInput()
204+
input.vulnStatus.value = 'pending'
205+
206+
const score = usePackageScore(input).value
207+
expect(score.checks.find(c => c.id === 'no-vulnerabilities')).toBeUndefined()
208+
// maxPoints is reduced (no vuln check = 2 less)
209+
expect(score.maxPoints).toBe(usePackageScore(createBaseInput()).value.maxPoints - 2)
210+
})
211+
212+
it('gives 0 for deprecated packages', () => {
213+
const input = createBaseInput()
214+
const deprecated: SlimVersion = { ...VERSION, deprecated: 'Use something else' }
215+
input.pkg.value = createPkg({ versions: { '1.0.0': deprecated } })
216+
217+
const check = usePackageScore(input).value.checks.find(c => c.id === 'not-deprecated')!
218+
expect(check.points).toBe(0)
219+
})
220+
})
221+
222+
describe('edge cases', () => {
223+
it('handles null pkg gracefully', () => {
224+
const input = createBaseInput()
225+
input.pkg.value = null
226+
227+
const score = usePackageScore(input).value
228+
expect(score.percentage).toBeGreaterThanOrEqual(0)
229+
expect(score.percentage).toBeLessThanOrEqual(100)
230+
})
231+
232+
it('handles null analysis gracefully', () => {
233+
const input = createBaseInput()
234+
input.analysis.value = null
235+
236+
const score = usePackageScore(input).value
237+
const types = score.checks.find(c => c.id === 'has-types')!
238+
const esm = score.checks.find(c => c.id === 'has-esm')!
239+
expect(types.points).toBe(0)
240+
expect(esm.points).toBe(0)
241+
})
242+
})
243+
244+
it('is reactive to input changes', () => {
245+
const input = createBaseInput()
246+
const score = usePackageScore(input)
247+
248+
expect(score.value.percentage).toBe(100)
249+
250+
input.hasProvenance.value = false
251+
expect(score.value.percentage).toBeLessThan(100)
252+
})
253+
})

0 commit comments

Comments
 (0)