makiwara 2.1.8 โ†’ 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![license](https://img.shields.io/npm/l/makiwara.svg)](https://piecioshka.mit-license.org)
8
8
  [![github-ci](https://github.com/piecioshka/makiwara/actions/workflows/testing.yml/badge.svg)](https://github.com/piecioshka/makiwara/actions/workflows/testing.yml)
9
9
 
10
- ๐Ÿ”จ Benchmark URL to gain HTTP requests limits
10
+ ๐Ÿ”จ CLI to benchmark URL to gain HTTP requests limits
11
11
 
12
12
  ## Install
13
13
 
@@ -18,9 +18,9 @@ npm install -g makiwara
18
18
  ## Usage
19
19
 
20
20
  ```javascript
21
- const { attack } = require('makiwara');
21
+ const { benchmark } = require('makiwara');
22
22
 
23
- attack('https://example.org', [1, 5, 10], 'sequence')
23
+ benchmark('https://example.org', [1, 5, 10], 'sequence')
24
24
  .then((result) => {
25
25
  console.log(result);
26
26
  })
@@ -41,7 +41,7 @@ Example:
41
41
 
42
42
  Options:
43
43
  -V, --version output the version number
44
- -u, --url <url> Define URL to attack. Ex. https://example.org/
44
+ -u, --url <url> Define URL to benchmark. Ex. https://example.org/
45
45
  -t, --timelimit [numbers] Define list of time thresholds (in seconds). Ex. 10,100,1000
46
46
  -s, --strategy <concurrent|sequence> Define strategy for making requests
47
47
  -h, --help output usage information
@@ -88,18 +88,6 @@ Result:
88
88
  โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•งโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
89
89
  ```
90
90
 
91
- ## Unit tests
92
-
93
- ```bash
94
- npm test
95
- ```
96
-
97
- ## Code coverage
98
-
99
- ```bash
100
- npm run coverage
101
- ```
102
-
103
91
  ## License
104
92
 
105
- [The MIT License](https://piecioshka.mit-license.org) @ 2017-2019
93
+ [The MIT License](https://piecioshka.mit-license.org) @ 2017-2024
package/bin/cli.js CHANGED
@@ -1,98 +1,112 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const fs = require('fs');
4
- const path = require('path');
5
-
6
- const http = require('http');
7
- const https = require('https');
3
+ const http = require("http");
4
+ const https = require("https");
8
5
  http.globalAgent.maxSockets = https.globalAgent.maxSockets = 512;
9
6
  // http.globalAgent.maxFreeSockets = https.globalAgent.maxFreeSockets = 512;
10
7
 
11
- const program = require('commander');
12
- const ora = require('ora');
13
- const isUrl = require('is-url');
14
- const bold = require('ansi-bold');
8
+ const { Command } = require("commander");
9
+ const program = new Command();
10
+
11
+ const ora = require("ora");
12
+ const isUrl = require("is-url");
13
+ const bold = require("ansi-bold");
15
14
 
16
- const { attack } = require('../index');
17
- const HTTP_STATUS = require('../src/http-status-codes');
18
- const { displaySummary, displayError } = require('../src/display');
19
- const { makeRequest } = require('../src/make-requests');
15
+ const logger = require("../src/color-logs");
16
+ const { benchmark } = require("../index");
17
+ const HTTP_STATUS = require("../src/http-status-codes");
18
+ const { displaySummary } = require("../src/display");
19
+ const { makeRequest } = require("../src/make-requests");
20
20
 
21
- // eslint-disable-next-line no-sync
22
- const pkg = JSON.parse(fs.readFileSync(
23
- path.join(__dirname, '..', 'package.json')
24
- ).toString());
21
+ const pkg = require("../package.json");
25
22
  const STRATEGY_REGEXP = /^(concurrent|sequence)$/;
26
23
 
27
24
  program
28
25
  .version(pkg.version)
29
- .option('-u, --url <url>', 'Define URL to attack. Ex. https://example.org/')
30
- .option('-t, --timelimit [numbers]', 'Define list of time thresholds (in seconds). Ex. 10,100,1000')
31
- .option('-s, --strategy <concurrent|sequence>', 'Define strategy for making requests')
32
- .description('Example:\n\tmakiwara -u https://localhost:3000 -t 10 -s sequence')
26
+ .option(
27
+ "-u, --url <url>",
28
+ "Define URL to benchmark. Ex. https://example.org/"
29
+ )
30
+ .option(
31
+ "-t, --timelimit [numbers]",
32
+ "Define list of time thresholds (in seconds). Ex. 10,100,1000"
33
+ )
34
+ .option(
35
+ "-s, --strategy <concurrent|sequence>",
36
+ "Define strategy for making requests"
37
+ )
38
+ .description(
39
+ "Example:\n\tmakiwara -u https://localhost:3000 -t 10 -s sequence"
40
+ )
33
41
  .parse(process.argv);
34
42
 
35
- if (typeof program.url !== 'string') {
36
- console.red('Error: url is not a string');
43
+ const options = program.opts();
44
+
45
+ if (typeof options.url !== "string") {
46
+ displayHeader();
47
+ logger.red("Error: url is not a string\n");
37
48
  program.help();
38
49
  }
39
50
 
40
- if (!isUrl(program.url)) {
41
- console.red('Error: url is not correct format');
51
+ if (!isUrl(options.url)) {
52
+ displayHeader();
53
+ logger.red("Error: url is not correct format\n");
42
54
  program.help();
43
55
  }
44
56
 
45
- if (!program.timelimit) {
46
- program.timelimit = '1,3,5';
47
- console.yellow('Ups... you did not put "timelimit" of thresholds');
48
- console.yellow(`Thresholds are sets to: ${bold(program.timelimit)} (seconds)\n`);
57
+ if (!options.timelimit) {
58
+ options.timelimit = "1,3,5";
59
+ logger.yellow('Ups... you did not define "timelimit" of thresholds');
60
+ logger.yellow(
61
+ `Thresholds are sets to: ${bold(options.timelimit)} (seconds)\n`
62
+ );
49
63
  }
50
64
 
51
- if (!(STRATEGY_REGEXP).test(program.strategy)) {
52
- program.strategy = 'concurrent';
53
- console.yellow('Ups... you did not put "strategy"');
54
- console.yellow(`Default strategy is: ${program.strategy}\n`);
65
+ if (!STRATEGY_REGEXP.test(options.strategy)) {
66
+ options.strategy = "concurrent";
67
+ logger.yellow('Ups... you did not define "strategy"');
68
+ logger.yellow(`Default strategy is: ${options.strategy}\n`);
55
69
  }
56
70
 
57
- const url = program.url;
58
- const timeLimit = program.timelimit.split(',').map(Number);
59
- const strategy = program.strategy;
71
+ const url = options.url;
72
+ const timeLimit = options.timelimit.split(",").map(Number);
73
+ const strategy = options.strategy;
60
74
  let spinner = null;
61
75
 
62
76
  function displayDelimiter() {
63
- console.gray('----------------------------------------------------\n');
77
+ logger.gray("----------------------------------------------------\n");
64
78
  }
65
79
 
66
80
  function displayHeader() {
67
- console.log(`${pkg.name}, Version ${pkg.version}`);
68
- const currentYear = new Date().getFullYear();
69
- console.log(`Copyright 2017-${currentYear} ${pkg.author.name} <${pkg.author.email}> ${pkg.author.url}`);
70
- console.log(`The ${pkg.license} License, https://piecioshka.mit-license.org/\n`);
71
- console.log(`> ${pkg.description}\n`);
81
+ const author = `${pkg.author.name} <${pkg.author.email}> ${pkg.author.url}`;
82
+ console.log(`${pkg.name} v${pkg.version}`);
83
+ console.log(`Copyright (c) ${new Date().getFullYear()} ${author}\n`);
72
84
  }
73
85
 
74
86
  async function sendTestRequest(testUrl) {
75
87
  spinner.succeed(`Start testing... ${bold(testUrl)}`);
76
88
  const response = await makeRequest(testUrl, { agent: false });
77
89
  if (response.status !== HTTP_STATUS.OK) {
78
- console.red(`HTTP Status Code: ${bold(response.status)}`);
79
- console.yellow(`Response Body: ${bold(response.text)}`);
90
+ logger.red(`HTTP Status Code: ${bold(response.status)}`);
91
+ logger.yellow(`Response Body: ${bold(response.text)}`);
80
92
  }
81
- spinner.succeed(`Testing completed (response: ${bold(response.text.length)} Bytes)`);
93
+ spinner.succeed(
94
+ `Testing completed (response: ${bold(response.text.length)} Bytes)`
95
+ );
82
96
  }
83
97
 
84
98
  async function main() {
85
99
  displayHeader();
86
100
 
87
- spinner = ora('Loading').start();
101
+ spinner = ora("Loading").start();
88
102
 
89
103
  try {
90
104
  await sendTestRequest(url);
91
105
 
92
- spinner.succeed('Start attacking...');
93
- const results = await attack(url, timeLimit, strategy);
106
+ spinner.succeed("Start benchmarking...");
107
+ const results = await benchmark(url, timeLimit, strategy);
94
108
  spinner.stop();
95
- spinner.succeed('Attacking completed\n');
109
+ spinner.succeed("Benchmarking completed\n");
96
110
 
97
111
  results.forEach((result, index) => {
98
112
  displaySummary(result);
@@ -103,7 +117,7 @@ async function main() {
103
117
  });
104
118
  } catch (err) {
105
119
  spinner.stop();
106
- displayError(err);
120
+ logger.red(err);
107
121
  }
108
122
 
109
123
  // eslint-disable-next-line no-process-exit
package/index.js CHANGED
@@ -1,22 +1 @@
1
- const { makeRequestsInConcurrentMode, makeRequestsInSequenceMode } = require('./src/make-requests');
2
-
3
- require('./src/color-logs');
4
-
5
- const strategies = new Map();
6
- strategies.set('sequence', makeRequestsInSequenceMode);
7
- strategies.set('concurrent', makeRequestsInConcurrentMode);
8
-
9
- function attack(url, timeLimits, strategy) {
10
- const method = strategies.get(strategy);
11
-
12
- return Promise.all(
13
- timeLimits
14
- // Remove zeros timeLimits
15
- .filter((k) => k)
16
- .map((duration) => method(url, duration))
17
- );
18
- }
19
-
20
- module.exports = {
21
- attack
22
- };
1
+ module.exports = require("./src/benchmark");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "makiwara",
3
- "description": "๐Ÿ”จ Benchmark URL to gain HTTP requests limits",
4
- "version": "2.1.8",
3
+ "description": "๐Ÿ”จ CLI to benchmark URL to gain HTTP requests limits",
4
+ "version": "2.2.0",
5
5
  "license": "MIT",
6
6
  "author": {
7
7
  "name": "Piotr Kowalski",
@@ -11,35 +11,34 @@
11
11
  "scripts": {
12
12
  "clear": "rm -rf dist/ coverage/ .nyc_output/",
13
13
  "clear:all": "rm -rf node_modules/ && npm run clear",
14
- "test": "jasmine test/specs/*.js",
15
- "coverage": "nyc npm run test && nyc report --reporter=html",
14
+ "test": "jest --watchAll=false",
15
+ "test:watch": "jest",
16
+ "coverage": "jest --watchAll=false --coverage",
16
17
  "lint": "eslint src/",
17
18
  "snyk-protect": "snyk protect",
18
19
  "prepare": "npm run snyk-protect"
19
20
  },
20
21
  "dependencies": {
21
- "@types/node": "^22.7.5",
22
+ "@types/jest": "^29.5.14",
22
23
  "ansi-bold": "^0.1.1",
23
- "ansi-cyan": "^0.1.1",
24
24
  "ansi-gray": "^0.1.1",
25
25
  "ansi-red": "^0.1.1",
26
26
  "ansi-yellow": "^0.1.1",
27
27
  "axios": "^1.7.7",
28
- "commander": "^12.1.0",
29
28
  "http-status-codes": "^2.3.0",
30
29
  "is-url": "^1.2.4",
30
+ "jest": "^29.7.0",
31
31
  "node-fetch": "^3.3.2",
32
- "ora": "^8.1.0",
33
32
  "snyk": "^1.1293.1",
34
33
  "table": "^6.8.2"
35
34
  },
36
35
  "devDependencies": {
37
- "@types/jasmine": "^5.1.4",
36
+ "@types/node": "^22.7.5",
37
+ "commander": "^12.1.0",
38
38
  "eslint": "^8.6.0",
39
39
  "eslint-config-piecioshka": "^2.3.1",
40
- "jasmine": "^5.4.0",
41
40
  "nock": "^13.5.5",
42
- "nyc": "^15.1.0"
41
+ "ora": "^5.4.1"
43
42
  },
44
43
  "repository": {
45
44
  "type": "git",
@@ -48,6 +47,9 @@
48
47
  "bugs": {
49
48
  "url": "https://github.com/piecioshka/makiwara/issues"
50
49
  },
50
+ "engines": {
51
+ "node": ">=14"
52
+ },
51
53
  "files": [
52
54
  "bin",
53
55
  "src",
@@ -56,9 +58,8 @@
56
58
  "README.md"
57
59
  ],
58
60
  "keywords": [
59
- "app",
60
- "test",
61
- "attack",
61
+ "cli",
62
+ "benchmark",
62
63
  "multi",
63
64
  "statistics",
64
65
  "summary",
@@ -66,9 +67,7 @@
66
67
  "verify",
67
68
  "request",
68
69
  "time",
69
- "analyze",
70
- "cli",
71
- "commonjs"
70
+ "analyze"
72
71
  ],
73
72
  "main": "./index.js",
74
73
  "bin": {
@@ -0,0 +1,23 @@
1
+ const {
2
+ makeRequestsInConcurrentMode,
3
+ makeRequestsInSequenceMode,
4
+ } = require("./make-requests");
5
+
6
+ const strategies = new Map();
7
+ strategies.set("sequence", makeRequestsInSequenceMode);
8
+ strategies.set("concurrent", makeRequestsInConcurrentMode);
9
+
10
+ function benchmark(url, timeLimits, strategy) {
11
+ const method = strategies.get(strategy);
12
+
13
+ return Promise.all(
14
+ timeLimits
15
+ // Remove zeros timeLimits
16
+ .filter((k) => k)
17
+ .map((duration) => method(url, duration))
18
+ );
19
+ }
20
+
21
+ module.exports = {
22
+ benchmark,
23
+ };
@@ -0,0 +1,15 @@
1
+ const nock = require("nock");
2
+ const HTTP_STATUS = require("./http-status-codes");
3
+ const { benchmark } = require("./benchmark");
4
+
5
+ describe("Benchmark", () => {
6
+ it("benchmark should send request", async () => {
7
+ const targetUrl = "https://localhost/";
8
+ nock(targetUrl).persist().get("/").reply(204, []);
9
+
10
+ const responses = await benchmark(targetUrl, [1], "sequence");
11
+ const requests = responses[0].requests;
12
+ const status = requests[0].status;
13
+ expect(status).toEqual(HTTP_STATUS.NO_CONTENT);
14
+ });
15
+ });
package/src/color-logs.js CHANGED
@@ -1,17 +1,10 @@
1
1
  function setupColorLogFunction(name) {
2
2
  const fn = require(`ansi-${name}`);
3
- console[name] = (...args) => console.log(...args.map(fn));
3
+ return (...args) => console.log(...args.map(fn));
4
4
  }
5
5
 
6
- setupColorLogFunction("red");
7
- setupColorLogFunction("yellow");
8
- setupColorLogFunction("gray");
9
- setupColorLogFunction("cyan");
10
-
11
- /**
12
- * @type console
13
- * @property red
14
- * @property yellow
15
- * @property gray
16
- * @property cyan
17
- */
6
+ module.exports = {
7
+ red: setupColorLogFunction("red"),
8
+ yellow: setupColorLogFunction("yellow"),
9
+ gray: setupColorLogFunction("gray"),
10
+ };
package/src/display.js CHANGED
@@ -2,7 +2,7 @@ const { table } = require("table");
2
2
  const HTTPStatusCodes = require("http-status-codes");
3
3
  const bold = require("ansi-bold");
4
4
 
5
- const { collapseArray } = require("./object-util");
5
+ const logger = require("../src/color-logs");
6
6
 
7
7
  const SECOND_IN_MILLISECONDS = 1000;
8
8
 
@@ -13,60 +13,50 @@ const tableOptions = {
13
13
  },
14
14
  };
15
15
 
16
- function appendHttpStatusCodeLabel(statusCodeEntries) {
17
- statusCodeEntries.forEach((entry) => {
18
- let label = null;
19
- try {
20
- label = HTTPStatusCodes.getStatusText(entry[0]);
21
- } catch (err) {
22
- console.red(err);
23
- }
24
- if (typeof label === "string") {
25
- entry[0] = `${entry[0]} ${label}`;
26
- }
27
- });
28
- }
16
+ function displayRequestsSummary(results) {
17
+ const aggregateResults = results.requests.reduce((acc, item) => {
18
+ acc[item.status] = acc[item.status] ? acc[item.status] + 1 : 1;
19
+ return acc;
20
+ }, {});
29
21
 
30
- function displayRequestsSummary(attackResults) {
31
- const statusCodes = collapseArray(
32
- attackResults.requests.map((r) => r.status)
33
- );
34
- const isEmptyResults = statusCodes.length === 0;
22
+ const codes = Object.keys(aggregateResults).map(Number);
35
23
 
36
- if (isEmptyResults) {
37
- statusCodes["-"] = -1;
38
- } else {
39
- appendHttpStatusCodeLabel(statusCodes);
24
+ if (codes.length === 0) {
25
+ logger.error("No requests were made");
26
+ return;
40
27
  }
41
28
 
42
- const data = [["HTTP Status Code", "Requests quantity"].map(bold)].concat(
43
- statusCodes
44
- );
29
+ const statuses = codes.reduce((acc, code) => {
30
+ const label = HTTPStatusCodes.getReasonPhrase(code);
31
+ acc[`${code} ${label}`] = aggregateResults[code];
32
+ return acc;
33
+ }, {});
34
+
35
+ const data = [
36
+ ["HTTP Status Code", "Requests quantity"].map(bold),
37
+ ...Object.entries(statuses),
38
+ ];
39
+
45
40
  console.log(table(data, tableOptions));
46
41
  }
47
42
 
48
- function displayAttackSummary(results) {
43
+ function displayBenchmarkSummary(results) {
49
44
  const meta = [];
50
- meta.push(["Type", results.type]);
45
+ meta.push([bold("Type"), results.type]);
51
46
  const durationInSeconds = results.duration / SECOND_IN_MILLISECONDS;
52
47
  meta.push([
53
- "Effective Duration",
48
+ bold("Effective Duration"),
54
49
  `${durationInSeconds.toLocaleString()} seconds`,
55
50
  ]);
56
- meta.push(["Times", `${results.times}`]);
51
+ meta.push([bold("Times"), results.times]);
57
52
  console.log(table(meta, tableOptions));
58
53
  }
59
54
 
60
- function displaySummary(attackResults) {
61
- displayRequestsSummary(attackResults);
62
- displayAttackSummary(attackResults);
63
- }
64
-
65
- function displayError(err) {
66
- console.log(`${err.name}: ${err.message}\n`);
55
+ function displaySummary(results) {
56
+ displayRequestsSummary(results);
57
+ displayBenchmarkSummary(results);
67
58
  }
68
59
 
69
60
  module.exports = {
70
61
  displaySummary,
71
- displayError,
72
62
  };
@@ -18,8 +18,8 @@ async function makeRequest(url, options = {}) {
18
18
  const protocol = getProtocol(url);
19
19
  return new Promise((resolve, reject) => {
20
20
  protocol.get(url, options, (res) => {
21
- res.addListener("data", (data) => {
22
- response.text += data.toString();
21
+ res.addListener("data", (buffer) => {
22
+ response.text += buffer.toString();
23
23
  });
24
24
  res.addListener("error", (err) => {
25
25
  reject(err);
@@ -0,0 +1,41 @@
1
+ const nock = require("nock");
2
+ const {
3
+ makeRequest,
4
+ makeRequestsInConcurrentMode,
5
+ makeRequestsInSequenceMode,
6
+ } = require("./make-requests");
7
+ const HTTP_STATUS = require("./http-status-codes");
8
+
9
+ describe("makeRequests", () => {
10
+ const targetUrl = "https://localhost/";
11
+
12
+ beforeEach(() => {
13
+ nock(targetUrl)
14
+ .persist()
15
+ .get("/")
16
+ .delay(100)
17
+ .times(Infinity)
18
+ .reply(204, []);
19
+ });
20
+
21
+ it("should returns status code", async () => {
22
+ const response = await makeRequest(targetUrl, { agent: false });
23
+ expect(response.status).toEqual(HTTP_STATUS.NO_CONTENT);
24
+ });
25
+
26
+ it.skip("should make HTTP requests in concurrent mode", async (done) => {
27
+ const results = await makeRequestsInConcurrentMode(targetUrl, 1);
28
+ setTimeout(() => {
29
+ expect(results.requests.length).toBeGreaterThan(10);
30
+ done();
31
+ }, 100);
32
+ });
33
+
34
+ it.skip("should make HTTP requests in sequence mode", async (done) => {
35
+ const results = await makeRequestsInSequenceMode(targetUrl, 1);
36
+ setTimeout(() => {
37
+ expect(results.requests.length).toBeLessThan(11);
38
+ done();
39
+ }, 100);
40
+ });
41
+ });
@@ -1,17 +0,0 @@
1
- function collapseArray(array) {
2
- const hashMap = array.reduce((mem, item) => {
3
- if (!mem[item]) {
4
- mem[item] = 0;
5
- }
6
- mem[item]++;
7
- return mem;
8
- }, {});
9
-
10
- const entries = Object.entries(hashMap);
11
-
12
- return entries.map((entry) => Number(entry[0]));
13
- }
14
-
15
- module.exports = {
16
- collapseArray,
17
- };