Skip to content

Commit b7e8141

Browse files
committed
Update frontend unit tests
Signed-off-by: droctothorpe <mythicalsunlight@gmail.com>
1 parent b65348c commit b7e8141

File tree

5 files changed

+92
-43
lines changed

5 files changed

+92
-43
lines changed

frontend/server/workflow-helper.test.ts

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,40 +39,49 @@ describe('workflow-helper', () => {
3939
describe('composePodLogsStreamHandler', () => {
4040
it('returns the stream from the default handler if there is no errors.', async () => {
4141
const defaultStream = new PassThrough();
42-
const defaultHandler = jest.fn((_podName: string, _namespace?: string) =>
42+
const defaultHandler = jest.fn((_podName: string, _createdAt: string, _namespace?: string) =>
4343
Promise.resolve(defaultStream),
4444
);
45-
const stream = await composePodLogsStreamHandler(defaultHandler)('podName', 'namespace');
46-
expect(defaultHandler).toBeCalledWith('podName', 'namespace');
45+
const stream = await composePodLogsStreamHandler(defaultHandler)(
46+
'podName',
47+
'2024-08-13',
48+
'namespace',
49+
);
50+
expect(defaultHandler).toBeCalledWith('podName', '2024-08-13', 'namespace');
4751
expect(stream).toBe(defaultStream);
4852
});
4953

5054
it('returns the stream from the fallback handler if there is any error.', async () => {
5155
const fallbackStream = new PassThrough();
52-
const defaultHandler = jest.fn((_podName: string, _namespace?: string) =>
56+
const defaultHandler = jest.fn((_podName: string, _createdAt: string, _namespace?: string) =>
5357
Promise.reject('unknown error'),
5458
);
55-
const fallbackHandler = jest.fn((_podName: string, _namespace?: string) =>
59+
const fallbackHandler = jest.fn((_podName: string, _createdAt: string, _namespace?: string) =>
5660
Promise.resolve(fallbackStream),
5761
);
5862
const stream = await composePodLogsStreamHandler(defaultHandler, fallbackHandler)(
5963
'podName',
64+
'2024-08-13',
6065
'namespace',
6166
);
62-
expect(defaultHandler).toBeCalledWith('podName', 'namespace');
63-
expect(fallbackHandler).toBeCalledWith('podName', 'namespace');
67+
expect(defaultHandler).toBeCalledWith('podName', '2024-08-13', 'namespace');
68+
expect(fallbackHandler).toBeCalledWith('podName', '2024-08-13', 'namespace');
6469
expect(stream).toBe(fallbackStream);
6570
});
6671

6772
it('throws error if both handler and fallback fails.', async () => {
68-
const defaultHandler = jest.fn((_podName: string, _namespace?: string) =>
73+
const defaultHandler = jest.fn((_podName: string, _createdAt: string, _namespace?: string) =>
6974
Promise.reject('unknown error for default'),
7075
);
71-
const fallbackHandler = jest.fn((_podName: string, _namespace?: string) =>
76+
const fallbackHandler = jest.fn((_podName: string, _createdAt: string, _namespace?: string) =>
7277
Promise.reject('unknown error for fallback'),
7378
);
7479
await expect(
75-
composePodLogsStreamHandler(defaultHandler, fallbackHandler)('podName', 'namespace'),
80+
composePodLogsStreamHandler(defaultHandler, fallbackHandler)(
81+
'podName',
82+
'2024-08-13',
83+
'namespace',
84+
),
7685
).rejects.toEqual('unknown error for fallback');
7786
});
7887
});
@@ -82,7 +91,7 @@ describe('workflow-helper', () => {
8291
const mockedGetPodLogs: jest.Mock = getPodLogs as any;
8392
mockedGetPodLogs.mockResolvedValueOnce('pod logs');
8493

85-
const stream = await getPodLogsStreamFromK8s('podName', 'namespace');
94+
const stream = await getPodLogsStreamFromK8s('podName', '', 'namespace');
8695
expect(mockedGetPodLogs).toBeCalledWith('podName', 'namespace', 'main');
8796
expect(stream.read().toString()).toBe('pod logs');
8897
});
@@ -101,24 +110,34 @@ describe('workflow-helper', () => {
101110
client,
102111
key: 'folder/key',
103112
};
104-
const createRequest = jest.fn((_podName: string, _namespace?: string) =>
113+
const createRequest = jest.fn((_podName: string, _createdAt: string, _namespace?: string) =>
105114
Promise.resolve(configs),
106115
);
107-
const stream = await toGetPodLogsStream(createRequest)('podName', 'namespace');
116+
const stream = await toGetPodLogsStream(createRequest)('podName', '2024-08-13', 'namespace');
108117
expect(mockedClientGetObject).toBeCalledWith('bucket', 'folder/key');
109118
});
110119
});
111120

112121
describe('createPodLogsMinioRequestConfig', () => {
113122
it('returns a MinioRequestConfig factory with the provided minioClientOptions, bucket, and prefix.', async () => {
114123
const mockedClient: jest.Mock = MinioClient as any;
115-
const requestFunc = await createPodLogsMinioRequestConfig(minioConfig, 'bucket', 'prefix');
116-
const request = await requestFunc('workflow-name-abc', 'namespace');
124+
const requestFunc = await createPodLogsMinioRequestConfig(
125+
minioConfig,
126+
'bucket',
127+
'artifacts/{{workflow.name}}/{{workflow.creationTimestamp.Y}}/{{workflow.creationTimestamp.m}}/{{workflow.creationTimestamp.d}}/{{pod.name}}',
128+
);
129+
const request = await requestFunc(
130+
'workflow-name-system-container-impl-foo',
131+
'2024-08-13',
132+
'namespace',
133+
);
117134

118135
expect(mockedClient).toBeCalledWith(minioConfig);
119136
expect(request.client).toBeInstanceOf(MinioClient);
120137
expect(request.bucket).toBe('bucket');
121-
expect(request.key).toBe('prefix/workflow-name/workflow-name-abc/main.log');
138+
expect(request.key).toBe(
139+
'artifacts/workflow-name/2024/08/13/workflow-name-system-container-impl-foo/main.log',
140+
);
122141
});
123142
});
124143

@@ -174,7 +193,7 @@ describe('workflow-helper', () => {
174193
mockedClientGetObject.mockResolvedValueOnce(objStream);
175194
objStream.end('some fake logs.');
176195

177-
const stream = await getPodLogsStreamFromWorkflow('workflow-name-abc', "2024-07-09");
196+
const stream = await getPodLogsStreamFromWorkflow('workflow-name-abc', '2024-07-09');
178197

179198
expect(mockedGetArgoWorkflow).toBeCalledWith('workflow-name');
180199

frontend/server/workflow-helper.ts

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,11 @@ export const getPodLogsStreamFromWorkflow = toGetPodLogsStream(
113113
* on the provided pod name and namespace (optional).
114114
*/
115115
export function toGetPodLogsStream(
116-
getMinioRequestConfig: (podName: string, createdAt: string, namespace?: string) => Promise<MinioRequestConfig>,
116+
getMinioRequestConfig: (
117+
podName: string,
118+
createdAt: string,
119+
namespace?: string,
120+
) => Promise<MinioRequestConfig>,
117121
) {
118122
return async (podName: string, createdAt: string, namespace?: string) => {
119123
const request = await getMinioRequestConfig(podName, createdAt, namespace);
@@ -128,50 +132,54 @@ export function toGetPodLogsStream(
128132
* client).
129133
* @param minioOptions Minio options to create a minio client.
130134
* @param bucket bucket containing the pod logs artifacts.
131-
* @param prefix prefix for pod logs artifacts stored in the bucket.
135+
* @param keyFormat the keyFormat for pod logs artifacts stored in the bucket.
132136
*/
133137
export function createPodLogsMinioRequestConfig(
134138
minioOptions: MinioClientOptions,
135139
bucket: string,
136140
keyFormat: string,
137141
) {
138-
return async (podName: string, createdAt: string, namespace: string = ''): Promise<MinioRequestConfig> => {
142+
return async (
143+
podName: string,
144+
createdAt: string,
145+
namespace: string = '',
146+
): Promise<MinioRequestConfig> => {
139147
// create a new client each time to ensure session token has not expired
140148
const client = await createMinioClient(minioOptions, 's3');
141-
const createdAtArray = createdAt.split("-")
149+
const createdAtArray = createdAt.split('-');
142150

143151
let key: string = keyFormat
144-
.replace(/\s+/g, '') // Remove all whitespace.
145-
.replace("{{workflow.name}}", podName.replace(/-system-container-impl-.*/, ''))
146-
.replace("{{workflow.creationTimestamp.Y}}", createdAtArray[0])
147-
.replace("{{workflow.creationTimestamp.m}}", createdAtArray[1])
148-
.replace("{{workflow.creationTimestamp.d}}", createdAtArray[2])
149-
.replace("{{pod.name}}", podName)
150-
.replace("{{workflow.namespace}}", namespace)
152+
.replace(/\s+/g, '') // Remove all whitespace.
153+
.replace('{{workflow.name}}', podName.replace(/-system-container-impl-.*/, ''))
154+
.replace('{{workflow.creationTimestamp.Y}}', createdAtArray[0])
155+
.replace('{{workflow.creationTimestamp.m}}', createdAtArray[1])
156+
.replace('{{workflow.creationTimestamp.d}}', createdAtArray[2])
157+
.replace('{{pod.name}}', podName)
158+
.replace('{{workflow.namespace}}', namespace);
151159

152-
if (!key.endsWith("/")) {
153-
key = key + "/"
160+
if (!key.endsWith('/')) {
161+
key = key + '/';
154162
}
155-
key = key + "main.log"
163+
key = key + 'main.log';
156164

157-
console.log("keyFormat: ", keyFormat)
158-
console.log("key: ", key)
165+
console.log('keyFormat: ', keyFormat);
166+
console.log('key: ', key);
159167

160168
// If there are unresolved template tags in the keyFormat, throw an error
161169
// that surfaces in the frontend's console log.
162-
if (key.includes("{") || key.includes("}")) {
170+
if (key.includes('{') || key.includes('}')) {
163171
throw new Error(
164172
`keyFormat, which is defined in config.ts or through the ARGO_KEYFORMAT env var, appears to include template tags that are not supported. ` +
165-
`The resulting log key, ${key}, includes unresolved template tags and is therefore invalid.`
166-
)
173+
`The resulting log key, ${key}, includes unresolved template tags and is therefore invalid.`,
174+
);
167175
}
168176

169177
const regex = /^[a-zA-Z0-9\-._/]+$/; // Allow letters, numbers, -, ., _, /
170178
if (!regex.test(key)) {
171179
throw new Error(
172180
`The log key, ${key}, which is derived from keyFormat in config.ts or through the ARGO_KEYFORMAT env var, is an invalid path. ` +
173-
`Supported characters include: letters, numbers, -, ., _, and /.`
174-
)
181+
`Supported characters include: letters, numbers, -, ., _, and /.`,
182+
);
175183
}
176184

177185
return { bucket, client, key };

frontend/src/components/tabs/RuntimeNodeDetailsV2.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ async function getLogsInfo(execution: Execution, runId?: string): Promise<Map<st
306306
let logsBannerMessage = '';
307307
let logsBannerAdditionalInfo = '';
308308
const customPropertiesMap = execution.getCustomPropertiesMap();
309-
const createdAt = new Date(execution.getCreateTimeSinceEpoch()).toISOString().split('T')[0]
309+
const createdAt = new Date(execution.getCreateTimeSinceEpoch()).toISOString().split('T')[0];
310310

311311
if (execution) {
312312
podName = customPropertiesMap.get(KfpExecutionProperties.POD_NAME)?.getStringValue() || '';

frontend/src/lib/Apis.test.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ describe('Apis', () => {
6060

6161
it('getPodLogs', async () => {
6262
const spy = fetchSpy('http://some/address');
63-
expect(await Apis.getPodLogs('a-run-id', 'some-pod-name', 'ns')).toEqual('http://some/address');
63+
expect(await Apis.getPodLogs('a-run-id', 'some-pod-name', 'ns', '')).toEqual(
64+
'http://some/address',
65+
);
6466
expect(spy).toHaveBeenCalledWith(
6567
'k8s/pod/logs?podname=some-pod-name&runid=a-run-id&podnamespace=ns',
6668
{
@@ -71,7 +73,7 @@ describe('Apis', () => {
7173

7274
it('getPodLogs in a specific namespace', async () => {
7375
const spy = fetchSpy('http://some/address');
74-
expect(await Apis.getPodLogs('a-run-id', 'some-pod-name', 'some-namespace-name')).toEqual(
76+
expect(await Apis.getPodLogs('a-run-id', 'some-pod-name', 'some-namespace-name', '')).toEqual(
7577
'http://some/address',
7678
);
7779
expect(spy).toHaveBeenCalledWith(
@@ -82,6 +84,19 @@ describe('Apis', () => {
8284
);
8385
});
8486

87+
it('getPodLogs with createdat specified', async () => {
88+
const spy = fetchSpy('http://some/address');
89+
expect(await Apis.getPodLogs('a-run-id', 'some-pod-name', 'ns', '2024-08-13')).toEqual(
90+
'http://some/address',
91+
);
92+
expect(spy).toHaveBeenCalledWith(
93+
'k8s/pod/logs?podname=some-pod-name&runid=a-run-id&podnamespace=ns&createdat=2024-08-13',
94+
{
95+
credentials: 'same-origin',
96+
},
97+
);
98+
});
99+
85100
it('getPodLogs error', async () => {
86101
jest.spyOn(console, 'error').mockImplementation(() => null);
87102
window.fetch = jest.fn(() =>

frontend/src/lib/Apis.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,21 @@ export class Apis {
108108
/**
109109
* Get pod logs
110110
*/
111-
public static getPodLogs(runId: string, podName: string, podNamespace: string, createdAt: string): Promise<string> {
111+
public static getPodLogs(
112+
runId: string,
113+
podName: string,
114+
podNamespace: string,
115+
createdAt: string,
116+
): Promise<string> {
112117
let query = `k8s/pod/logs?podname=${encodeURIComponent(podName)}&runid=${encodeURIComponent(
113118
runId,
114119
)}`;
115120
if (podNamespace) {
116121
query += `&podnamespace=${encodeURIComponent(podNamespace)}`;
117122
}
118-
query += `&createdat=${encodeURIComponent(createdAt)}`;
123+
if (createdAt) {
124+
query += `&createdat=${encodeURIComponent(createdAt)}`;
125+
}
119126
return this._fetch(query);
120127
}
121128

0 commit comments

Comments
 (0)