blob: 8815511bdd35082487439cfb11b47f91fa56b4ad [file] [log] [blame]
danielhanrahan4e9f68c2024-06-28 13:43:35 +01001/*
2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2024 Nordix Foundation
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
19 */
20
21import { check } from 'k6';
danielhanrahan78894db2024-08-16 13:56:14 +010022import { Trend } from 'k6/metrics';
danielhanrahana1cdf732024-08-16 11:09:25 +010023import { Reader } from 'k6/x/kafka';
halil.cakal65b870b2024-07-25 11:12:29 +010024import {
25 TOTAL_CM_HANDLES, READ_DATA_FOR_CM_HANDLE_DELAY_MS, WRITE_DATA_FOR_CM_HANDLE_DELAY_MS,
halil.cakalfc499102024-08-28 10:19:26 +010026 makeCustomSummaryReport, makeBatchOfCmHandleIds, LEGACY_BATCH_THROUGHPUT_TEST_BATCH_SIZE,
27 LEGACY_BATCH_TOPIC_NAME, KAFKA_BOOTSTRAP_SERVERS, REGISTRATION_BATCH_SIZE,
28 LEGACY_BATCH_THROUGHPUT_TEST_NUMBER_OF_REQUESTS
halil.cakal65b870b2024-07-25 11:12:29 +010029} from './common/utils.js';
danielhanrahanf2e1e252024-08-25 21:35:19 +010030import { createCmHandles, deleteCmHandles, waitForAllCmHandlesToBeReady } from './common/cmhandle-crud.js';
danielhanrahan4e9f68c2024-06-28 13:43:35 +010031import { executeCmHandleSearch, executeCmHandleIdSearch } from './common/search-base.js';
halil.cakalfc499102024-08-28 10:19:26 +010032import { passthroughRead, passthroughWrite, legacyBatchRead } from './common/passthrough-crud.js';
danielhanrahan4e9f68c2024-06-28 13:43:35 +010033
danielhanrahan78894db2024-08-16 13:56:14 +010034let cmHandlesCreatedPerSecondTrend = new Trend('cmhandles_created_per_second', false);
35let cmHandlesDeletedPerSecondTrend = new Trend('cmhandles_deleted_per_second', false);
danielhanrahana1cdf732024-08-16 11:09:25 +010036let passthroughReadNcmpOverheadTrend = new Trend('ncmp_overhead_passthrough_read', true);
37let passthroughReadNcmpOverheadTrendWithAlternateId = new Trend('ncmp_overhead_passthrough_read_alt_id', true);
38let passthroughWriteNcmpOverheadTrend = new Trend('ncmp_overhead_passthrough_write', true);
danielhanrahanf2e1e252024-08-25 21:35:19 +010039let passthroughWriteNcmpOverheadTrendWithAlternateId = new Trend('ncmp_overhead_passthrough_write_alt_id', true);
danielhanrahana9de1662024-09-21 16:47:43 +010040let idSearchNoFilterDurationTrend = new Trend('id_search_nofilter_duration', true);
41let idSearchModuleDurationTrend = new Trend('id_search_module_duration', true);
42let idSearchPropertyDurationTrend = new Trend('id_search_property_duration', true);
43let idSearchCpsPathDurationTrend = new Trend('id_search_cpspath_duration', true);
44let idSearchTrustLevelDurationTrend = new Trend('id_search_trustlevel_duration', true);
45let cmSearchNoFilterDurationTrend = new Trend('cm_search_nofilter_duration', true);
46let cmSearchModuleDurationTrend = new Trend('cm_search_module_duration', true);
47let cmSearchPropertyDurationTrend = new Trend('cm_search_property_duration', true);
48let cmSearchCpsPathDurationTrend = new Trend('cm_search_cpspath_duration', true);
49let cmSearchTrustLevelDurationTrend = new Trend('cm_search_trustlevel_duration', true);
halil.cakalfc499102024-08-28 10:19:26 +010050let legacyBatchReadCmHandlesPerSecondTrend = new Trend('legacy_batch_read_cmhandles_per_second', false);
halil.cakal65b870b2024-07-25 11:12:29 +010051
halil.cakalfc499102024-08-28 10:19:26 +010052const legacyBatchEventReader = new Reader({
halil.cakal65b870b2024-07-25 11:12:29 +010053 brokers: KAFKA_BOOTSTRAP_SERVERS,
halil.cakalfc499102024-08-28 10:19:26 +010054 topic: LEGACY_BATCH_TOPIC_NAME,
halil.cakal65b870b2024-07-25 11:12:29 +010055});
danielhanrahan4e9f68c2024-06-28 13:43:35 +010056
57const DURATION = '15m';
halil.cakal8ea90c92024-09-18 12:48:27 +010058const LEGACY_BATCH_THROUGHPUT_TEST_START_TIME = '15m30s';
danielhanrahan4e9f68c2024-06-28 13:43:35 +010059
60export const options = {
danielhanrahan4dac1302024-08-30 15:03:06 +010061 setupTimeout: '8m',
danielhanrahan4e9f68c2024-06-28 13:43:35 +010062 teardownTimeout: '6m',
63 scenarios: {
halil.cakalfc499102024-08-28 10:19:26 +010064 passthrough_read_scenario: {
danielhanrahan4e9f68c2024-06-28 13:43:35 +010065 executor: 'constant-vus',
halil.cakalfc499102024-08-28 10:19:26 +010066 exec: 'passthroughReadScenario',
danielhanrahana9de1662024-09-21 16:47:43 +010067 vus: 2,
danielhanrahan4e9f68c2024-06-28 13:43:35 +010068 duration: DURATION,
69 },
halil.cakalfc499102024-08-28 10:19:26 +010070 passthrough_read_alt_id_scenario: {
seanbeirne28559522024-07-19 16:55:06 +010071 executor: 'constant-vus',
halil.cakalfc499102024-08-28 10:19:26 +010072 exec: 'passthroughReadAltIdScenario',
danielhanrahana9de1662024-09-21 16:47:43 +010073 vus: 2,
seanbeirne28559522024-07-19 16:55:06 +010074 duration: DURATION,
75 },
halil.cakalfc499102024-08-28 10:19:26 +010076 passthrough_write_scenario: {
halil.cakalfcc81ee2024-07-11 14:54:57 +010077 executor: 'constant-vus',
halil.cakalfc499102024-08-28 10:19:26 +010078 exec: 'passthroughWriteScenario',
danielhanrahana9de1662024-09-21 16:47:43 +010079 vus: 2,
halil.cakalfcc81ee2024-07-11 14:54:57 +010080 duration: DURATION,
81 },
halil.cakalfc499102024-08-28 10:19:26 +010082 passthrough_write_alt_id_scenario: {
danielhanrahan4e9f68c2024-06-28 13:43:35 +010083 executor: 'constant-vus',
halil.cakalfc499102024-08-28 10:19:26 +010084 exec: 'passthroughWriteAltIdScenario',
danielhanrahana9de1662024-09-21 16:47:43 +010085 vus: 2,
danielhanrahan4e9f68c2024-06-28 13:43:35 +010086 duration: DURATION,
87 },
danielhanrahana9de1662024-09-21 16:47:43 +010088 cm_handle_id_search_nofilter_scenario: {
danielhanrahan4e9f68c2024-06-28 13:43:35 +010089 executor: 'constant-vus',
danielhanrahana9de1662024-09-21 16:47:43 +010090 exec: 'cmHandleIdSearchNoFilterScenario',
91 vus: 1,
danielhanrahanf2e1e252024-08-25 21:35:19 +010092 duration: DURATION,
93 },
danielhanrahana9de1662024-09-21 16:47:43 +010094 cm_handle_search_nofilter_scenario: {
danielhanrahanf2e1e252024-08-25 21:35:19 +010095 executor: 'constant-vus',
danielhanrahana9de1662024-09-21 16:47:43 +010096 exec: 'cmHandleSearchNoFilterScenario',
97 vus: 1,
98 duration: DURATION,
99 },
100 cm_handle_id_search_module_scenario: {
101 executor: 'constant-vus',
102 exec: 'cmHandleIdSearchModuleScenario',
103 vus: 1,
104 duration: DURATION,
105 },
106 cm_handle_search_module_scenario: {
107 executor: 'constant-vus',
108 exec: 'cmHandleSearchModuleScenario',
109 vus: 1,
110 duration: DURATION,
111 },
112 cm_handle_id_search_property_scenario: {
113 executor: 'constant-vus',
114 exec: 'cmHandleIdSearchPropertyScenario',
115 vus: 1,
116 duration: DURATION,
117 },
118 cm_handle_search_property_scenario: {
119 executor: 'constant-vus',
120 exec: 'cmHandleSearchPropertyScenario',
121 vus: 1,
122 duration: DURATION,
123 },
124 cm_handle_id_search_cpspath_scenario: {
125 executor: 'constant-vus',
126 exec: 'cmHandleIdSearchCpsPathScenario',
127 vus: 1,
128 duration: DURATION,
129 },
130 cm_handle_search_cpspath_scenario: {
131 executor: 'constant-vus',
132 exec: 'cmHandleSearchCpsPathScenario',
133 vus: 1,
134 duration: DURATION,
135 },
136 cm_handle_id_search_trustlevel_scenario: {
137 executor: 'constant-vus',
138 exec: 'cmHandleIdSearchTrustLevelScenario',
139 vus: 1,
140 duration: DURATION,
141 },
142 cm_handle_search_trustlevel_scenario: {
143 executor: 'constant-vus',
144 exec: 'cmHandleSearchTrustLevelScenario',
145 vus: 1,
danielhanrahan4e9f68c2024-06-28 13:43:35 +0100146 duration: DURATION,
147 },
halil.cakalfc499102024-08-28 10:19:26 +0100148 legacy_batch_produce_scenario: {
149 executor: 'shared-iterations',
150 exec: 'legacyBatchProduceScenario',
151 vus: 2,
152 iterations: LEGACY_BATCH_THROUGHPUT_TEST_NUMBER_OF_REQUESTS,
halil.cakal8ea90c92024-09-18 12:48:27 +0100153 startTime: LEGACY_BATCH_THROUGHPUT_TEST_START_TIME,
halil.cakal65b870b2024-07-25 11:12:29 +0100154 },
halil.cakalfc499102024-08-28 10:19:26 +0100155 legacy_batch_consume_scenario: {
156 executor: 'per-vu-iterations',
157 exec: 'legacyBatchConsumeScenario',
158 vus: 1,
159 iterations: 1,
halil.cakal8ea90c92024-09-18 12:48:27 +0100160 startTime: LEGACY_BATCH_THROUGHPUT_TEST_START_TIME,
halil.cakal65b870b2024-07-25 11:12:29 +0100161 }
danielhanrahan4e9f68c2024-06-28 13:43:35 +0100162 },
163 thresholds: {
danielhanrahan4cb62392024-08-27 17:57:26 +0100164 'http_req_failed': ['rate == 0'],
danielhanrahan78894db2024-08-16 13:56:14 +0100165 'cmhandles_created_per_second': ['avg >= 22'],
166 'cmhandles_deleted_per_second': ['avg >= 22'],
danielhanrahanf2e1e252024-08-25 21:35:19 +0100167 'ncmp_overhead_passthrough_read': ['avg <= 40'],
168 'ncmp_overhead_passthrough_write': ['avg <= 40'],
169 'ncmp_overhead_passthrough_read_alt_id': ['avg <= 40'],
170 'ncmp_overhead_passthrough_write_alt_id': ['avg <= 40'],
danielhanrahana9de1662024-09-21 16:47:43 +0100171 'id_search_nofilter_duration': ['avg <= 2000'],
172 'id_search_module_duration': ['avg <= 2000'],
173 'id_search_property_duration': ['avg <= 2000'],
174 'id_search_cpspath_duration': ['avg <= 2000'],
175 'id_search_trustlevel_duration': ['avg <= 2000'],
176 'cm_search_nofilter_duration': ['avg <= 15000'],
177 'cm_search_module_duration': ['avg <= 15000'],
178 'cm_search_property_duration': ['avg <= 15000'],
179 'cm_search_cpspath_duration': ['avg <= 15000'],
180 'cm_search_trustlevel_duration': ['avg <= 15000'],
halil.cakalfc499102024-08-28 10:19:26 +0100181 'legacy_batch_read_cmhandles_per_second': ['avg >= 150'],
danielhanrahan4e9f68c2024-06-28 13:43:35 +0100182 },
183};
184
185export function setup() {
halil.cakal1745d202024-08-06 14:02:49 +0100186 const startTimeInMillis = Date.now();
187
188 const TOTAL_BATCHES = Math.ceil(TOTAL_CM_HANDLES / REGISTRATION_BATCH_SIZE);
189 for (let batchNumber = 0; batchNumber < TOTAL_BATCHES; batchNumber++) {
190 const nextBatchOfCmHandleIds = makeBatchOfCmHandleIds(REGISTRATION_BATCH_SIZE, batchNumber);
191 const response = createCmHandles(nextBatchOfCmHandleIds);
192 check(response, { 'create CM-handles status equals 200': (r) => r.status === 200 });
193 }
194
195 waitForAllCmHandlesToBeReady();
196
197 const endTimeInMillis = Date.now();
198 const totalRegistrationTimeInSeconds = (endTimeInMillis - startTimeInMillis) / 1000.0;
199
danielhanrahan78894db2024-08-16 13:56:14 +0100200 cmHandlesCreatedPerSecondTrend.add(TOTAL_CM_HANDLES / totalRegistrationTimeInSeconds);
danielhanrahan4e9f68c2024-06-28 13:43:35 +0100201}
202
203export function teardown() {
halil.cakal1745d202024-08-06 14:02:49 +0100204 const startTimeInMillis = Date.now();
205
egernug2bdef262024-09-06 12:09:31 +0100206 let DEREGISTERED_CM_HANDLES = 0
halil.cakal1745d202024-08-06 14:02:49 +0100207 const TOTAL_BATCHES = Math.ceil(TOTAL_CM_HANDLES / REGISTRATION_BATCH_SIZE);
208 for (let batchNumber = 0; batchNumber < TOTAL_BATCHES; batchNumber++) {
209 const nextBatchOfCmHandleIds = makeBatchOfCmHandleIds(REGISTRATION_BATCH_SIZE, batchNumber);
210 const response = deleteCmHandles(nextBatchOfCmHandleIds);
egernug2bdef262024-09-06 12:09:31 +0100211 if (response.error_code === 0) {
212 DEREGISTERED_CM_HANDLES += REGISTRATION_BATCH_SIZE
213 }
halil.cakal1745d202024-08-06 14:02:49 +0100214 check(response, { 'delete CM-handles status equals 200': (r) => r.status === 200 });
215 }
216
217 const endTimeInMillis = Date.now();
218 const totalDeregistrationTimeInSeconds = (endTimeInMillis - startTimeInMillis) / 1000.0;
219
egernug2bdef262024-09-06 12:09:31 +0100220 cmHandlesDeletedPerSecondTrend.add(DEREGISTERED_CM_HANDLES / totalDeregistrationTimeInSeconds);
danielhanrahan4e9f68c2024-06-28 13:43:35 +0100221}
222
halil.cakalfc499102024-08-28 10:19:26 +0100223export function passthroughReadScenario() {
danielhanrahanf2e1e252024-08-25 21:35:19 +0100224 const response = passthroughRead(false);
danielhanrahan78894db2024-08-16 13:56:14 +0100225 if (check(response, { 'passthrough read status equals 200': (r) => r.status === 200 })) {
226 const overhead = response.timings.duration - READ_DATA_FOR_CM_HANDLE_DELAY_MS;
227 passthroughReadNcmpOverheadTrend.add(overhead);
228 }
danielhanrahan4e9f68c2024-06-28 13:43:35 +0100229}
230
halil.cakalfc499102024-08-28 10:19:26 +0100231export function passthroughReadAltIdScenario() {
danielhanrahanf2e1e252024-08-25 21:35:19 +0100232 const response = passthroughRead(true);
danielhanrahan78894db2024-08-16 13:56:14 +0100233 if (check(response, { 'passthrough read with alternate Id status equals 200': (r) => r.status === 200 })) {
234 const overhead = response.timings.duration - READ_DATA_FOR_CM_HANDLE_DELAY_MS;
235 passthroughReadNcmpOverheadTrendWithAlternateId.add(overhead);
236 }
seanbeirne28559522024-07-19 16:55:06 +0100237}
238
halil.cakalfc499102024-08-28 10:19:26 +0100239export function passthroughWriteScenario() {
danielhanrahanf2e1e252024-08-25 21:35:19 +0100240 const response = passthroughWrite(false);
danielhanrahan78894db2024-08-16 13:56:14 +0100241 if (check(response, { 'passthrough write status equals 201': (r) => r.status === 201 })) {
242 const overhead = response.timings.duration - WRITE_DATA_FOR_CM_HANDLE_DELAY_MS;
243 passthroughWriteNcmpOverheadTrend.add(overhead);
244 }
halil.cakalfcc81ee2024-07-11 14:54:57 +0100245}
246
halil.cakalfc499102024-08-28 10:19:26 +0100247export function passthroughWriteAltIdScenario() {
danielhanrahanf2e1e252024-08-25 21:35:19 +0100248 const response = passthroughWrite(true);
249 if (check(response, { 'passthrough write with alternate Id status equals 201': (r) => r.status === 201 })) {
250 const overhead = response.timings.duration - WRITE_DATA_FOR_CM_HANDLE_DELAY_MS;
251 passthroughWriteNcmpOverheadTrendWithAlternateId.add(overhead);
252 }
253}
254
danielhanrahana9de1662024-09-21 16:47:43 +0100255export function cmHandleIdSearchNoFilterScenario() {
256 const response = executeCmHandleIdSearch('no-filter');
257 if (check(response, { 'CM handle ID no-filter search status equals 200': (r) => r.status === 200 })
258 && check(response, { 'CM handle ID no-filter search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) {
259 idSearchNoFilterDurationTrend.add(response.timings.duration);
danielhanrahan78894db2024-08-16 13:56:14 +0100260 }
danielhanrahan4e9f68c2024-06-28 13:43:35 +0100261}
262
danielhanrahana9de1662024-09-21 16:47:43 +0100263export function cmHandleSearchNoFilterScenario() {
264 const response = executeCmHandleSearch('no-filter');
265 if (check(response, { 'CM handle no-filter search status equals 200': (r) => r.status === 200 })
266 && check(response, { 'CM handle no-filter search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) {
267 cmSearchNoFilterDurationTrend.add(response.timings.duration);
268 }
269}
270
271export function cmHandleIdSearchModuleScenario() {
272 const response = executeCmHandleIdSearch('module');
273 if (check(response, { 'CM handle ID module search status equals 200': (r) => r.status === 200 })
274 && check(response, { 'CM handle ID module search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) {
275 idSearchModuleDurationTrend.add(response.timings.duration);
276 }
277}
278
279export function cmHandleSearchModuleScenario() {
280 const response = executeCmHandleSearch('module');
281 if (check(response, { 'CM handle module search status equals 200': (r) => r.status === 200 })
282 && check(response, { 'CM handle module search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) {
283 cmSearchModuleDurationTrend.add(response.timings.duration);
284 }
285}
286
287export function cmHandleIdSearchPropertyScenario() {
288 const response = executeCmHandleIdSearch('property');
289 if (check(response, { 'CM handle ID property search status equals 200': (r) => r.status === 200 })
290 && check(response, { 'CM handle ID property search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) {
291 idSearchPropertyDurationTrend.add(response.timings.duration);
292 }
293}
294
295export function cmHandleSearchPropertyScenario() {
296 const response = executeCmHandleSearch('property');
297 if (check(response, { 'CM handle property search status equals 200': (r) => r.status === 200 })
298 && check(response, { 'CM handle property search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) {
299 cmSearchPropertyDurationTrend.add(response.timings.duration);
300 }
301}
302
303export function cmHandleIdSearchCpsPathScenario() {
304 const response = executeCmHandleIdSearch('cps-path-for-ready-cm-handles');
305 if (check(response, { 'CM handle ID cps path search status equals 200': (r) => r.status === 200 })
306 && check(response, { 'CM handle ID cps path search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) {
307 idSearchCpsPathDurationTrend.add(response.timings.duration);
308 }
309}
310
311export function cmHandleSearchCpsPathScenario() {
312 const response = executeCmHandleSearch('cps-path-for-ready-cm-handles');
313 if (check(response, { 'CM handle cps path search status equals 200': (r) => r.status === 200 })
314 && check(response, { 'CM handle cps path search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) {
315 cmSearchCpsPathDurationTrend.add(response.timings.duration);
316 }
317}
318
319export function cmHandleIdSearchTrustLevelScenario() {
320 const response = executeCmHandleIdSearch('trust-level');
321 if (check(response, { 'CM handle ID trust level search status equals 200': (r) => r.status === 200 })
322 && check(response, { 'CM handle ID trust level search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) {
323 idSearchTrustLevelDurationTrend.add(response.timings.duration);
324 }
325}
326
327export function cmHandleSearchTrustLevelScenario() {
328 const response = executeCmHandleSearch('trust-level');
329 if (check(response, { 'CM handle trust level search status equals 200': (r) => r.status === 200 })
330 && check(response, { 'CM handle trust level search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) {
331 cmSearchTrustLevelDurationTrend.add(response.timings.duration);
danielhanrahan78894db2024-08-16 13:56:14 +0100332 }
danielhanrahan4e9f68c2024-06-28 13:43:35 +0100333}
334
halil.cakalfc499102024-08-28 10:19:26 +0100335export function legacyBatchProduceScenario() {
336 const nextBatchOfCmHandleIds = makeBatchOfCmHandleIds(LEGACY_BATCH_THROUGHPUT_TEST_BATCH_SIZE, 0);
337 const response = legacyBatchRead(nextBatchOfCmHandleIds);
halil.cakal65b870b2024-07-25 11:12:29 +0100338 check(response, { 'data operation batch read status equals 200': (r) => r.status === 200 });
339}
340
halil.cakalfc499102024-08-28 10:19:26 +0100341export function legacyBatchConsumeScenario() {
342 const TOTAL_MESSAGES_TO_CONSUME = LEGACY_BATCH_THROUGHPUT_TEST_NUMBER_OF_REQUESTS * LEGACY_BATCH_THROUGHPUT_TEST_BATCH_SIZE;
halil.cakal65b870b2024-07-25 11:12:29 +0100343 try {
halil.cakalfc499102024-08-28 10:19:26 +0100344 let messagesConsumed = 0;
345 let startTime = Date.now();
346
347 while (messagesConsumed < TOTAL_MESSAGES_TO_CONSUME) {
348 let messages = legacyBatchEventReader.consume({ limit: 1000 });
349
350 if (messages.length > 0) {
351 messagesConsumed += messages.length;
352 }
353 }
354
355 let endTime = Date.now();
356 const timeToConsumeMessagesInSeconds = (endTime - startTime) / 1000.0;
357 legacyBatchReadCmHandlesPerSecondTrend.add(TOTAL_MESSAGES_TO_CONSUME / timeToConsumeMessagesInSeconds);
halil.cakal65b870b2024-07-25 11:12:29 +0100358 } catch (error) {
halil.cakalfc499102024-08-28 10:19:26 +0100359 legacyBatchReadCmHandlesPerSecondTrend.add(0);
halil.cakal65b870b2024-07-25 11:12:29 +0100360 console.error(error);
361 }
362}
363
danielhanrahan4e9f68c2024-06-28 13:43:35 +0100364export function handleSummary(data) {
365 return {
366 stdout: makeCustomSummaryReport(data, options),
367 };
368}