• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22'use strict';
23const common = require('../common');
24
25const assert = require('node:assert');
26const cluster = require('node:cluster');
27const { spawnSync } = require('node:child_process');
28
29assert.strictEqual('NODE_UNIQUE_ID' in process.env, false,
30                   `NODE_UNIQUE_ID (${process.env.NODE_UNIQUE_ID}) ` +
31                   'should be removed on startup');
32
33{
34  const { status } = spawnSync(process.execPath, [
35    '-e',
36    `
37      const { strictEqual } = require('node:assert');
38      Object.setPrototypeOf(process.env, { NODE_UNIQUE_ID: 0 });
39      strictEqual(require('cluster').isPrimary, true);
40    `,
41  ]);
42  assert.strictEqual(status, 0);
43}
44
45function forEach(obj, fn) {
46  Object.keys(obj).forEach((name, index) => {
47    fn(obj[name], name, index);
48  });
49}
50
51
52if (cluster.isWorker) {
53  require('http').Server(common.mustNotCall()).listen(0, '127.0.0.1');
54} else if (cluster.isPrimary) {
55
56  const checks = {
57    cluster: {
58      events: {
59        fork: false,
60        online: false,
61        listening: false,
62        exit: false
63      },
64      equal: {
65        fork: false,
66        online: false,
67        listening: false,
68        exit: false
69      }
70    },
71
72    worker: {
73      events: {
74        online: false,
75        listening: false,
76        exit: false
77      },
78      equal: {
79        online: false,
80        listening: false,
81        exit: false
82      },
83      states: {
84        none: false,
85        online: false,
86        listening: false,
87        dead: false
88      }
89    }
90  };
91
92  const stateNames = Object.keys(checks.worker.states);
93
94  // Check events, states, and emit arguments
95  forEach(checks.cluster.events, (bool, name, index) => {
96
97    // Listen on event
98    cluster.on(name, common.mustCall(function(/* worker */) {
99
100      // Set event
101      checks.cluster.events[name] = true;
102
103      // Check argument
104      checks.cluster.equal[name] = worker === arguments[0];
105
106      // Check state
107      const state = stateNames[index];
108      checks.worker.states[state] = (state === worker.state);
109    }));
110  });
111
112  // Kill worker when listening
113  cluster.on('listening', common.mustCall(() => {
114    worker.kill();
115  }));
116
117  // Kill process when worker is killed
118  cluster.on('exit', common.mustCall());
119
120  // Create worker
121  const worker = cluster.fork();
122  assert.strictEqual(worker.id, 1);
123  assert(worker instanceof cluster.Worker,
124         'the worker is not a instance of the Worker constructor');
125
126  // Check event
127  forEach(checks.worker.events, function(bool, name, index) {
128    worker.on(name, common.mustCall(function() {
129      // Set event
130      checks.worker.events[name] = true;
131
132      // Check argument
133      checks.worker.equal[name] = (worker === this);
134
135      switch (name) {
136        case 'exit':
137          assert.strictEqual(arguments[0], worker.process.exitCode);
138          assert.strictEqual(arguments[1], worker.process.signalCode);
139          assert.strictEqual(arguments.length, 2);
140          break;
141
142        case 'listening': {
143          assert.strictEqual(arguments.length, 1);
144          assert.strictEqual(Object.keys(arguments[0]).length, 4);
145          assert.strictEqual(arguments[0].address, '127.0.0.1');
146          assert.strictEqual(arguments[0].addressType, 4);
147          assert(Object.hasOwn(arguments[0], 'fd'));
148          assert.strictEqual(arguments[0].fd, undefined);
149          const port = arguments[0].port;
150          assert(Number.isInteger(port));
151          assert(port >= 1);
152          assert(port <= 65535);
153          break;
154        }
155        default:
156          assert.strictEqual(arguments.length, 0);
157          break;
158      }
159    }));
160  });
161
162  // Check all values
163  process.once('exit', () => {
164    // Check cluster events
165    forEach(checks.cluster.events, (check, name) => {
166      assert(check,
167             `The cluster event "${name}" on the cluster object did not fire`);
168    });
169
170    // Check cluster event arguments
171    forEach(checks.cluster.equal, (check, name) => {
172      assert(check,
173             `The cluster event "${name}" did not emit with correct argument`);
174    });
175
176    // Check worker states
177    forEach(checks.worker.states, (check, name) => {
178      assert(check,
179             `The worker state "${name}" was not set to true`);
180    });
181
182    // Check worker events
183    forEach(checks.worker.events, (check, name) => {
184      assert(check,
185             `The worker event "${name}" on the worker object did not fire`);
186    });
187
188    // Check worker event arguments
189    forEach(checks.worker.equal, (check, name) => {
190      assert(check,
191             `The worker event "${name}" did not emit with correct argument`);
192    });
193  });
194
195}
196