Skip to content

Commit e73a63e

Browse files
[Test Improver] Add unit tests for namespace-filter.js and async.ts utilities (#17692)
* test: add unit tests for namespace-filter.js and async.ts utilities - namespace-filter.js: 14 tests covering all exported constants and createNamespaceFilterKey / createNamespaceFilterKeyWithId / splitNamespaceFilterKey (100% stmts/branches/fns/lines) - async.ts: 6 tests for wait() and waitFor() using fake timers, covering immediate resolve, polling, timeout with message, and timeout without message (94% stmts, 86% branches, 100% fns) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * ci: trigger checks --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 8d82e5b commit e73a63e

2 files changed

Lines changed: 196 additions & 0 deletions

File tree

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { waitFor, wait } from '@shell/utils/async';
2+
3+
describe('wait', () => {
4+
it('resolves after the specified delay', async() => {
5+
jest.useFakeTimers();
6+
7+
const p = wait(1000);
8+
9+
jest.advanceTimersByTime(1000);
10+
await expect(p).resolves.toBeUndefined();
11+
12+
jest.useRealTimers();
13+
});
14+
15+
it('does not resolve before the delay elapses', async() => {
16+
jest.useFakeTimers();
17+
18+
let resolved = false;
19+
const p = wait(500).then(() => {
20+
resolved = true;
21+
});
22+
23+
jest.advanceTimersByTime(499);
24+
expect(resolved).toBe(false);
25+
26+
jest.advanceTimersByTime(1);
27+
await p;
28+
expect(resolved).toBe(true);
29+
30+
jest.useRealTimers();
31+
});
32+
});
33+
34+
describe('waitFor', () => {
35+
afterEach(() => {
36+
jest.useRealTimers();
37+
});
38+
39+
it('resolves immediately when testFn returns true on first call', async() => {
40+
jest.useFakeTimers();
41+
const testFn = jest.fn().mockReturnValue(true);
42+
43+
const p = waitFor(testFn, 'ready');
44+
45+
await expect(p).resolves.toBeDefined();
46+
expect(testFn).toHaveBeenCalledTimes(1);
47+
});
48+
49+
it('resolves after testFn eventually returns true', async() => {
50+
jest.useFakeTimers();
51+
let count = 0;
52+
const testFn = jest.fn().mockImplementation(() => {
53+
count++;
54+
55+
return count >= 3;
56+
});
57+
58+
const p = waitFor(testFn, 'ready', 10000, 500);
59+
60+
// first call returns false (immediate check), then intervals fire
61+
jest.advanceTimersByTime(500); // interval 1 → count=2, false
62+
jest.advanceTimersByTime(500); // interval 2 → count=3, true
63+
await p;
64+
65+
expect(testFn).toHaveBeenCalledTimes(3);
66+
});
67+
68+
it('rejects with msg when timeout elapses', async() => {
69+
jest.useFakeTimers();
70+
const testFn = jest.fn().mockReturnValue(false);
71+
72+
const p = waitFor(testFn, 'some condition', 1000, 500);
73+
74+
jest.advanceTimersByTime(1001);
75+
await expect(p).rejects.toThrow('Failed waiting for: some condition');
76+
});
77+
78+
it('rejects with generic message when no msg provided and timeout elapses', async() => {
79+
jest.useFakeTimers();
80+
const testFn = jest.fn().mockReturnValue(false);
81+
82+
const p = waitFor(testFn, '', 2000, 500);
83+
84+
jest.advanceTimersByTime(2001);
85+
await expect(p).rejects.toThrow('waitFor timed out after 2 seconds');
86+
});
87+
});
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import {
2+
NAMESPACE_FILTER_ALL_PREFIX,
3+
NAMESPACE_FILTER_NS_PREFIX,
4+
NAMESPACE_FILTER_P_PREFIX,
5+
NAMESPACE_FILTER_NS_FULL_PREFIX,
6+
NAMESPACE_FILTER_P_FULL_PREFIX,
7+
NAMESPACE_FILTER_ALL,
8+
NAMESPACE_FILTER_ALL_SYSTEM,
9+
NAMESPACE_FILTER_ALL_USER,
10+
NAMESPACE_FILTER_ALL_ORPHANS,
11+
NAMESPACE_FILTER_NAMESPACED_PREFIX,
12+
NAMESPACE_FILTER_NAMESPACED_YES,
13+
NAMESPACE_FILTER_NAMESPACED_NO,
14+
NAMESPACE_FILTER_KINDS,
15+
createNamespaceFilterKey,
16+
createNamespaceFilterKeyWithId,
17+
splitNamespaceFilterKey,
18+
} from '@shell/utils/namespace-filter';
19+
20+
describe('namespace-filter constants', () => {
21+
it('prefix constants have expected values', () => {
22+
expect(NAMESPACE_FILTER_ALL_PREFIX).toStrictEqual('all');
23+
expect(NAMESPACE_FILTER_NS_PREFIX).toStrictEqual('ns');
24+
expect(NAMESPACE_FILTER_P_PREFIX).toStrictEqual('project');
25+
});
26+
27+
it('full prefix constants are built from base prefixes', () => {
28+
expect(NAMESPACE_FILTER_NS_FULL_PREFIX).toStrictEqual('ns://');
29+
expect(NAMESPACE_FILTER_P_FULL_PREFIX).toStrictEqual('project://');
30+
});
31+
32+
it('all-namespace filter constants have expected values', () => {
33+
expect(NAMESPACE_FILTER_ALL).toStrictEqual('all');
34+
expect(NAMESPACE_FILTER_ALL_SYSTEM).toStrictEqual('all://system');
35+
expect(NAMESPACE_FILTER_ALL_USER).toStrictEqual('all://user');
36+
expect(NAMESPACE_FILTER_ALL_ORPHANS).toStrictEqual('all://orphans');
37+
});
38+
39+
it('namespaced filter constants have expected values', () => {
40+
expect(NAMESPACE_FILTER_NAMESPACED_PREFIX).toStrictEqual('namespaced://');
41+
expect(NAMESPACE_FILTER_NAMESPACED_YES).toStrictEqual('namespaced://true');
42+
expect(NAMESPACE_FILTER_NAMESPACED_NO).toStrictEqual('namespaced://false');
43+
});
44+
45+
it('nAMESPACE_FILTER_KINDS has expected shape', () => {
46+
expect(NAMESPACE_FILTER_KINDS).toStrictEqual({
47+
DIVIDER: 'divider',
48+
PROJECT: 'project',
49+
NAMESPACE: 'namespace',
50+
SPECIAL: 'special',
51+
});
52+
});
53+
});
54+
55+
describe('createNamespaceFilterKey', () => {
56+
it('returns clusterId when product is undefined', () => {
57+
expect(createNamespaceFilterKey('c-abc', undefined)).toStrictEqual('c-abc');
58+
});
59+
60+
it('returns clusterId when product has no customNamespaceFilter', () => {
61+
expect(createNamespaceFilterKey('c-abc', { name: 'manager' })).toStrictEqual('c-abc');
62+
});
63+
64+
it('returns clusterId when product.customNamespaceFilter is falsy', () => {
65+
expect(createNamespaceFilterKey('c-abc', { name: 'manager', customNamespaceFilter: false })).toStrictEqual('c-abc');
66+
});
67+
68+
it('returns compound key when product has customNamespaceFilter', () => {
69+
const result = createNamespaceFilterKey('c-abc', { name: 'myProduct', customNamespaceFilter: true });
70+
71+
expect(result).toStrictEqual('c-abc__%%__myProduct');
72+
});
73+
});
74+
75+
describe('createNamespaceFilterKeyWithId', () => {
76+
it('combines clusterId and productId with separator', () => {
77+
expect(createNamespaceFilterKeyWithId('c-abc', 'myProduct')).toStrictEqual('c-abc__%%__myProduct');
78+
});
79+
80+
it('works with empty strings', () => {
81+
expect(createNamespaceFilterKeyWithId('', '')).toStrictEqual('__%%__');
82+
});
83+
});
84+
85+
describe('splitNamespaceFilterKey', () => {
86+
it('splits a compound key into clusterId and productId', () => {
87+
expect(splitNamespaceFilterKey('c-abc__%%__myProduct')).toStrictEqual({
88+
clusterId: 'c-abc',
89+
productId: 'myProduct',
90+
});
91+
});
92+
93+
it('returns undefined productId when key has no separator', () => {
94+
expect(splitNamespaceFilterKey('c-abc')).toStrictEqual({
95+
clusterId: 'c-abc',
96+
productId: undefined,
97+
});
98+
});
99+
100+
it('round-trips with createNamespaceFilterKeyWithId', () => {
101+
const key = createNamespaceFilterKeyWithId('cluster-1', 'product-a');
102+
const result = splitNamespaceFilterKey(key);
103+
104+
expect(result).toStrictEqual({
105+
clusterId: 'cluster-1',
106+
productId: 'product-a',
107+
});
108+
});
109+
});

0 commit comments

Comments
 (0)