artilleryio / artillery

The complete load testing platform. Everything you need for production-grade load tests. Serverless & distributed. Load test with Playwright. Load test HTTP APIs, GraphQL, WebSocket, and more. Use any Node.js module.
https://www.artillery.io
Mozilla Public License 2.0
8k stars 510 forks source link

Key containing periods behave oddly #879

Open hueblerw opened 4 years ago

hueblerw commented 4 years ago

I am reporting this because the behavior seems a bit strange. When running artillery with json key that contains a dot the actually sent payload produces a duplicate.
This is the yaml file:

Screen Shot 2020-09-10 at 3 54 58 PM

And this is the produced payload using DEBUG=http artillery run myscript.yaml

Screen Shot 2020-09-10 at 3 50 54 PM

I was hoping to get the first entry. "foo.bar": [ "M", "L" ], NOT: "foo": { "bar": [ "M", "L" ] } Though, I can understand how that might be the expected behavior, but it seems really strange to have both? Is there anyway to get this to be the only entry in the payload? "foo.bar": [ "M", "L" ],

Thanks for your time!

kanso-git commented 5 months ago

I'm using version ^2.0.12 and I'm facing the same issue. I have looked through the documentation but couldn't find anything that could help.

drew-vault commented 3 months ago

Hi - we ran into this issue too and think we've found where it happens. I have a workaround but not a complete solution, so leaving findings here in case useful to any eventual fixer-upper:

The issue stems from this line here (commons/engine_util.js, 289) where the code sets the parsed value at the matching path in the result object. The code calls lodash's set method with the path, which will parse any dots as path delimiters and so nest the values as seen above:

  L.set(o, `${prefix}["${key}"]`, newValue);

Hacky workaround we've taken is to patch the package to treat the prefix as a string key, rather than a parseable path, like so:

 if (prefix.match(/[.]/) {
    // we want to preserve the key - nb this will break arrays
    L.set(o, `["${prefix}"]["${key}"]`, newValue);
} else {     
    L.set(o, `${prefix}["${key}"]`, newValue);
}

this works for our case (a prefix like foo.bar) but strictly speaking wouldn't handle other cases - foo[0] will always be treated as referring to an array index, and never as if it were a complex key (e.g. { ["foo[0]"]: "bar" }). An edge case, but would be better in any case to detect whether the prefix is intended as a reference to a location in the result object, or is intended simply as the name of a key.

vilkinsons commented 2 months ago

We're also new to Artillery (2.0.18) and running into this issue.

We're going to give the above patch a go this AM, but is there any prospect of a proper fix (be it the above patch or a more expansive case-covering alternative fix) being shipped as part of an official update? Thank you!

TimDiekmann commented 2 months ago

The solution posted by @drew-vault sadly didn't work for us as we have to deal with arrays as well. However, I assume that the behavior observed is not intended and a side-effect of deepForEach which was not expected. Instead, I have patched it for us to replace templateObjectOrArray with

// Mutates the object in place
function templateObject(o, context) {
  Object.entries(o).forEach(([key, value]) => {
    if (value && value.constructor !== Object && value.constructor !== Array) {
      o[key] = template(value, context, true);
    } else {
      templateObjectOrArray(value, context);
    }
  });
}

// Mutates the array in place
function templateArray(o, context) {
  o.forEach((value, index) => {
    if (value && value.constructor !== Object && value.constructor !== Array) {
      o[index] = template(value, context, true);
    } else {
      templateObjectOrArray(value, context);
    }
  });
}

// Mutates the object or array in place
function templateObjectOrArray(o, context) {
  if (o.constructor === Array) {
    templateArray(o, context);
  } else {
    templateObject(o, context);
  }
}

Happy to open a PR for that if desired!