HTTP Batch requests with user defined tags not appearing in summary output

Hi,

K6 version: k6 v0.56.0 (commit/cc8511256d, go1.23.6, linux/amd64)

I have the following script where i want to display summary rows per scenario in the test

import { check, sleep } from 'k6'
import { Counter } from 'k6/metrics'
import encoding from 'k6/encoding'
import { uuidv4 } from 'https://jslib.k6.io/k6-utils/1.4.0/index.js'
import { SharedArray } from 'k6/data'
import { scenario } from 'k6/execution'
import { importPrivateKey, getHeaders } from './script_signing_copy_helpers.js'

// Load configuration from config.json
const config = JSON.parse(open('/config/config.json', 'r'))
if (!config) {
  throw new Error(`Failed to open file: config file`)
}

const baseUrl = config.endpoint
const subPath = config.subPathProcess

const cert = open('/certs/cert.pem') // Read certificate file
const key = open('/certs/key.pem') // Read private key file

// Precompute static values to avoid redundant processing inside loops
const base64EncodedCert = encoding.b64encode(cert)
const privateKey = await importPrivateKey(key)

const data = new SharedArray('reqs', function () {
  const file = JSON.parse(open('/data/IFs/filelist.json', 'r'))

  const requests = []

  file.forEach(filePath => {
    requests.push(JSON.parse(open(`/data/IFs/${filePath}`))[0])
  })

  return requests
})

const errorCount = new Counter('errors')

// This is the test configuration
export let options = {
  scenarios: {
    'process-all-data-from-dip-tst1r': {
      executor: 'constant-vus',
      vus: 5, // Number of VUs to keep active
      duration: '1m' // Test duration
    },
    'process-all-data-from-dip-tst2r': {
      executor: 'constant-vus',
      vus: 5, // Number of VUs to keep active
      duration: '1m' // Test duration
    },
    'process-all-data-from-dip-tst3p': {
      executor: 'constant-vus',
      vus: 5, // Number of VUs to keep active
      duration: '1m' // Test duration
    },
    'process-all-data-from-dip-tst4p': {
      executor: 'constant-vus',
      vus: 5, // Number of VUs to keep active
      duration: '1m' // Test duration
    },
    'process-all-data-from-dip-tst5p': {
      executor: 'constant-vus',
      vus: 5, // Number of VUs to keep active
      duration: '1m' // Test duration
    },
    'process-all-data-from-dip-tst6p': {
      executor: 'constant-vus',
      vus: 5, // Number of VUs to keep active
      duration: '1m' // Test duration
    }
  },
  thresholds: {
    // Define threshold for response time
    http_req_duration: ['p(95)<3000'] // 95% of requests must complete in under 10 seconds
  },
  // vus: 1, // 100 Virtual Users
  // iterations: 1, // 1000 iterations in total
  tlsAuth: [
    {
      cert: cert,
      key: key
    }
  ],
  cloud: {
    distribution: {
      distributionLabel1: { loadZone: 'amazon:gb:london', percent: 100 }
    }
  }
}

// Main test function
export default async function () {
  const scenarioMapping = {
    'process-all-data-from-dip-tst1r': 'TST1R',
    'process-all-data-from-dip-tst2r': 'TST2R',
    'process-all-data-from-dip-tst3p': 'TST3P',
    'process-all-data-from-dip-tst4p': 'TST4P',
    'process-all-data-from-dip-tst5p': 'TST5P',
    'process-all-data-from-dip-tst6p': 'TST6P',
    'process-all-data-from-dip-etclr': 'ETCLR',
    'process-all-data-from-dip-ecoes': 'ECOES'
  }

  const currentScenario = scenario.name
  const mpidRole = scenarioMapping[currentScenario]

  if (!mpidRole) {
    console.error(`Unknown scenario: ${currentScenario}`)
    return
  }

  const matchingObject = config.mpidDetails.find(item => item.name === mpidRole)
  if (!matchingObject) {
    console.error(`No matching config found for MPID: ${mpidRole}`)
    return
  }

  const { includedIfTypes, updateMPAN, mpanPrefix, defaultMPAN } =
    matchingObject
  if (!includedIfTypes || includedIfTypes.length === 0) {
    console.error(`No interface types configured for MPID: ${mpidRole}`)
    return
  }

  for (const interfaceType of includedIfTypes) {
    const url = `${baseUrl}${subPath}${mpidRole.toLowerCase()}/${interfaceType}`
    const batchRequests = []

    for (const originalMessage of data) {
      let message = JSON.parse(JSON.stringify(originalMessage))

      if (interfaceType === message.payload.CommonBlock.S0.interfaceID) {
        const randomUUID = uuidv4()

        if (updateMPAN) {
          const mpan = defaultMPAN
          try {
            message.payload.CommonBlock.M0.MPANCore = `${mpan}`
          } catch {}
          try {
            message.payload.CommonBlock.M1.principalMPAN = `${mpan}`
          } catch {}
        }

        message.payload.CommonBlock.S1.senderUniqueReference = `S-${randomUUID}`
        message.payload.CommonBlock.D0.transactionID = `T-${randomUUID}`

        const messageBody = JSON.stringify([message])

        let params = {
          headers: await getHeaders(
            messageBody,
            url,
            privateKey,
            base64EncodedCert
          ),
          tags: {
            group: currentScenario
          }
        }

        // console.log(params)
        // let headers = await getHeaders(
        //   messageBody,
        //   url,
        //   privateKey,
        //   base64EncodedCert
        // )

        // Collect requests for batch execution
        batchRequests.push({
          method: 'POST',
          url: url,
          body: messageBody,
          params: params
        })
      }
    }

    if (batchRequests.length > 0) {
      // console.log(batchRequests[0].params)

      // Execute all collected requests in parallel using http.batch()
      const responses = http.batch(batchRequests)

      // Process responses
      responses.forEach((res, index) => {
        let responseBody
        try {
          responseBody =
            res.body.trim() !== '' ? res.json() : 'Empty response body'
        } catch (e) {
          responseBody = res.body
        }

        const successStatusCode = check(res, {
          'status is 201': r => r.status === 201
        })

        if (!successStatusCode) {
          console.log(`Failed Response [${index}]: ${res.body}`)
          errorCount.add(1)
        }
      })
    }
  }
}

I expected to see some info of each scenario in the terminal summary. I think it might be the way that i am creating my batch request array but i am not sure.

The terminal output i get is

If i uncomment out the console.log(batchRequests[0].params) line this is the output and it shows that params includes headers AND tags as expected

Thanks in advance

Hi @chrisellison :wave:

You’re in luck. Although this was not possible so far, the upcoming version of k6 will most likely allow it, by default.

If you are in more of a hurry though, I recommend to look into custom summaries. This will be a bit more involved than “just” waiting, but you should be able to reach a somewhat similar end state this way too. Another pointer that might prove useful is the k6-jslib-summary project.

I hope this is helpful, and let me know if I can assist you any further :bowing_man:

1 Like

Thanks @oleiade

Is there an ETA for this new version? It isn’t a show stopper currently but in March it will be for us. I will have a go at your suggestions and report back. Once i have proven that i can display what i need i will mark your answer as the solution.

Thanks again

I can see the tags if i output the raw data. I’m just going to wait until the functionality is available.

thanks for your help

@chrisellison We are planning to have it (most likely) released in the upcoming version at the end of march.

Just to make sure there’s no misunderstandings, currently, the new end of test summary will display metrics specific to each scenarios (which are tags under the hood), which I assumed was what your question was about :bowing_man:

1 Like