Repository: mcollina/autocannon (js)

Issue:
Is there a way to generate a url for each request?
I'd like to benchmark `POST` requests where I could pass a random id at the end of path.
I tried using `setupClient` to change the url, but I'll only change the first request of each client.

Pull request description:
Allow for ID replacement in the URL
Resolves #512 

Patch:
diff --git a/lib/requestIterator.js b/lib/requestIterator.js
index 5c1d27e..a3f0a03 100644
--- a/lib/requestIterator.js
+++ b/lib/requestIterator.js
@@ -108,8 +108,12 @@ RequestIterator.prototype.rebuildRequest = function () {
     this.currentRequest.headers = this.currentRequest.headers || this.headers
     data = this.requestBuilder(this.currentRequest, this.context)
     if (data) {
+      const hyperid = this.hyperid()
       this.currentRequest.requestBuffer = this.reqDefaults.idReplacement
-        ? Buffer.from(data.toString().replace(/\[<id>\]/g, this.hyperid()))
+        ? Buffer.from(data.toString()
+          .replace(/\[<id>\]/g, hyperid)
+          // in the first line only (the url), replace encoded id placeholders
+          .replace(/^.+/, m => m.replace(/\[%3Cid%3E]/g, hyperid)))
         : data
     } else if (this.currentRequestIndex === 0) {
       // when first request fails to build, we can not reset pipeline, or it'll never end


Test patch:
diff --git a/test/requestIterator.test.js b/test/requestIterator.test.js
index 07f7a0e..3d25c15 100644
--- a/test/requestIterator.test.js
+++ b/test/requestIterator.test.js
@@ -184,9 +184,10 @@ test('request iterator should allow for rebuilding the current request', (t) =>
 })
 
 test('request iterator should not replace any [<id>] tags with generated IDs when calling move with idReplacement disabled', (t) => {
-  t.plan(2)
+  t.plan(3)
 
   const opts = server.address()
+  opts.path = '/[%3Cid%3E]'
   opts.method = 'POST'
   opts.body = '[<id>]'
   opts.requests = [{}]
@@ -194,6 +195,9 @@ test('request iterator should not replace any [<id>] tags with generated IDs whe
   const iterator = new RequestIterator(opts)
   const result = iterator.currentRequest.requestBuffer.toString().trim()
 
+  const path = result.split(' ')[1]
+  t.equal(path, '/[%3Cid%3E]', '[<id>] should be present in path')
+
   const contentLength = result.split('Content-Length: ')[1].slice(0, 1)
   t.equal(contentLength, '6', 'Content-Length was incorrect')
 
@@ -202,7 +206,7 @@ test('request iterator should not replace any [<id>] tags with generated IDs whe
 })
 
 test('request iterator should replace all [<id>] tags with generated IDs when calling move with idReplacement enabled', (t) => {
-  t.plan(6)
+  t.plan(10)
 
   function isUrlSafe (string) {
     return /^[A-Za-z0-9\-_]+$/.test(string)
@@ -210,23 +214,30 @@ test('request iterator should replace all [<id>] tags with generated IDs when ca
 
   const opts = server.address()
   opts.method = 'POST'
-  opts.body = '[<id>]'
+  opts.path = '/[%3Cid%3E]'
+  opts.body = '[<id>] [%3Cid%3E]'
   opts.requests = [{}]
   opts.idReplacement = true
 
   const iterator = new RequestIterator(opts)
   const first = iterator.currentRequest.requestBuffer.toString().trim()
-  const firstId = first.split('\n').pop().trim()
+  const firstPath = first.split(' ')[1]
+  const firstId = firstPath.slice(1)
 
-  t.equal(first.includes('[<id>]'), false, 'One or more [<id>] tags were not replaced')
+  t.equal(first.includes('[<id>]'), false, 'One or more [<id>] tags were not replaced in body')
+  t.ok(first.includes('[%3Cid%3E]'), 'Encoded [<id>] tags should not be replaced in body')
+  t.equal(firstPath.includes('[%3Cid%3E]'), false, 'One or more [<id>] tags were not replaced in path')
   t.ok(firstId.endsWith('0'), 'Generated ID should end with request number')
   t.ok(isUrlSafe(firstId), 'Generated ID should be URL-safe')
 
   iterator.nextRequest()
   const second = iterator.currentRequest.requestBuffer.toString().trim()
-  const secondId = second.split('\n').pop().trim()
+  const secondPath = second.split(' ')[1]
+  const secondId = secondPath.slice(1)
 
-  t.equal(second.includes('[<id>]'), false, 'One or more [<id>] tags were not replaced')
+  t.equal(second.includes('[<id>]'), false, 'One or more [<id>] tags were not replaced in body')
+  t.ok(second.includes('[%3Cid%3E]'), 'Encoded [<id>] tags should not be replaced in body')
+  t.equal(secondPath.includes('[%3Cid%3E]'), false, 'One or more [<id>] tags were not replaced in path')
   t.ok(secondId.endsWith('1'), 'Generated ID should end with a unique request number')
   t.ok(isUrlSafe(secondId), 'Generated ID should be URL-safe')
 })