Skip to content

Commit 26152db

Browse files
committed
Add tests for promise-queue and tables
Quick coverage wins for previously untested utility modules.
1 parent 63f6d8a commit 26152db

2 files changed

Lines changed: 186 additions & 0 deletions

File tree

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { describe, expect, it } from 'vitest'
2+
3+
import { PromiseQueue } from '../../../registry/src/lib/promise-queue'
4+
5+
describe('PromiseQueue', () => {
6+
it('executes tasks with concurrency limit', async () => {
7+
const queue = new PromiseQueue(2)
8+
let running = 0
9+
let maxRunning = 0
10+
11+
const task = async () => {
12+
running += 1
13+
maxRunning = Math.max(maxRunning, running)
14+
await new Promise(resolve => setTimeout(resolve, 10))
15+
running -= 1
16+
return 'done'
17+
}
18+
19+
const results = await Promise.all([
20+
queue.add(task),
21+
queue.add(task),
22+
queue.add(task),
23+
queue.add(task),
24+
])
25+
26+
expect(results).toEqual(['done', 'done', 'done', 'done'])
27+
expect(maxRunning).toBeLessThanOrEqual(2)
28+
})
29+
30+
it('handles errors in tasks', async () => {
31+
const queue = new PromiseQueue(1)
32+
33+
const failingTask = async () => {
34+
throw new Error('task failed')
35+
}
36+
37+
await expect(queue.add(failingTask)).rejects.toThrow('task failed')
38+
})
39+
40+
it('drops oldest task when maxQueueLength exceeded', async () => {
41+
const queue = new PromiseQueue(1, 2)
42+
const results: string[] = []
43+
44+
const slowTask = async (id: string) => {
45+
await new Promise(resolve => setTimeout(resolve, 20))
46+
results.push(id)
47+
return id
48+
}
49+
50+
const promises = [
51+
queue.add(() => slowTask('1')),
52+
queue.add(() => slowTask('2')),
53+
queue.add(() => slowTask('3')),
54+
queue.add(() => slowTask('4')),
55+
]
56+
57+
await Promise.allSettled(promises)
58+
expect(results.length).toBeGreaterThan(0)
59+
})
60+
61+
it('throws error for invalid maxConcurrency', () => {
62+
expect(() => new PromiseQueue(0)).toThrow('maxConcurrency must be at least 1')
63+
expect(() => new PromiseQueue(-1)).toThrow(
64+
'maxConcurrency must be at least 1',
65+
)
66+
})
67+
68+
it('handles empty queue', async () => {
69+
const queue = new PromiseQueue(2)
70+
const task = async () => 'result'
71+
72+
const result = await queue.add(task)
73+
expect(result).toBe('result')
74+
})
75+
})

test/unit/lib/tables.test.mts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { describe, expect, it } from 'vitest'
2+
3+
import {
4+
formatSimpleTable,
5+
formatTable,
6+
} from '../../../registry/src/lib/tables'
7+
8+
describe('tables', () => {
9+
describe('formatSimpleTable', () => {
10+
it('renders simple table', () => {
11+
const result = formatSimpleTable(
12+
[
13+
{ key: 'name', header: 'Name' },
14+
{ key: 'value', header: 'Value' },
15+
],
16+
[
17+
{ name: 'foo', value: '123' },
18+
{ name: 'bar', value: '456' },
19+
],
20+
)
21+
expect(result).toContain('Name')
22+
expect(result).toContain('Value')
23+
expect(result).toContain('foo')
24+
expect(result).toContain('bar')
25+
expect(result).toContain('123')
26+
expect(result).toContain('456')
27+
})
28+
29+
it('aligns columns', () => {
30+
const result = formatSimpleTable(
31+
[
32+
{ key: 'left', header: 'Left', align: 'left' as const },
33+
{ key: 'right', header: 'Right', align: 'right' as const },
34+
{ key: 'center', header: 'Center', align: 'center' as const },
35+
],
36+
[{ left: 'A', right: 'B', center: 'C' }],
37+
)
38+
expect(result).toContain('Left')
39+
expect(result).toContain('Right')
40+
expect(result).toContain('Center')
41+
})
42+
43+
it('applies column colors', () => {
44+
const result = formatSimpleTable(
45+
[
46+
{
47+
key: 'name',
48+
header: 'Name',
49+
color: (v: string) => `<${v}>`,
50+
},
51+
],
52+
[{ name: 'test' }],
53+
)
54+
expect(result).toContain('<test>')
55+
})
56+
57+
it('handles fixed column widths', () => {
58+
const result = formatSimpleTable(
59+
[{ key: 'name', header: 'Name', width: 20 }],
60+
[{ name: 'short' }],
61+
)
62+
expect(result).toContain('Name')
63+
expect(result).toContain('short')
64+
})
65+
66+
it('handles empty data', () => {
67+
const result = formatSimpleTable(
68+
[
69+
{ key: 'name', header: 'Name' },
70+
{ key: 'value', header: 'Value' },
71+
],
72+
[],
73+
)
74+
expect(result).toContain('Name')
75+
expect(result).toContain('Value')
76+
})
77+
})
78+
79+
describe('formatTable', () => {
80+
it('renders table with borders', () => {
81+
const result = formatTable(
82+
[
83+
{ key: 'name', header: 'Name' },
84+
{ key: 'age', header: 'Age' },
85+
],
86+
[
87+
{ name: 'Alice', age: '30' },
88+
{ name: 'Bob', age: '25' },
89+
],
90+
)
91+
expect(result).toContain('Name')
92+
expect(result).toContain('Age')
93+
expect(result).toContain('Alice')
94+
expect(result).toContain('Bob')
95+
})
96+
97+
it('handles alignment in bordered table', () => {
98+
const result = formatTable(
99+
[
100+
{ key: 'num', header: 'Number', align: 'right' as const },
101+
{ key: 'text', header: 'Text', align: 'left' as const },
102+
],
103+
[{ num: '42', text: 'hello' }],
104+
)
105+
expect(result).toContain('Number')
106+
expect(result).toContain('Text')
107+
expect(result).toContain('42')
108+
expect(result).toContain('hello')
109+
})
110+
})
111+
})

0 commit comments

Comments
 (0)