Skip to content

Commit c77eec0

Browse files
committed
fix version incrementing
1 parent 3a1a1e2 commit c77eec0

File tree

3 files changed

+383
-41
lines changed

3 files changed

+383
-41
lines changed
Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
import { describe, expect, it, afterEach, spyOn, mock } from 'bun:test'
2+
3+
import * as versionUtils from '../version-utils'
4+
5+
const {
6+
versionOne,
7+
parseVersion,
8+
stringifyVersion,
9+
incrementPatchVersion,
10+
getMaximumVersion,
11+
getLatestAgentVersion,
12+
determineNextVersion,
13+
versionExists,
14+
} = versionUtils
15+
16+
describe('version-utils', () => {
17+
afterEach(() => {
18+
mock.restore()
19+
})
20+
21+
describe('versionOne', () => {
22+
it('should return version 0.0.1', () => {
23+
const result = versionOne()
24+
expect(result).toEqual({ major: 0, minor: 0, patch: 1 })
25+
})
26+
})
27+
28+
describe('parseVersion', () => {
29+
it('should parse valid semantic version strings', () => {
30+
expect(parseVersion('1.2.3')).toEqual({ major: 1, minor: 2, patch: 3 })
31+
expect(parseVersion('0.0.1')).toEqual({ major: 0, minor: 0, patch: 1 })
32+
expect(parseVersion('10.20.30')).toEqual({
33+
major: 10,
34+
minor: 20,
35+
patch: 30,
36+
})
37+
})
38+
39+
it('should throw error for invalid version formats', () => {
40+
expect(() => parseVersion('1.2')).toThrow(
41+
'Invalid semantic version format: 1.2',
42+
)
43+
expect(() => parseVersion('1.2.3.4')).toThrow(
44+
'Invalid semantic version format: 1.2.3.4',
45+
)
46+
expect(() => parseVersion('v1.2.3')).toThrow(
47+
'Invalid semantic version format: v1.2.3',
48+
)
49+
expect(() => parseVersion('1.2.3-alpha')).toThrow(
50+
'Invalid semantic version format: 1.2.3-alpha',
51+
)
52+
expect(() => parseVersion('')).toThrow(
53+
'Invalid semantic version format: ',
54+
)
55+
expect(() => parseVersion('abc.def.ghi')).toThrow(
56+
'Invalid semantic version format: abc.def.ghi',
57+
)
58+
})
59+
})
60+
61+
describe('stringifyVersion', () => {
62+
it('should convert version object to string', () => {
63+
expect(stringifyVersion({ major: 1, minor: 2, patch: 3 })).toBe('1.2.3')
64+
expect(stringifyVersion({ major: 0, minor: 0, patch: 1 })).toBe('0.0.1')
65+
expect(stringifyVersion({ major: 10, minor: 20, patch: 30 })).toBe(
66+
'10.20.30',
67+
)
68+
})
69+
})
70+
71+
describe('incrementPatchVersion', () => {
72+
it('should increment patch version by 1', () => {
73+
expect(incrementPatchVersion({ major: 1, minor: 2, patch: 3 })).toEqual({
74+
major: 1,
75+
minor: 2,
76+
patch: 4,
77+
})
78+
expect(incrementPatchVersion({ major: 0, minor: 0, patch: 0 })).toEqual({
79+
major: 0,
80+
minor: 0,
81+
patch: 1,
82+
})
83+
})
84+
85+
it('should not modify the original version object', () => {
86+
const original = { major: 1, minor: 2, patch: 3 }
87+
const result = incrementPatchVersion(original)
88+
expect(original).toEqual({ major: 1, minor: 2, patch: 3 })
89+
expect(result).toEqual({ major: 1, minor: 2, patch: 4 })
90+
})
91+
})
92+
93+
describe('getMaximumVersion', () => {
94+
it('should return version with higher major version', () => {
95+
const v1 = { major: 2, minor: 0, patch: 0 }
96+
const v2 = { major: 1, minor: 9, patch: 9 }
97+
expect(getMaximumVersion(v1, v2)).toEqual(v1)
98+
expect(getMaximumVersion(v2, v1)).toEqual(v1)
99+
})
100+
101+
it('should return version with higher minor version when major is same', () => {
102+
const v1 = { major: 1, minor: 2, patch: 0 }
103+
const v2 = { major: 1, minor: 1, patch: 9 }
104+
expect(getMaximumVersion(v1, v2)).toEqual(v1)
105+
expect(getMaximumVersion(v2, v1)).toEqual(v1)
106+
})
107+
108+
it('should return version with higher patch version when major and minor are same', () => {
109+
const v1 = { major: 1, minor: 2, patch: 4 }
110+
const v2 = { major: 1, minor: 2, patch: 3 }
111+
expect(getMaximumVersion(v1, v2)).toEqual(v1)
112+
expect(getMaximumVersion(v2, v1)).toEqual(v1)
113+
})
114+
115+
it('should return first version when versions are equal', () => {
116+
const v1 = { major: 1, minor: 2, patch: 3 }
117+
const v2 = { major: 1, minor: 2, patch: 3 }
118+
expect(getMaximumVersion(v1, v2)).toEqual(v1)
119+
})
120+
})
121+
122+
describe('getLatestAgentVersion', () => {
123+
it('should return version 0.0.0 when no agent exists', async () => {
124+
// Mock the database to return empty result
125+
mock.module('@codebuff/common/db', () => ({
126+
default: {
127+
select: () => ({
128+
from: () => ({
129+
where: () => ({
130+
orderBy: () => ({
131+
limit: () => ({
132+
then: (fn: (rows: any[]) => any) => fn([]),
133+
}),
134+
}),
135+
}),
136+
}),
137+
}),
138+
},
139+
}))
140+
141+
const result = await getLatestAgentVersion('test-agent', 'test-publisher')
142+
expect(result).toEqual({ major: 0, minor: 0, patch: 0 })
143+
})
144+
145+
it('should return latest version when agent exists', async () => {
146+
// Mock the database to return a version
147+
mock.module('@codebuff/common/db', () => ({
148+
default: {
149+
select: () => ({
150+
from: () => ({
151+
where: () => ({
152+
orderBy: () => ({
153+
limit: () => ({
154+
then: (fn: (rows: any[]) => any) =>
155+
fn([{ major: 1, minor: 2, patch: 3 }]),
156+
}),
157+
}),
158+
}),
159+
}),
160+
}),
161+
},
162+
}))
163+
164+
const result = await getLatestAgentVersion('test-agent', 'test-publisher')
165+
expect(result).toEqual({ major: 1, minor: 2, patch: 3 })
166+
})
167+
168+
it('should handle null values in database response', async () => {
169+
// Mock the database to return null values
170+
mock.module('@codebuff/common/db', () => ({
171+
default: {
172+
select: () => ({
173+
from: () => ({
174+
where: () => ({
175+
orderBy: () => ({
176+
limit: () => ({
177+
then: (fn: (rows: any[]) => any) =>
178+
fn([{ major: null, minor: null, patch: null }]),
179+
}),
180+
}),
181+
}),
182+
}),
183+
}),
184+
},
185+
}))
186+
187+
const result = await getLatestAgentVersion('test-agent', 'test-publisher')
188+
expect(result).toEqual({ major: 0, minor: 0, patch: 0 })
189+
})
190+
})
191+
192+
describe('determineNextVersion', () => {
193+
it('should increment patch of latest version when no version provided', async () => {
194+
spyOn(versionUtils, 'getLatestAgentVersion').mockResolvedValue({
195+
major: 1,
196+
minor: 2,
197+
patch: 3,
198+
})
199+
200+
const result = await determineNextVersion('test-agent', 'test-publisher')
201+
expect(result).toEqual({ major: 1, minor: 2, patch: 4 })
202+
})
203+
204+
it('should use provided version when higher than incremented latest', async () => {
205+
spyOn(versionUtils, 'getLatestAgentVersion').mockResolvedValue({
206+
major: 0,
207+
minor: 0,
208+
patch: 0,
209+
})
210+
211+
const result = await determineNextVersion(
212+
'test-agent',
213+
'test-publisher',
214+
'2.0.0',
215+
)
216+
expect(result).toEqual({ major: 2, minor: 0, patch: 0 })
217+
})
218+
219+
it('should use maximum of latest and provided version', async () => {
220+
spyOn(versionUtils, 'getLatestAgentVersion').mockResolvedValue({
221+
major: 2,
222+
minor: 0,
223+
patch: 0,
224+
})
225+
226+
const result = await determineNextVersion(
227+
'test-agent',
228+
'test-publisher',
229+
'1.5.0',
230+
)
231+
expect(result).toEqual({ major: 2, minor: 0, patch: 1 })
232+
})
233+
234+
it('should throw error for invalid provided version', async () => {
235+
spyOn(versionUtils, 'getLatestAgentVersion').mockResolvedValue({
236+
major: 0,
237+
minor: 0,
238+
patch: 0,
239+
})
240+
241+
await expect(
242+
determineNextVersion('test-agent', 'test-publisher', 'invalid'),
243+
).rejects.toThrow(
244+
'Invalid version format: invalid. Must be in semver format (e.g., 1.0.0)',
245+
)
246+
})
247+
})
248+
249+
describe('versionExists', () => {
250+
it('should return true when version exists', async () => {
251+
// Mock the database to return a result
252+
mock.module('@codebuff/common/db', () => ({
253+
default: {
254+
select: () => ({
255+
from: () => ({
256+
where: () => ({
257+
then: (fn: (rows: any[]) => any) => fn([{ id: 'test-agent' }]),
258+
}),
259+
}),
260+
}),
261+
},
262+
}))
263+
264+
const result = await versionExists(
265+
'test-agent',
266+
{ major: 1, minor: 0, patch: 0 },
267+
'test-publisher',
268+
)
269+
expect(result).toBe(true)
270+
})
271+
272+
it('should return false when version does not exist', async () => {
273+
// Mock the database to return empty result
274+
mock.module('@codebuff/common/db', () => ({
275+
default: {
276+
select: () => ({
277+
from: () => ({
278+
where: () => ({
279+
then: (fn: (rows: any[]) => any) => fn([]),
280+
}),
281+
}),
282+
}),
283+
},
284+
}))
285+
286+
const result = await versionExists(
287+
'test-agent',
288+
{ major: 1, minor: 0, patch: 0 },
289+
'test-publisher',
290+
)
291+
expect(result).toBe(false)
292+
})
293+
})
294+
295+
describe('integration tests', () => {
296+
it('should handle complete version workflow', () => {
297+
// Test the complete flow of version operations
298+
const version1 = parseVersion('1.2.3')
299+
const version2 = parseVersion('1.2.4')
300+
const maxVersion = getMaximumVersion(version1, version2)
301+
const nextVersion = incrementPatchVersion(maxVersion)
302+
const versionString = stringifyVersion(nextVersion)
303+
304+
expect(versionString).toBe('1.2.5')
305+
})
306+
307+
it('should handle edge cases with versionOne', () => {
308+
const one = versionOne()
309+
const incremented = incrementPatchVersion(one)
310+
const max = getMaximumVersion(one, incremented)
311+
312+
expect(max).toEqual({ major: 0, minor: 0, patch: 2 })
313+
})
314+
})
315+
})

0 commit comments

Comments
 (0)