-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathruntime-versions.test.ts
More file actions
145 lines (128 loc) · 5.63 KB
/
Copy pathruntime-versions.test.ts
File metadata and controls
145 lines (128 loc) · 5.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import { describe, expect, it } from 'vitest'
import {
compareVersions,
expectedVersion,
parseEmbeddedVersions,
pinnedSpec,
RUNTIME_PACKAGE_VERSIONS,
} from '../runtime-versions.js'
// Source-mode runs (this test) never see the tsup define, so the embedded map
// must be empty and every helper must degrade to the unpinned behaviour —
// that fallback is itself part of the contract (dev/`tsx` runs keep working).
describe('runtime-versions (no build-time embed)', () => {
it('exposes an empty version map', () => {
expect(RUNTIME_PACKAGE_VERSIONS).toEqual({})
})
it('pinnedSpec falls back to the bare package name', () => {
expect(pinnedSpec('@cipherstash/stack')).toBe('@cipherstash/stack')
})
it('expectedVersion is undefined', () => {
expect(expectedVersion('@cipherstash/stack')).toBeUndefined()
})
})
// The embed parser is what stands between a build defect and silently
// unpinned installs (#661): absent (source mode) is fine; present-but-broken
// must THROW, never degrade to {}.
describe('parseEmbeddedVersions', () => {
it('absent embed (source mode) yields an empty map', () => {
expect(parseEmbeddedVersions(undefined)).toEqual({})
})
it('parses a valid embed', () => {
expect(
parseEmbeddedVersions('{"stash":"9.9.9-test.1","@x/y":"8.8.8-test.2"}'),
).toEqual({ stash: '9.9.9-test.1', '@x/y': '8.8.8-test.2' })
})
it('throws on unparseable JSON instead of degrading to unpinned', () => {
expect(() => parseEmbeddedVersions('{not json')).toThrow(
/build defect.*not valid JSON/s,
)
})
it('throws on a non-object shape', () => {
expect(() => parseEmbeddedVersions('["stash"]')).toThrow(
/not a plain object/,
)
expect(() => parseEmbeddedVersions('null')).toThrow(/not a plain object/)
expect(() => parseEmbeddedVersions('"9.9.9"')).toThrow(/not a plain object/)
})
it('throws on a missing/non-string version value', () => {
expect(() => parseEmbeddedVersions('{"stash":""}')).toThrow(
/usable version for "stash"/,
)
expect(() => parseEmbeddedVersions('{"stash":42}')).toThrow(
/usable version for "stash"/,
)
})
})
describe('runtime-versions (explicit version map)', () => {
// Arbitrary FIXTURE versions, deliberately unreal: these tests assert the
// map is threaded through verbatim, not that any actual release exists.
// Real versions are never hard-coded anywhere — the shipped map is read
// from the workspace manifests at build time (tsup.config.ts).
const versions = {
stash: '9.9.9-test.1',
'@cipherstash/stack': '9.9.9-test.1',
'@cipherstash/prisma-next': '8.8.8-test.2',
}
it('pins known packages to the release version', () => {
expect(pinnedSpec('@cipherstash/stack', versions)).toBe(
'@cipherstash/stack@9.9.9-test.1',
)
// prisma-next versions on its own line — the map, not a shared constant,
// is the source of truth.
expect(pinnedSpec('@cipherstash/prisma-next', versions)).toBe(
'@cipherstash/prisma-next@8.8.8-test.2',
)
})
it('leaves unknown packages unpinned', () => {
expect(pinnedSpec('@cipherstash/not-on-the-train', versions)).toBe(
'@cipherstash/not-on-the-train',
)
})
it('expectedVersion reads the map', () => {
expect(expectedVersion('stash', versions)).toBe('9.9.9-test.1')
expect(expectedVersion('nope', versions)).toBeUndefined()
})
})
// Precedence per semver §11, over the shapes this repo actually publishes.
describe('compareVersions', () => {
it('orders numeric cores', () => {
expect(compareVersions('0.19.0', '1.0.0-rc.1')).toBe(-1)
expect(compareVersions('1.1.0', '1.0.9')).toBe(1)
expect(compareVersions('1.0.0', '1.0.0')).toBe(0)
})
it('a release outranks its own prereleases', () => {
expect(compareVersions('1.0.0', '1.0.0-rc.9')).toBe(1)
expect(compareVersions('1.0.0-rc.9', '1.0.0')).toBe(-1)
})
it('orders prerelease identifiers numerically, not lexically', () => {
expect(compareVersions('1.0.0-rc.2', '1.0.0-rc.1')).toBe(1)
// Lexical comparison would get this one wrong ('10' < '2').
expect(compareVersions('1.0.0-rc.10', '1.0.0-rc.2')).toBe(1)
expect(compareVersions('1.0.0-rc.1', '1.0.0-rc.1')).toBe(0)
})
it('treats any malformed version as not comparable (never "ahead")', () => {
// A corrupt manifest version must not partially parse into 1 ("installed
// is newer") — that would suppress the align guidance. The strict shape
// gate rejects every malformed class, not just NaN-able cores:
expect(compareVersions('v1.0.0', '1.0.0')).toBe(0)
expect(compareVersions('1.0.x', '1.0.0')).toBe(0)
expect(compareVersions('garbage', '1.0.0')).toBe(0)
// truncated core — '1.0' padded to 1.0.0 would outrank 1.0.0-rc.2
expect(compareVersions('1.0', '1.0.0-rc.2')).toBe(0)
// extra core segment
expect(compareVersions('1.2.3.4', '1.2.3')).toBe(0)
// empty prerelease — '1.0.0-' as a release would outrank any rc
expect(compareVersions('1.0.0-', '1.0.0-rc.1')).toBe(0)
// parseInt trailing garbage — '9.9.10rc' would parse as core 9.9.10
expect(compareVersions('9.9.10rc', '9.9.9')).toBe(0)
expect(compareVersions('1.0.0beta', '1.0.0-rc.1')).toBe(0)
// build metadata — '2+sha' as an identifier would mis-order vs 'rc.10'
expect(compareVersions('1.0.0-rc.2+sha.abc', '1.0.0-rc.10')).toBe(0)
// empty prerelease identifier
expect(compareVersions('1.0.0-rc..1', '1.0.0-rc.1')).toBe(0)
})
it('numeric identifiers sort below alphanumeric; shorter below longer', () => {
expect(compareVersions('1.0.0-1', '1.0.0-alpha')).toBe(-1)
expect(compareVersions('1.0.0-rc', '1.0.0-rc.1')).toBe(-1)
})
})