k6ctl 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +27 -3
- package/dist/cli.js.map +1 -1
- package/dist/commands/delete.d.ts +10 -0
- package/dist/commands/delete.d.ts.map +1 -0
- package/dist/commands/delete.js +42 -0
- package/dist/commands/delete.js.map +1 -0
- package/dist/commands/list.js +6 -6
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/logs.d.ts +7 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +39 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/run.d.ts +1 -0
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +64 -0
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/status.d.ts +6 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +26 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/services/kubernetes.service.d.ts +8 -2
- package/dist/services/kubernetes.service.d.ts.map +1 -1
- package/dist/services/kubernetes.service.js +71 -23
- package/dist/services/kubernetes.service.js.map +1 -1
- package/dist/types/lastRun.types.d.ts +8 -0
- package/dist/types/lastRun.types.d.ts.map +1 -0
- package/dist/types/lastRun.types.js +3 -0
- package/dist/types/lastRun.types.js.map +1 -0
- package/dist/types/testRunManifest.types.d.ts +2 -4
- package/dist/types/testRunManifest.types.d.ts.map +1 -1
- package/dist/utils/lastRunStore.d.ts +5 -0
- package/dist/utils/lastRunStore.d.ts.map +1 -0
- package/dist/utils/lastRunStore.js +41 -0
- package/dist/utils/lastRunStore.js.map +1 -0
- package/dist/utils/testRunManifestBuilder.d.ts +12 -0
- package/dist/utils/testRunManifestBuilder.d.ts.map +1 -1
- package/dist/utils/testRunManifestBuilder.js +36 -14
- package/dist/utils/testRunManifestBuilder.js.map +1 -1
- package/package.json +1 -1
- package/src/cli.ts +30 -3
- package/src/commands/delete.ts +52 -0
- package/src/commands/list.ts +7 -7
- package/src/commands/logs.ts +41 -0
- package/src/commands/run.ts +69 -0
- package/src/commands/status.ts +28 -0
- package/src/services/kubernetes.service.ts +77 -26
- package/src/types/lastRun.types.ts +7 -0
- package/src/types/testRunManifest.types.ts +3 -4
- package/src/utils/lastRunStore.ts +36 -0
- package/src/utils/testRunManifestBuilder.ts +41 -17
- package/test/integration/kubernetes.service.test.ts +23 -20
- package/test/unit/kubernetes.service.test.ts +3 -0
|
@@ -38,6 +38,9 @@ export class KubernetesService {
|
|
|
38
38
|
|
|
39
39
|
await this.k8sApi.createNamespacedConfigMap({ namespace, body: configMap });
|
|
40
40
|
logger.info(`ConfigMap ${configMapName} created in namespace ${namespace}`);
|
|
41
|
+
|
|
42
|
+
// Clean up the archive file after creating the ConfigMap
|
|
43
|
+
await fs_promises.unlink(archiveFile.archivePath).catch(error => console.error(`Error deleting file ${archiveFile.archivePath}:`, error));
|
|
41
44
|
return { namespace, configMapName };
|
|
42
45
|
}
|
|
43
46
|
|
|
@@ -70,20 +73,7 @@ export class KubernetesService {
|
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
async deleteTestRun(testRunManifest: TestRunManifest): Promise<void> {
|
|
73
|
-
|
|
74
|
-
const response = await this.k8sCustomApi.deleteNamespacedCustomObject({
|
|
75
|
-
group: testRunManifest.apiVersion.split('/')[0],
|
|
76
|
-
version: testRunManifest.apiVersion.split('/')[1],
|
|
77
|
-
namespace: testRunManifest.metadata.namespace,
|
|
78
|
-
plural: "testruns",
|
|
79
|
-
name: testRunManifest.metadata.name,
|
|
80
|
-
});
|
|
81
|
-
logger.info(`TestRun ${testRunManifest.metadata.name} deleted from namespace ${testRunManifest.metadata.namespace}`);
|
|
82
|
-
logger.debug(`Delete response: ${JSON.stringify(response)}`);
|
|
83
|
-
} catch (error) {
|
|
84
|
-
const errorMessage = (error as Error).message ?? 'Unknown error';
|
|
85
|
-
throw new Error(`Failed to delete TestRun ${testRunManifest.metadata.name} from namespace ${testRunManifest.metadata.namespace}: ${errorMessage}`);
|
|
86
|
-
}
|
|
76
|
+
await this.deleteTestRunByName(testRunManifest.metadata.name, testRunManifest.metadata.namespace);
|
|
87
77
|
}
|
|
88
78
|
|
|
89
79
|
async listTestRuns(namespace: string = "default"): Promise<any> {
|
|
@@ -94,38 +84,100 @@ export class KubernetesService {
|
|
|
94
84
|
namespace: namespace,
|
|
95
85
|
plural: "testruns",
|
|
96
86
|
});
|
|
97
|
-
|
|
98
|
-
printTestRunsTable((response as any).items ?? []);
|
|
99
|
-
return response;
|
|
87
|
+
return response.items as TestRunManifest[];
|
|
100
88
|
} catch (error) {
|
|
101
89
|
const errorMessage = (error as Error).message ?? 'Unknown error';
|
|
102
90
|
throw new Error(`Failed to list TestRuns in namespace ${namespace}: ${errorMessage}`);
|
|
103
91
|
}
|
|
104
92
|
}
|
|
105
93
|
|
|
106
|
-
async listPods(namespace: string = "default"): Promise<
|
|
94
|
+
async listPods(namespace: string = "default"): Promise<k8s.V1PodList> {
|
|
107
95
|
try {
|
|
108
96
|
const response = await this.k8sApi.listNamespacedPod({ namespace });
|
|
109
|
-
|
|
110
|
-
// logger.debug(`List Pods: ${JSON.stringify(response)}`);
|
|
111
|
-
return response;
|
|
97
|
+
return response as k8s.V1PodList;
|
|
112
98
|
} catch (error) {
|
|
113
99
|
const errorMessage = (error as Error).message ?? 'Unknown error';
|
|
114
100
|
throw new Error(`Failed to list Pods in namespace ${namespace}: ${errorMessage}`);
|
|
115
101
|
}
|
|
116
102
|
}
|
|
117
103
|
|
|
118
|
-
async listConfigMaps(namespace: string = "default"): Promise<
|
|
104
|
+
async listConfigMaps(namespace: string = "default"): Promise<k8s.V1ConfigMapList> {
|
|
119
105
|
try {
|
|
120
106
|
const response = await this.k8sApi.listNamespacedConfigMap({ namespace });
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
return response;
|
|
107
|
+
response.items = response.items?.filter(cm => cm.metadata?.name?.startsWith('archive-'));
|
|
108
|
+
return response as k8s.V1ConfigMapList;
|
|
124
109
|
} catch (error) {
|
|
125
110
|
const errorMessage = (error as Error).message ?? 'Unknown error';
|
|
126
111
|
throw new Error(`Failed to list ConfigMaps in namespace ${namespace}: ${errorMessage}`);
|
|
127
112
|
}
|
|
128
113
|
}
|
|
114
|
+
|
|
115
|
+
private getTestRunCustomObjectParams(testRunName: string, namespace: string = "default") {
|
|
116
|
+
return {
|
|
117
|
+
group: "k6.io",
|
|
118
|
+
version: "v1alpha1",
|
|
119
|
+
namespace,
|
|
120
|
+
plural: "testruns",
|
|
121
|
+
name: testRunName,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async deleteTestRunByName(testRunName: string, namespace: string = "default"): Promise<void> {
|
|
126
|
+
try {
|
|
127
|
+
await this.k8sCustomApi.deleteNamespacedCustomObject(this.getTestRunCustomObjectParams(testRunName, namespace));
|
|
128
|
+
logger.info(`TestRun ${testRunName} deleted from namespace ${namespace}`);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
const errorMessage = (error as Error).message ?? 'Unknown error';
|
|
131
|
+
throw new Error(`Failed to delete TestRun ${testRunName} from namespace ${namespace}: ${errorMessage}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async deletePodByName(podName: string, namespace: string = "default"): Promise<void> {
|
|
136
|
+
try {
|
|
137
|
+
await this.k8sApi.deleteNamespacedPod({ name: podName, namespace });
|
|
138
|
+
logger.info(`Pod ${podName} deleted from namespace ${namespace}`);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
const errorMessage = (error as Error).message ?? 'Unknown error';
|
|
141
|
+
throw new Error(`Failed to delete Pod ${podName} from namespace ${namespace}: ${errorMessage}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async getTestRun(testRunName: string, namespace: string = "default"): Promise<TestRunManifest> {
|
|
146
|
+
try {
|
|
147
|
+
const response = await this.k8sCustomApi.getNamespacedCustomObject(this.getTestRunCustomObjectParams(testRunName, namespace));
|
|
148
|
+
return response as TestRunManifest;
|
|
149
|
+
} catch (error) {
|
|
150
|
+
const errorMessage = (error as Error).message ?? 'Unknown error';
|
|
151
|
+
throw new Error(`Failed to get TestRun ${testRunName} in namespace ${namespace}: ${errorMessage}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async getPodsForTestRun(testRunName: string, namespace: string = "default"): Promise<k8s.V1PodList> {
|
|
156
|
+
try {
|
|
157
|
+
const response = await this.k8sApi.listNamespacedPod({
|
|
158
|
+
namespace,
|
|
159
|
+
labelSelector: `k6_cr=${testRunName}`,
|
|
160
|
+
});
|
|
161
|
+
return response as k8s.V1PodList;
|
|
162
|
+
} catch (error) {
|
|
163
|
+
const errorMessage = (error as Error).message ?? 'Unknown error';
|
|
164
|
+
throw new Error(`Failed to get pods for TestRun ${testRunName} in namespace ${namespace}: ${errorMessage}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async getPodLogs(podName: string, namespace: string = "default", container?: string): Promise<string> {
|
|
169
|
+
try {
|
|
170
|
+
const response = await this.k8sApi.readNamespacedPodLog({
|
|
171
|
+
name: podName,
|
|
172
|
+
namespace,
|
|
173
|
+
...(container ? { container } : {}),
|
|
174
|
+
});
|
|
175
|
+
return response;
|
|
176
|
+
} catch (error) {
|
|
177
|
+
const errorMessage = (error as Error).message ?? 'Unknown error';
|
|
178
|
+
throw new Error(`Failed to get logs for pod ${podName} in namespace ${namespace}: ${errorMessage}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
129
181
|
}
|
|
130
182
|
|
|
131
183
|
function fmt(v: any) {
|
|
@@ -231,7 +283,6 @@ export function printTestRunsTable(testRuns: TestRunManifest[]): void {
|
|
|
231
283
|
const separate = tr.spec?.separate ?? "N/A";
|
|
232
284
|
const quiet = tr.spec?.quiet ?? "N/A";
|
|
233
285
|
const age = (tr as any)?.metadata?.creationTimestamp ?? "N/A";
|
|
234
|
-
// const age = ageSince((tr as any)?.metadata?.creationTimestamp) || "N/A";
|
|
235
286
|
return [[name, namespace, parallelism, cleanup, separate, quiet, age]];
|
|
236
287
|
}
|
|
237
288
|
});
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { RunnerEnvVar } from "../utils/testRunManifestBuilder";
|
|
2
|
+
|
|
1
3
|
export interface TestRunManifest {
|
|
2
4
|
apiVersion: string;
|
|
3
5
|
kind: string;
|
|
@@ -13,10 +15,7 @@ export interface TestRunManifest {
|
|
|
13
15
|
separate?: boolean;
|
|
14
16
|
runner?: {
|
|
15
17
|
image?: string;
|
|
16
|
-
env?:
|
|
17
|
-
name: string;
|
|
18
|
-
value: string;
|
|
19
|
-
}>;
|
|
18
|
+
env?: RunnerEnvVar[];
|
|
20
19
|
resources?: {
|
|
21
20
|
limits: {
|
|
22
21
|
cpu: string
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import logger from './logger';
|
|
4
|
+
import type { LastRunState } from '../types/lastRun.types';
|
|
5
|
+
|
|
6
|
+
const LAST_RUN_FILE = '.k6ctl-last-run.json';
|
|
7
|
+
|
|
8
|
+
function getFilePath(): string {
|
|
9
|
+
return path.join(process.cwd(), LAST_RUN_FILE);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function saveLastRun(state: LastRunState): Promise<void> {
|
|
13
|
+
const filePath = getFilePath();
|
|
14
|
+
await fs.writeFile(filePath, JSON.stringify(state, null, 2), 'utf8');
|
|
15
|
+
logger.debug(`Last run state saved to ${filePath}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function loadLastRun(): Promise<LastRunState | null> {
|
|
19
|
+
const filePath = getFilePath();
|
|
20
|
+
try {
|
|
21
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
22
|
+
return JSON.parse(content) as LastRunState;
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function clearLastRun(): Promise<void> {
|
|
29
|
+
const filePath = getFilePath();
|
|
30
|
+
try {
|
|
31
|
+
await fs.unlink(filePath);
|
|
32
|
+
logger.debug(`Last run state cleared (${filePath})`);
|
|
33
|
+
} catch {
|
|
34
|
+
logger.info('No last run state to clear.');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -3,7 +3,9 @@ import type { ArchivedFile, ConfigMapResult } from '../types/kubernetes.types';
|
|
|
3
3
|
import { K6Config } from '../types/config.types';
|
|
4
4
|
import { TestRunManifest } from '../types/testRunManifest.types';
|
|
5
5
|
|
|
6
|
-
type RunnerEnvVar =
|
|
6
|
+
export type RunnerEnvVar =
|
|
7
|
+
| { name: string; value: string }
|
|
8
|
+
| { name: string; valueFrom: { secretKeyRef: { name: string; key: string } } };
|
|
7
9
|
const K6_GROUP = "k6.io";
|
|
8
10
|
const K6_VERSION = "v1alpha1";
|
|
9
11
|
const K6_KIND = "TestRun";
|
|
@@ -61,24 +63,15 @@ function buildRunnerEnv(
|
|
|
61
63
|
cfg: K6Config,
|
|
62
64
|
envFromLoader?: Record<string, string>
|
|
63
65
|
): RunnerEnvVar[] | undefined {
|
|
64
|
-
const envMap = new Map<string,
|
|
66
|
+
const envMap = new Map<string, RunnerEnvVar>();
|
|
65
67
|
if (envFromLoader) {
|
|
66
|
-
for (const [key,
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
}
|
|
68
|
+
for (const [key, raw] of Object.entries(envFromLoader)) {
|
|
69
|
+
if (raw === undefined || raw === null) continue;
|
|
70
|
+
addEnvToMap(key, raw, envMap);
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (cfg.prometheus.trendStats?.length) {
|
|
75
|
-
envMap.set(K6_PROMETHEUS_RW_TREND_STATS, cfg.prometheus.trendStats.join(","));
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
const envArray: RunnerEnvVar[] = Array.from(envMap.entries()).map(([name, value]) => ({
|
|
79
|
-
name,
|
|
80
|
-
value,
|
|
81
|
-
}));
|
|
73
|
+
addPrometheusEnvVars(cfg, envMap);
|
|
74
|
+
const envArray: RunnerEnvVar[] = Array.from(envMap.values());
|
|
82
75
|
return envArray.length > 0 ? envArray : undefined;
|
|
83
76
|
}
|
|
84
77
|
|
|
@@ -99,4 +92,35 @@ function buildArgumentsString(args: string[] | undefined, cfg: K6Config, name: s
|
|
|
99
92
|
return undefined;
|
|
100
93
|
}
|
|
101
94
|
return finalArgs.join(" ");
|
|
102
|
-
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function parseSecretPlaceholder(value: string): { secretName: string; secretKey: string } | null {
|
|
98
|
+
const m = /^\{\{SECRETS\.([^.}]+)\.([^.}]+)\}\}$/.exec(value.trim());
|
|
99
|
+
if (!m) return null;
|
|
100
|
+
return { secretName: m[1], secretKey: m[2] };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function addEnvToMap(key: string, raw: string, envMap: Map<string, RunnerEnvVar>) {
|
|
104
|
+
const value = String(raw);
|
|
105
|
+
const secret = parseSecretPlaceholder(value);
|
|
106
|
+
if (secret) {
|
|
107
|
+
envMap.set(key, { name: key, valueFrom: { secretKeyRef: { name: secret.secretName, key: secret.secretKey } } });
|
|
108
|
+
} else {
|
|
109
|
+
envMap.set(key, { name: key, value });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function addPrometheusEnvVars(cfg: K6Config, envMap: Map<string, RunnerEnvVar>) {
|
|
114
|
+
if (cfg.prometheus?.serverUrl) {
|
|
115
|
+
envMap.set(K6_PROMETHEUS_RW_SERVER_URL, {
|
|
116
|
+
name: K6_PROMETHEUS_RW_SERVER_URL,
|
|
117
|
+
value: cfg.prometheus.serverUrl,
|
|
118
|
+
});
|
|
119
|
+
if (cfg.prometheus.trendStats?.length) {
|
|
120
|
+
envMap.set(K6_PROMETHEUS_RW_TREND_STATS, {
|
|
121
|
+
name: K6_PROMETHEUS_RW_TREND_STATS,
|
|
122
|
+
value: cfg.prometheus.trendStats.join(","),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { describe, expect, test } from '@jest/globals';
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
3
|
import { existsSync } from 'node:fs';
|
|
4
|
-
import { unlink } from 'node:fs/promises';
|
|
5
4
|
import { createDefaultKubernetesService } from '../../src/services/kubernetes.service';
|
|
6
5
|
import { ScriptService } from '../../src/services/script.service';
|
|
7
6
|
import { ConfigMapResult } from '../../src/types/kubernetes.types';
|
|
@@ -11,6 +10,11 @@ import { TestRunManifest } from '../../src/types/testRunManifest.types';
|
|
|
11
10
|
import { K6Config } from '../../src/types/config.types';
|
|
12
11
|
import { loadK6Config } from '../../src/utils/configLoader';
|
|
13
12
|
import { buildTestRunManifest } from '../../src/utils/testRunManifestBuilder';
|
|
13
|
+
import { saveLastRun } from '../../src/utils/lastRunStore';
|
|
14
|
+
import { status } from '../../src/commands/status';
|
|
15
|
+
import { logs } from '../../src/commands/logs';
|
|
16
|
+
import { deleteLastRun } from '../../src/commands/delete';
|
|
17
|
+
|
|
14
18
|
|
|
15
19
|
const samplesPath = resolve(__dirname, '..', 'samples');
|
|
16
20
|
const scriptSample1 = join(samplesPath, 'k6_script_sample_1.js');
|
|
@@ -24,18 +28,6 @@ let archiveOutput: ArchiveResult;
|
|
|
24
28
|
let configMapResult: ConfigMapResult;
|
|
25
29
|
|
|
26
30
|
describe('KubernetesService integration tests', () => {
|
|
27
|
-
afterAll(async () => {
|
|
28
|
-
// Clean up archived files after tests
|
|
29
|
-
await Promise.all(
|
|
30
|
-
archivedFiles.map(file => unlink(file).catch(error => console.error(`Error deleting file ${file}:`, error)))
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
// Clean up created config maps in Kubernetes
|
|
34
|
-
await Promise.all(
|
|
35
|
-
configMaps.map(cm => kubernetesService.deleteConfigMap(cm.configMapName, cm.namespace)
|
|
36
|
-
.catch(error => console.error(`Error deleting config map ${cm.configMapName}:`, error)))
|
|
37
|
-
);
|
|
38
|
-
});
|
|
39
31
|
|
|
40
32
|
test('create config map from archived script', async () => {
|
|
41
33
|
archiveOutput = await scriptService.archiveTest(scriptSample2);
|
|
@@ -56,15 +48,26 @@ describe('KubernetesService integration tests', () => {
|
|
|
56
48
|
expect(response).toBeDefined();
|
|
57
49
|
logger.info("TestRun result:", JSON.stringify(response));
|
|
58
50
|
|
|
51
|
+
// Persist last run state for use by logs/status/delete commands
|
|
52
|
+
await saveLastRun({
|
|
53
|
+
testRunName: testRunManifest.metadata.name,
|
|
54
|
+
namespace: testRunManifest.metadata.namespace,
|
|
55
|
+
configMapName: configMapResult.configMapName,
|
|
56
|
+
scriptPath: archiveOutput.archivePath,
|
|
57
|
+
createdAt: new Date().toISOString(),
|
|
58
|
+
});
|
|
59
|
+
logger.info(`Last run saved: ${testRunManifest.metadata.name} (namespace: ${testRunManifest.metadata.namespace})`);
|
|
60
|
+
|
|
59
61
|
// Wait for some time to allow the TestRun to be created and start running
|
|
60
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
62
|
+
await new Promise((resolve) => setTimeout(resolve, 60000));
|
|
63
|
+
|
|
64
|
+
// Check status command output
|
|
65
|
+
await status({ namespace: testRunManifest.metadata.namespace });
|
|
61
66
|
|
|
62
|
-
//
|
|
63
|
-
await
|
|
64
|
-
await kubernetesService.listPods();
|
|
65
|
-
await kubernetesService.listConfigMaps();
|
|
67
|
+
// Check logs command output
|
|
68
|
+
await logs({ namespace: testRunManifest.metadata.namespace });
|
|
66
69
|
|
|
67
70
|
// Clean up the TestRun after successful creation and execution
|
|
68
|
-
await
|
|
71
|
+
await deleteLastRun({ namespace: testRunManifest.metadata.namespace });
|
|
69
72
|
}, 120000);
|
|
70
73
|
});
|
|
@@ -10,6 +10,7 @@ jest.mock('fs', () => ({
|
|
|
10
10
|
promises: {
|
|
11
11
|
stat: jest.fn(),
|
|
12
12
|
readFile: jest.fn(),
|
|
13
|
+
unlink: jest.fn(),
|
|
13
14
|
},
|
|
14
15
|
}));
|
|
15
16
|
|
|
@@ -49,6 +50,7 @@ jest.mock('../../src/utils/logger', () => ({
|
|
|
49
50
|
const mockedExistsSync = existsSync as unknown as jest.Mock;
|
|
50
51
|
const mockedStat = fs_promises.stat as unknown as jest.Mock;
|
|
51
52
|
const mockedReadFile = fs_promises.readFile as unknown as jest.Mock;
|
|
53
|
+
const mockedUnlink = fs_promises.unlink as unknown as jest.MockedFunction<typeof fs_promises.unlink>;
|
|
52
54
|
const mockedLoggerInfo = logger.info as unknown as jest.Mock;
|
|
53
55
|
|
|
54
56
|
const mockedK8sModule = k8s as unknown as {
|
|
@@ -119,6 +121,7 @@ describe('KubernetesService', () => {
|
|
|
119
121
|
});
|
|
120
122
|
|
|
121
123
|
test('creates configmap with binaryData and returns namespace/name', async () => {
|
|
124
|
+
mockedUnlink.mockResolvedValue(undefined);
|
|
122
125
|
mockedExistsSync.mockReturnValue(true);
|
|
123
126
|
mockedStat.mockImplementation(async () => ({ size: 512 }));
|
|
124
127
|
mockedReadFile.mockImplementation(async () => 'YmFzZTY0');
|