Skip to content

Commit e64d7ad

Browse files
[Test Improver] test: add unit tests for versions.ts (#17815)
* test: add unit tests for versions.ts 10 tests for the Versions singleton class covering: - Dispatch calls for /rancherversion and /version endpoints - setVersionData/setKubeVersionData called with responses - Promise caching (second fetch does not re-dispatch) - Error handling: no throw on network errors, console.warn called 100% statements/branches/functions/lines coverage. 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 39359f6 commit e64d7ad

1 file changed

Lines changed: 128 additions & 0 deletions

File tree

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
function makeStoreWithCalls(rancherPromise: Promise<any>, kubePromise: Promise<any>) {
2+
let callCount = 0;
3+
4+
return {
5+
dispatch: jest.fn(() => {
6+
callCount++;
7+
8+
return callCount === 1 ? rancherPromise : kubePromise;
9+
}),
10+
};
11+
}
12+
13+
describe('versions', () => {
14+
let versions: any;
15+
let mockSetVersionData: jest.Mock;
16+
let mockSetKubeVersionData: jest.Mock;
17+
18+
beforeEach(() => {
19+
jest.resetModules();
20+
mockSetVersionData = jest.fn();
21+
mockSetKubeVersionData = jest.fn();
22+
jest.mock('@shell/config/version', () => ({
23+
setVersionData: mockSetVersionData,
24+
setKubeVersionData: mockSetKubeVersionData,
25+
}));
26+
versions = require('@shell/utils/versions').default;
27+
});
28+
29+
describe('fetch', () => {
30+
it('dispatches rancher/request for /rancherversion', async() => {
31+
const store = { dispatch: jest.fn(() => Promise.resolve({})) };
32+
33+
await versions.fetch({ store });
34+
35+
expect(store.dispatch).toHaveBeenCalledWith('rancher/request', {
36+
url: '/rancherversion',
37+
method: 'get',
38+
redirectUnauthorized: false,
39+
});
40+
});
41+
42+
it('dispatches rancher/request for /version', async() => {
43+
const store = { dispatch: jest.fn(() => Promise.resolve({})) };
44+
45+
await versions.fetch({ store });
46+
47+
expect(store.dispatch).toHaveBeenCalledWith('rancher/request', {
48+
url: '/version',
49+
method: 'get',
50+
redirectUnauthorized: false,
51+
});
52+
});
53+
54+
it('calls setVersionData with the rancher version response', async() => {
55+
const rancherResponse = { Version: '2.9.0', GitCommit: 'abc123' };
56+
const store = makeStoreWithCalls(Promise.resolve(rancherResponse), Promise.resolve({}));
57+
58+
await versions.fetch({ store });
59+
60+
expect(mockSetVersionData).toHaveBeenCalledWith(rancherResponse);
61+
});
62+
63+
it('calls setKubeVersionData with the kube version response', async() => {
64+
const kubeResponse = { gitVersion: 'v1.29.0' };
65+
const store = makeStoreWithCalls(Promise.resolve({}), Promise.resolve(kubeResponse));
66+
67+
await versions.fetch({ store });
68+
69+
expect(mockSetKubeVersionData).toHaveBeenCalledWith(kubeResponse);
70+
});
71+
72+
it('caches the promise — second call does not dispatch again', async() => {
73+
const store = { dispatch: jest.fn(() => Promise.resolve({})) };
74+
75+
await versions.fetch({ store });
76+
await versions.fetch({ store });
77+
78+
expect(store.dispatch).toHaveBeenCalledTimes(2);
79+
});
80+
81+
it('returns the same promise object on repeated calls', () => {
82+
const store = { dispatch: jest.fn(() => Promise.resolve({})) };
83+
84+
versions.fetch({ store });
85+
versions.fetch({ store });
86+
87+
// Both initial calls should dispatch only twice total (2 endpoints, cached after that)
88+
expect(store.dispatch).toHaveBeenCalledTimes(2);
89+
// Third fetch should not dispatch additional times
90+
versions.fetch({ store });
91+
expect(store.dispatch).toHaveBeenCalledTimes(2);
92+
});
93+
94+
it('does not throw when rancher request fails', async() => {
95+
const store = makeStoreWithCalls(Promise.reject(new Error('network error')), Promise.resolve({}));
96+
97+
await expect(versions.fetch({ store })).resolves.not.toThrow();
98+
});
99+
100+
it('does not throw when kube request fails', async() => {
101+
const store = makeStoreWithCalls(Promise.resolve({}), Promise.reject(new Error('network error')));
102+
103+
await expect(versions.fetch({ store })).resolves.not.toThrow();
104+
});
105+
106+
it('logs a warning when rancher request fails', async() => {
107+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
108+
const error = new Error('rancher down');
109+
const store = makeStoreWithCalls(Promise.reject(error), Promise.resolve({}));
110+
111+
await versions.fetch({ store });
112+
113+
expect(warnSpy).toHaveBeenCalledWith('Failed to fetch Rancher version metadata', error);
114+
warnSpy.mockRestore();
115+
});
116+
117+
it('logs a warning when kube request fails', async() => {
118+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
119+
const error = new Error('kube down');
120+
const store = makeStoreWithCalls(Promise.resolve({}), Promise.reject(error));
121+
122+
await versions.fetch({ store });
123+
124+
expect(warnSpy).toHaveBeenCalledWith('Failed to fetch Kube version metadata', error);
125+
warnSpy.mockRestore();
126+
});
127+
});
128+
});

0 commit comments

Comments
 (0)