Skip to content

Remove vows from tests.#854

Merged
jdmarshall merged 10 commits intonode-config:masterfrom
jdmarshall:avow
Aug 15, 2025
Merged

Remove vows from tests.#854
jdmarshall merged 10 commits intonode-config:masterfrom
jdmarshall:avow

Conversation

@jdmarshall
Copy link
Copy Markdown
Collaborator

@jdmarshall jdmarshall commented Aug 15, 2025

This is prep work for ESM conversion.

Vows tries to use the module.exports to find tests, and in the process it generates some very, very strange errors when you go from require -> import -> require, which I don't think nodejs quite knows how to do yet.

This fixes #807, #852, #853, and should unblock #846

This will however mess up #845 which I will have to rebase and fix

Summary by CodeRabbit

  • Tests

    • Migrated entire test suite from the legacy runner to Node’s built-in test runner, modernized structure and assertions, improved setup/teardown, and expanded/clarified coverage across many configuration scenarios (env/CLI overrides, parsing, async/deferred values, immutability, multi-file resolution).
  • Chores

    • Updated test invocation to use Node's built-in test runner with coverage; removed the legacy Vows test script and the Vows devDependency.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Aug 15, 2025

Warning

Rate limit exceeded

@jdmarshall has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 7 minutes and 13 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 0556b9d and eada383.

📒 Files selected for processing (32)
  • package.json (1 hunks)
  • test/0-util.js (1 hunks)
  • test/1-protected-test.js (8 hunks)
  • test/10-gitcrypt-test.js (3 hunks)
  • test/11-regexp.js (1 hunks)
  • test/12-node_env-override.js (1 hunks)
  • test/13-node_env-hostname.js (1 hunks)
  • test/14-node_env-load-multiple-files.js (1 hunks)
  • test/15-async-configs.js (2 hunks)
  • test/16-custom-parser.js (1 hunks)
  • test/17-toml.js (2 hunks)
  • test/18-skipConfigSources.js (1 hunks)
  • test/19-custom-environment-variables.js (1 hunks)
  • test/2-config-test.js (4 hunks)
  • test/20-multiple-config.js (6 hunks)
  • test/21-date.js (1 hunks)
  • test/22-binary.js (1 hunks)
  • test/22-files-order.js (3 hunks)
  • test/23-serialize.js (1 hunks)
  • test/3-deferred-configs.js (2 hunks)
  • test/4-array-merging.js (2 hunks)
  • test/5-getConfigSources.js (2 hunks)
  • test/6-strict-mode.js (2 hunks)
  • test/7-unicode-situations.js (2 hunks)
  • test/8-config-extending.js (2 hunks)
  • test/9-raw-configs.js (1 hunks)
  • test/makeImmutable-shared-refs.js (1 hunks)
  • test/util.js (1 hunks)
  • test/x-config-js-test-transpiled.js (2 hunks)
  • test/x-config-test-ts-module-exports.js (2 hunks)
  • test/x-config-test-ts.js (2 hunks)
  • test/x-deferred-configs-ts.js (2 hunks)

Walkthrough

All test files were migrated from the Vows framework to Node's built-in test runner (node:test), assertions moved to Node assert, test exports removed, and package.json scripts/devDependency updated to use Node's test runner and remove vows. No production code was modified.

Changes

Cohort / File(s) Summary
package & scripts
package.json
Removed devDependencies.vows; updated scripts.test to "NODE_OPTIONS='--no-experimental-strip-types' c8 node --test"; removed scripts.vows.
All tests — harness migration
test/*, test/...
Rewrote test suites to use Node's node:test (describe/it/before/after hooks), replaced Vows assertions with Node assert (strict/deep), removed .export(module) calls, converted topics/batches to per-test hooks (before/beforeEach), introduced requireUncached usage for isolated loads. Files include (but are not limited to): test/0-util.js, test/1-protected-test.js, test/2-config-test.js, test/3-deferred-configs.js, test/4-array-merging.js, test/5-getConfigSources.js, test/6-strict-mode.js, test/7-unicode-situations.js, test/8-config-extending.js, test/9-raw-configs.js, test/10-gitcrypt-test.js, test/11-regexp.js, test/12-node_env-override.js, test/13-node_env-hostname.js, test/14-node_env-load-multiple-files.js, test/15-async-configs.js, test/16-custom-parser.js, test/17-toml.js, test/18-skipConfigSources.js, test/19-custom-environment-variables.js, test/20-multiple-config.js, test/21-date.js, test/22-binary.js, test/22-files-order.js, test/23-serialize.js, test/0-23 variants and many x-* / helper tests (e.g., test/util.js, test/makeImmutable-shared-refs.js, test/x-config-*.js, test/*-transpiled.js). No production/library source changes.

Sequence Diagram(s)

(omitted — changes are test-harness migrations across many files; no single control-flow diagram applies)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Assessment against linked issues

Objective Addressed Explanation
Remove Vows and migrate tests to Node's built-in test runner (vows needs to go) [#807]

"I hopped from Vows to node:test tonight,
Tucked scripts and assertions neat and tight.
Uncached loads, envs reset with care,
CI hums softly — carrots in the air.
Thump-thump, small rabbit, tests all green and bright!"

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@jdmarshall
Copy link
Copy Markdown
Collaborator Author

jdmarshall commented Aug 15, 2025

Oh goodie. Node 22.18 breaks my tests.

looks like node --test is the problem

Known issue: nodejs/node#59364

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

🔭 Outside diff range comments (2)
test/6-strict-mode.js (1)

106-107: Remove vows-specific assertion

The assert.instanceOf method is from the vows framework and doesn't exist in Node's built-in assert module.

-          assert.instanceOf(error,Error);
+          assert(error instanceof Error);
test/2-config-test.js (1)

31-33: Avoid polluting process.argv across tests.

Using push accumulates flags over multiple tests and can leak state into later suites. Set argv explicitly per test to avoid order-dependent failures.

-      process.argv.push('--NODE_CONFIG={"EnvOverride":{"parm5":"overridden from --NODE_CONFIG","parm6":101}}');
+      process.argv = ['node', 'test.js', '--NODE_CONFIG={"EnvOverride":{"parm5":"overridden from --NODE_CONFIG","parm6":101}}'];
🧹 Nitpick comments (44)
test/4-array-merging.js (2)

28-38: Prefer deepStrictEqual for exact structural comparison

Using assert.deepStrictEqual avoids loose equality and is the recommended assert variant for arrays/objects.

Apply this diff:

-        assert.deepEqual(CONFIG.arrayMerging.emptyArray, ['not empty anymore']);
+        assert.deepStrictEqual(CONFIG.arrayMerging.emptyArray, ['not empty anymore']);
@@
-        assert.deepEqual(CONFIG.arrayMerging.oneItem, ['replaced']);
+        assert.deepStrictEqual(CONFIG.arrayMerging.oneItem, ['replaced']);
@@
-        assert.deepEqual(CONFIG.arrayMerging.removeMe, []);
+        assert.deepStrictEqual(CONFIG.arrayMerging.removeMe, []);

27-27: Minor wording nit: duplicated “Tests” in suite title

Tighten the description string.

-  describe('Array merging tests Tests', function() {
+  describe('Array merging tests', function() {
test/17-toml.js (1)

6-6: Clarify parser import as a side-effect (or avoid unused binding)

If this require is for side effects (registering TOML support), avoid the unused variable and document the intent.

-const Parser = require('../parser');
+// Register TOML parser via module side effects.
+require('../parser');
test/8-config-extending.js (1)

25-31: Tidy up: path.join, const naming, and leaner assertion function

Non-functional cleanup that improves readability and consistency.

-      process.env.NODE_CONFIG_DIR = __dirname + '/8-config';
-      var base_config = require(process.env.NODE_CONFIG_DIR + path.sep + 'base-config.json');
-      CONFIG.util.attachProtoDeep(base_config);
-
-      assert.doesNotThrow(function () {
-          let result = Util.extendDeep(base_config, CONFIG);
-      }, 'Extending a configuration with another configuration has an error');
+      process.env.NODE_CONFIG_DIR = __dirname + '/8-config';
+      const baseConfig = require(path.join(process.env.NODE_CONFIG_DIR, 'base-config.json'));
+      CONFIG.util.attachProtoDeep(baseConfig);
+
+      assert.doesNotThrow(() => {
+        Util.extendDeep(baseConfig, CONFIG);
+      }, 'Extending a configuration with another configuration has an error');
test/22-binary.js (1)

28-36: Use deepStrictEqual for Buffer comparison

Buffers should be compared strictly; deepStrictEqual avoids edge cases and aligns with modern assert usage.

-      assert.deepEqual(expectedBuffer, actualBuffer);
+      assert.deepStrictEqual(expectedBuffer, actualBuffer);
test/23-serialize.js (1)

11-11: Nit: Rename inner describe from “Binary tests” to reflect serialization

Minor naming mismatch; this suite isn’t testing binary data.

Apply this diff:

-  describe('Binary tests', function() {
+  describe('Serialization tests', function() {
test/11-regexp.js (5)

14-14: Nit: Typo in suite name (“Regexp tests Tests”)

Drop the duplicate “Tests” for readability.

-  describe('Regexp tests Tests', function() {
+  describe('Regexp tests', function() {

15-15: Prefer const for immutable alias

config isn’t reassigned; const communicates intent and prevents accidental reassignments.

-    let config = CONFIG;
+    const config = CONFIG;

25-30: Clarify exec() call with an explicit test string

Calling RegExp#exec with no argument implicitly coerces undefined to 'undefined'. Passing a literal string removes ambiguity.

-        regExp.exec();
+        regExp.exec('some input');

41-46: Use strict assertions for exact string matches

assert.strictEqual avoids type coercion and aligns with modern assert usage.

-      assert.equal(config.SomeMore.regexp1.source, /This is a Regexp/g.source);
+      assert.strictEqual(config.SomeMore.regexp1.source, /This is a Regexp/g.source);
@@
-      assert.equal(config.SomeMore.regexp2.source, /This is the replaced/g.source);
+      assert.strictEqual(config.SomeMore.regexp2.source, /This is the replaced/g.source);

11-11: Nit: Prefer const over var for CONFIG

CONFIG isn’t reassigned; const is safer and idiomatic.

-var CONFIG = requireUncached(__dirname + '/../lib/config');
+const CONFIG = requireUncached(__dirname + '/../lib/config');
test/14-node_env-load-multiple-files.js (4)

29-29: Mismatch between suite title and setup

The description says NODE_ENV includes 'cloud' and 'bare-metal', but setup uses 'development,bare-metal'. Align the title to the setup.

-  describe('When there is \'cloud\' and \'bare-metal\' values in NODE_ENV and HOST is \'test\'', function() {
+  describe('When there is \'development\' and \'bare-metal\' values in NODE_ENV and HOST is \'test\'', function() {

18-26: Prefer strict equality in assertions

Use assert.strictEqual to avoid coercion and match best practices (also aligns with the objective to correct assert usage).

-      assert.equal(config.get('db.name'), 'development-config-env-provided');
-      assert.equal(config.get('db.port'), 3000);
+      assert.strictEqual(config.get('db.name'), 'development-config-env-provided');
+      assert.strictEqual(config.get('db.port'), 3000);
@@
-      assert.equal(config.get('app.context'), 'local cloud');
-      assert.equal(config.get('app.message'), 'local development');
+      assert.strictEqual(config.get('app.context'), 'local cloud');
+      assert.strictEqual(config.get('app.message'), 'local development');

40-43: Prefer strict equality in assertions

Same reasoning as above.

-      assert.equal(config.get('host.os'), 'linux');
-      assert.equal(config.get('host.arch'), 'x86_64');
+      assert.strictEqual(config.get('host.os'), 'linux');
+      assert.strictEqual(config.get('host.arch'), 'x86_64');

55-57: Prefer strict equality in assertions

Same reasoning as above.

-      assert.equal(config.get('db.name'), 'bare-metal-config-env-provided');
+      assert.strictEqual(config.get('db.name'), 'bare-metal-config-env-provided');
test/x-config-js-test-transpiled.js (1)

21-21: Nit: Assign env vars as strings

process.env values are strings; being explicit avoids accidental toString surprises and is consistent with other env assignments.

-      process.env.NODE_CONFIG_STRICT_MODE = false;
+      process.env.NODE_CONFIG_STRICT_MODE = 'false';
test/21-date.js (1)

10-10: Nit: Prefer const for CONFIG

CONFIG is never reassigned; const is clearer and safer.

-var CONFIG = requireUncached(__dirname + '/../lib/config');
+const CONFIG = requireUncached(__dirname + '/../lib/config');
test/16-custom-parser.js (3)

20-24: Prefer assert.strictEqual over assert.equal for precise comparisons

Use strict equality to avoid coercion and align with Node’s assert best practices.

-      assert.equal(config.get('file.type'), 'yaml');
-      assert.equal(config.get('file.name'), 'local.yml');
-      assert.equal(config.get('parser'), 'js-yaml');
-      assert.equal(config.has('custom.key'), false);
+      assert.strictEqual(config.get('file.type'), 'yaml');
+      assert.strictEqual(config.get('file.name'), 'local.yml');
+      assert.strictEqual(config.get('parser'), 'js-yaml');
+      assert.strictEqual(config.has('custom.key'), false);
-      assert.equal(config.get('file.type'), 'custom');
-      assert.equal(config.get('file.name'), 'local.yml');
+      assert.strictEqual(config.get('file.type'), 'custom');
+      assert.strictEqual(config.get('file.name'), 'local.yml');
       // assert.equal(config.get('file.name'), 'my-custom-awesome-dsl');
-      assert.equal(config.get('parser'), 'custom-awesomeness');
-      assert.equal(config.get('custom.key'), 'wow!');
+      assert.strictEqual(config.get('parser'), 'custom-awesomeness');
+      assert.strictEqual(config.get('custom.key'), 'wow!');
-      assert.equal(config.get('file.type'), 'json');
-      assert.equal(config.get('file.name'), 'local.yml');
-      assert.equal(config.get('parser'), 'json');
-      assert.equal(config.get('custom.key'), 'wow!');
+      assert.strictEqual(config.get('file.type'), 'json');
+      assert.strictEqual(config.get('file.name'), 'local.yml');
+      assert.strictEqual(config.get('parser'), 'json');
+      assert.strictEqual(config.get('custom.key'), 'wow!');
-      assert.equal(config.get('file.type'), 'yaml');
-      assert.equal(config.get('file.name'), 'local.yml');
-      assert.equal(config.get('parser'), 'json5');
-      assert.equal(config.get('custom.key'), 'json5 rules!');
+      assert.strictEqual(config.get('file.type'), 'yaml');
+      assert.strictEqual(config.get('file.name'), 'local.yml');
+      assert.strictEqual(config.get('parser'), 'json5');
+      assert.strictEqual(config.get('custom.key'), 'json5 rules!');

Also applies to: 38-43, 56-61, 75-79


82-85: Tighten teardown: remove unused parameter and clean NODE_CONFIG_DIR

The after hook doesn’t need a parameter and should also clear NODE_CONFIG_DIR to avoid side effects across files.

-  after(function (topic) {
-    delete process.env.NODE_CONFIG_PARSER;
-    requireUncached(__dirname + '/../parser');
-  });
+  after(function () {
+    delete process.env.NODE_CONFIG_PARSER;
+    delete process.env.NODE_CONFIG_DIR;
+    requireUncached(__dirname + '/../parser');
+  });

40-41: Remove or update stale commented assertion

This commented-out assertion contradicts the active assertion above. Either delete it or rework the scenario to assert the intended value.

-      // assert.equal(config.get('file.name'), 'my-custom-awesome-dsl');
+      // If needed, assert the intended custom filename here once behavior is defined.
test/13-node_env-hostname.js (2)

35-37: Use strict equality for HOSTNAME test

Safer and consistent with the rest of the suite.

-    it('HOSTNAME env variable is the winner', function() {
-      assert.equal(config.util.getEnv('HOSTNAME'), 'some.machine');
-    });
+    it('HOSTNAME env variable is the winner', function() {
+      assert.strictEqual(config.util.getEnv('HOSTNAME'), 'some.machine');
+    });

4-4: Add teardown to avoid env leakage across tests

Clean up HOST and HOSTNAME to reduce cross-file coupling. Requires importing after.

-const { describe, it, before, beforeEach } = require('node:test');
+const { describe, it, before, beforeEach, after } = require('node:test');
   });
-});
+  after(() => {
+    delete process.env.HOST;
+    delete process.env.HOSTNAME;
+  });
+});

Also applies to: 55-55

test/10-gitcrypt-test.js (2)

4-4: Add teardown for environment variables

Minor hygiene to prevent env leakage to other files. Requires importing after.

-const { describe, it, beforeEach } = require('node:test');
+const { describe, it, beforeEach, after } = require('node:test');
-});
+  after(() => {
+    delete process.env.CONFIG_SKIP_GITCRYPT;
+    delete process.env.NODE_ENV;
+    delete process.env.NODE_CONFIG_STRICT_MODE;
+    delete process.env.NODE_CONFIG;
+    delete process.env.CUSTOM_JSON_ENVIRONMENT_VAR;
+  });
+});

Also applies to: 57-57


18-25: Optional: assign string values to process.env for clarity

process.env values are strings; using booleans/numbers relies on implicit coercion. Assign string values for intent.

-      process.env.NODE_CONFIG_STRICT_MODE = false;
+      process.env.NODE_CONFIG_STRICT_MODE = 'false';
@@
-      process.env.CONFIG_SKIP_GITCRYPT = 1;
+      process.env.CONFIG_SKIP_GITCRYPT = '1';
@@
-      process.env.CONFIG_SKIP_GITCRYPT = 0;
+      process.env.CONFIG_SKIP_GITCRYPT = '0';

Also applies to: 46-47

test/x-config-test-ts.js (3)

21-27: Delete env vars instead of setting empty strings

Assigning '' keeps the variable defined; delete provides a true “unset” state and mirrors behavior in other tests.

-    process.env.NODE_APP_INSTANCE = '';
-    process.env.NODE_ENV = '';
-    process.env.NODE_CONFIG = '';
+    delete process.env.NODE_APP_INSTANCE;
+    delete process.env.NODE_ENV;
+    delete process.env.NODE_CONFIG;

41-43: Use strict equality for string comparison

Avoids coercion and keeps assert usage consistent.

-      assert.equal(config.siteTitle, 'New Instance!');
+      assert.strictEqual(config.siteTitle, 'New Instance!');

51-52: Avoid variable shadowing in last test

No need to re-declare config; it isn’t used. This avoids shadowing the outer variable and reduces lint noise.

-      let config = requireUncached(__dirname + '/../lib/config');
+      requireUncached(__dirname + '/../lib/config');
test/12-node_env-override.js (3)

41-43: Prefer strict equality in env and config assertions

Use assert.strictEqual to avoid coercion and keep consistent with Node’s assert best practices.

-      assert.equal(config.util.getEnv('NODE_ENV'), 'development');
-      assert.equal(config.get('deploymentUsed'), 'default');
+      assert.strictEqual(config.util.getEnv('NODE_ENV'), 'development');
+      assert.strictEqual(config.get('deploymentUsed'), 'default');
-      assert.equal(config.util.getEnv('NODE_CONFIG_ENV'), 'apollo');
-      assert.equal(config.get('deploymentUsed'), 'node-config-env-provided');
+      assert.strictEqual(config.util.getEnv('NODE_CONFIG_ENV'), 'apollo');
+      assert.strictEqual(config.get('deploymentUsed'), 'node-config-env-provided');
-      assert.equal(config.util.getEnv('NODE_CONFIG_ENV'), 'mercury');
-      assert.equal(config.get('deploymentUsed'), 'node-env-provided');
+      assert.strictEqual(config.util.getEnv('NODE_CONFIG_ENV'), 'mercury');
+      assert.strictEqual(config.get('deploymentUsed'), 'node-env-provided');
-      assert.equal(config.get('deploymentUsed'), 'node-config-env-provided');
+      assert.strictEqual(config.get('deploymentUsed'), 'node-config-env-provided');

Also applies to: 56-58, 75-77, 95-96


46-59: Clarify test title to match behavior

The test description says “NODE_CONFIG_ENV by itself should be used,” but the setup defines only NODE_ENV. Consider rewording to avoid confusion.

-    it('NODE_CONFIG_ENV by itself should be used', function () {
+    it('NODE_ENV by itself should be used (reflected via NODE_CONFIG_ENV lookup)', function () {

65-82: Symmetry: cleanup correct variable and wording

This suite sets only NODE_CONFIG_ENV. The after hook should clean that var; message also mentions NODE_ENV. See prior fix; ensure wording matches setup.

test/0-util.js (3)

51-73: Improve variable naming consistency

Line 71 has an arithmetic expression split across lines which appears to be unintentional.

-        1.1
-        - 1,
+        1.1 - 1,

134-144: Use strictEqual for consistency

Several assertions use assert.equal when assert.strictEqual would be more appropriate for type-safe comparisons.

-      assert.equal(util.getOption(options, "item1"), 23);
+      assert.strictEqual(util.getOption(options, "item1"), 23);
-      assert.equal(util.getOption(options, "missing", "orange"), "orange");
+      assert.strictEqual(util.getOption(options, "missing", "orange"), "orange");
-      assert.equal(util.getOption(options, "item2", "orange"), 0);
+      assert.strictEqual(util.getOption(options, "item2", "orange"), 0);

152-156: Use strictEqual for consistency in locateMatchingFiles test

-      assert.equal(Path.basename(results[0]), "default.json");
-      assert.equal(Path.basename(results[1]), "default.mjs");
+      assert.strictEqual(Path.basename(results[0]), "default.json");
+      assert.strictEqual(Path.basename(results[1]), "default.mjs");
test/20-multiple-config.js (1)

8-65: Consider extracting common test logic

All test cases follow the same pattern of setting NODE_CONFIG_DIR and asserting that CONFIG loads without error. Consider extracting this into a helper function to reduce duplication.

Add a helper function at the top of the describe block:

describe('Tests for multiple config', function() {
  function testConfigDirSetup(description, configDirs) {
    it(description, function() {
      process.env.NODE_CONFIG_DIR = configDirs.join(path.delimiter);
      assert.doesNotThrow(function () {
        const CONFIG = requireUncached(__dirname + '/../lib/config');
      }, description.replace('should work without error', 'has an error'));
    });
  }

  testConfigDirSetup('Adding multiple relative configuration paths should work without error', 
    ['./test/20-config', './test/20-extra-config']);
  
  testConfigDirSetup('Adding multiple absolute configuration paths should work without error',
    [__dirname + '/20-config', __dirname + '/20-extra-config']);
  
  // ... etc
});
test/6-strict-mode.js (1)

3-5: Clean migration to Node.js built-in test framework

The imports are properly updated. Note that before and beforeEach are imported but not used - consider removing them to keep imports clean.

-const { describe, it, before, beforeEach } = require('node:test');
+const { describe, it } = require('node:test');
test/9-raw-configs.js (2)

8-8: Remove unused test hooks (before, beforeEach).

They're not used in this file. Trimming these keeps the import clean and avoids lints.

-const { describe, it, before, beforeEach } = require('node:test');
+const { describe, it } = require('node:test');

16-29: Prefer strict assertions to avoid coercion.

Use strictEqual/deepStrictEqual to eliminate any risk of type coercion in tests.

-      assert.equal(CONFIG.get('circularReference'), process.stdout);
-      assert.deepEqual(CONFIG.get('testObj'), { foo: 'bar' });
+      assert.strictEqual(CONFIG.get('circularReference'), process.stdout);
+      assert.deepStrictEqual(CONFIG.get('testObj'), { foo: 'bar' });
       assert.strictEqual(typeof CONFIG.get('yell'), 'function');
-      assert.equal(CONFIG.get('innerRaw').innerCircularReference, process.stdout);
-      assert.equal(CONFIG.get('innerRaw.innerCircularReference'), process.stdout);
+      assert.strictEqual(CONFIG.get('innerRaw').innerCircularReference, process.stdout);
+      assert.strictEqual(CONFIG.get('innerRaw.innerCircularReference'), process.stdout);
-      assert.equal(CONFIG.get('nestedRaw').nested.test, process.stdout);
-      assert.equal(CONFIG.get('nestedRaw.nested').test, process.stdout);
-      assert.equal(CONFIG.get('nestedRaw.nested.test'), process.stdout);
+      assert.strictEqual(CONFIG.get('nestedRaw').nested.test, process.stdout);
+      assert.strictEqual(CONFIG.get('nestedRaw.nested').test, process.stdout);
+      assert.strictEqual(CONFIG.get('nestedRaw.nested.test'), process.stdout);
test/util.js (2)

21-25: Isolate process.argv mutations within the test.

Mutating process.argv without restoring can leak into subsequent tests. Use a local save/restore.

-      process.env.BOTH = 'in-the-env';
-      process.argv = ['ignore', 'ignore', '--BOTH=in-the-argv'];
-      assert.equal(initParam('BOTH', 'mydefault'), 'in-the-argv');
+      process.env.BOTH = 'in-the-env';
+      const prevArgv = process.argv.slice();
+      try {
+        process.argv = ['ignore', 'ignore', '--BOTH=in-the-argv'];
+        assert.equal(initParam('BOTH', 'mydefault'), 'in-the-argv');
+      } finally {
+        process.argv = prevArgv;
+      }

Apply the same pattern to other tests that modify process.argv.


63-69: Restore NODE_CONFIG safely to avoid setting the string 'undefined'.

If prev is undefined, assigning it back will set the env var to the string 'undefined'. Delete instead.

-      process.env.NODE_CONFIG = prev;
+      if (prev === undefined) {
+        delete process.env.NODE_CONFIG;
+      } else {
+        process.env.NODE_CONFIG = prev;
+      }
test/3-deferred-configs.js (1)

28-29: Optional: adopt strict asserts for consistency.

Not required, but switching to strictEqual/deepStrictEqual across this file aligns with modern assert usage and avoids accidental coercion.

Example:

-      assert.equal(CONFIG.welcomeEmail.subject, 'Welcome to New Instance!');
+      assert.strictEqual(CONFIG.welcomeEmail.subject, 'Welcome to New Instance!');

Also applies to: 33-34, 38-39, 46-47, 50-51, 54-56, 59-61

test/5-getConfigSources.js (3)

27-29: Align the test description with the expectation.

The description says “four entries” but the assertion expects 6. Either adjust the description or the expectation; given the multi-directory setup + env + CLI, 6 is plausible.

Suggested minimal edit:

-    it('Two files plus NODE_CONFIG in env and as command line args should result in four entries', function() {
-      assert.equal(sources.length,6);
+    it('Two directories + NODE_CONFIG in env + CLI should result in six entries', function() {
+      assert.equal(sources.length, 6);

105-107: Fix test title to reflect the actual expectation (4 entries).

The body asserts 4 entries, but the title says 3.

-  it('Three files should result in 3 entries', function() {
-    assert.equal(sources.length,4);
+  it('Empty-returning file is still included, resulting in 4 entries', function() {
+    assert.equal(sources.length, 4);

5-6: Prefer strict assertions to avoid type coercion.

Using assert.strictEqual and assert.deepStrictEqual improves correctness in cases where types matter (numbers/booleans/strings).

Example:

-      assert.equal(sources.length, 6);
+      assert.strictEqual(sources.length, 6);
test/2-config-test.js (1)

5-5: Prefer strict assertions across the suite.

Using assert.strictEqual/deepStrictEqual instead of equal/deepEqual prevents type coercion and false positives, especially for booleans and numbers used throughout this file.

Example:

-assert.equal(config.EnvOverride.parm6, 101);
+assert.strictEqual(config.EnvOverride.parm6, 101);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2040baf and 994eee7.

📒 Files selected for processing (32)
  • package.json (1 hunks)
  • test/0-util.js (1 hunks)
  • test/1-protected-test.js (8 hunks)
  • test/10-gitcrypt-test.js (3 hunks)
  • test/11-regexp.js (1 hunks)
  • test/12-node_env-override.js (1 hunks)
  • test/13-node_env-hostname.js (1 hunks)
  • test/14-node_env-load-multiple-files.js (1 hunks)
  • test/15-async-configs.js (2 hunks)
  • test/16-custom-parser.js (1 hunks)
  • test/17-toml.js (2 hunks)
  • test/18-skipConfigSources.js (1 hunks)
  • test/19-custom-environment-variables.js (1 hunks)
  • test/2-config-test.js (4 hunks)
  • test/20-multiple-config.js (6 hunks)
  • test/21-date.js (1 hunks)
  • test/22-binary.js (1 hunks)
  • test/22-files-order.js (3 hunks)
  • test/23-serialize.js (1 hunks)
  • test/3-deferred-configs.js (2 hunks)
  • test/4-array-merging.js (2 hunks)
  • test/5-getConfigSources.js (3 hunks)
  • test/6-strict-mode.js (2 hunks)
  • test/7-unicode-situations.js (2 hunks)
  • test/8-config-extending.js (2 hunks)
  • test/9-raw-configs.js (1 hunks)
  • test/makeImmutable-shared-refs.js (1 hunks)
  • test/util.js (1 hunks)
  • test/x-config-js-test-transpiled.js (2 hunks)
  • test/x-config-test-ts-module-exports.js (2 hunks)
  • test/x-config-test-ts.js (2 hunks)
  • test/x-deferred-configs-ts.js (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (27)
test/21-date.js (4)
test/11-regexp.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/9-raw-configs.js (3)
  • requireUncached (7-7)
  • require (8-8)
  • assert (9-9)
test/x-config-js-test-transpiled.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/23-serialize.js (3)
  • requireUncached (5-5)
  • require (6-6)
  • assert (7-7)
test/19-custom-environment-variables.js (3)
test/5-getConfigSources.js (6)
  • Path (3-3)
  • require (5-5)
  • requireUncached (4-4)
  • config (22-22)
  • config (76-76)
  • config (101-101)
test/12-node_env-override.js (7)
  • require (4-4)
  • requireUncached (3-3)
  • config (11-11)
  • config (31-31)
  • config (47-47)
  • config (66-66)
  • config (85-85)
test/makeImmutable-shared-refs.js (1)
  • require (3-3)
test/x-config-test-ts-module-exports.js (1)
lib/config.js (1)
  • config (1030-1030)
test/18-skipConfigSources.js (3)
lib/config.js (5)
  • Path (9-9)
  • require (8-8)
  • config (1030-1030)
  • value (142-142)
  • value (336-336)
lib/util.js (1)
  • Path (8-8)
test/6-strict-mode.js (3)
  • require (4-4)
  • requireUncached (3-3)
  • assert (5-5)
test/x-config-js-test-transpiled.js (7)
test/22-binary.js (4)
  • requireUncached (7-7)
  • require (8-8)
  • require (11-11)
  • assert (9-9)
test/17-toml.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/21-date.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/22-files-order.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/8-config-extending.js (4)
  • requireUncached (4-4)
  • require (5-5)
  • require (7-7)
  • assert (6-6)
test/9-raw-configs.js (4)
  • requireUncached (7-7)
  • require (8-8)
  • assert (9-9)
  • CONFIG (11-11)
test/23-serialize.js (4)
  • requireUncached (5-5)
  • require (6-6)
  • assert (7-7)
  • CONFIG (8-8)
test/1-protected-test.js (2)
test/8-config-extending.js (1)
  • result (30-30)
lib/config.js (2)
  • config (1030-1030)
  • diff (815-815)
test/17-toml.js (7)
test/21-date.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/22-files-order.js (4)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
  • config (18-18)
test/8-config-extending.js (4)
  • requireUncached (4-4)
  • require (5-5)
  • require (7-7)
  • assert (6-6)
test/9-raw-configs.js (3)
  • requireUncached (7-7)
  • require (8-8)
  • assert (9-9)
test/x-config-js-test-transpiled.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/23-serialize.js (3)
  • requireUncached (5-5)
  • require (6-6)
  • assert (7-7)
lib/config.js (2)
  • require (8-8)
  • config (1030-1030)
test/14-node_env-load-multiple-files.js (3)
test/6-strict-mode.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/15-async-configs.js (4)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
  • config (26-26)
test/x-config-test-ts-module-exports.js (4)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
  • config (17-17)
test/makeImmutable-shared-refs.js (2)
test/19-custom-environment-variables.js (2)
  • require (5-5)
  • assert (6-6)
lib/config.js (2)
  • require (8-8)
  • util (114-114)
test/13-node_env-hostname.js (1)
lib/config.js (2)
  • require (8-8)
  • config (1030-1030)
test/22-binary.js (9)
test/11-regexp.js (4)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
  • config (15-15)
test/17-toml.js (4)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
  • config (10-10)
test/21-date.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/22-files-order.js (4)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
  • config (18-18)
test/8-config-extending.js (4)
  • requireUncached (4-4)
  • require (5-5)
  • require (7-7)
  • assert (6-6)
test/9-raw-configs.js (3)
  • requireUncached (7-7)
  • require (8-8)
  • assert (9-9)
test/x-config-js-test-transpiled.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/23-serialize.js (3)
  • requireUncached (5-5)
  • require (6-6)
  • assert (7-7)
lib/config.js (2)
  • require (8-8)
  • config (1030-1030)
test/4-array-merging.js (1)
lib/config.js (1)
  • require (8-8)
test/8-config-extending.js (2)
test/2-config-test.js (2)
  • require (4-4)
  • requireUncached (3-3)
test/13-node_env-hostname.js (1)
  • requireUncached (3-3)
test/x-config-test-ts.js (9)
test/6-strict-mode.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/20-multiple-config.js (3)
  • requireUncached (4-4)
  • require (5-5)
  • assert (6-6)
test/3-deferred-configs.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/5-getConfigSources.js (3)
  • requireUncached (4-4)
  • require (5-5)
  • assert (6-6)
test/10-gitcrypt-test.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/x-config-test-ts-module-exports.js (4)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
  • config (17-17)
test/4-array-merging.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/7-unicode-situations.js (3)
  • requireUncached (4-4)
  • require (5-5)
  • assert (6-6)
test/9-raw-configs.js (2)
  • requireUncached (7-7)
  • assert (9-9)
test/2-config-test.js (1)
lib/config.js (3)
  • require (8-8)
  • config (1030-1030)
  • moduleConfig (214-214)
test/16-custom-parser.js (4)
test/20-multiple-config.js (3)
  • requireUncached (4-4)
  • require (5-5)
  • assert (6-6)
test/3-deferred-configs.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/x-config-test-ts.js (5)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
  • config (17-17)
  • config (51-51)
lib/config.js (2)
  • require (8-8)
  • config (1030-1030)
test/6-strict-mode.js (1)
test/18-skipConfigSources.js (1)
  • require (5-5)
test/5-getConfigSources.js (9)
test/19-custom-environment-variables.js (2)
  • require (5-5)
  • requireUncached (4-4)
lib/config.js (3)
  • require (8-8)
  • sources (978-978)
  • config (1030-1030)
test/1-protected-test.js (3)
  • require (4-4)
  • requireUncached (3-3)
  • config (30-30)
test/12-node_env-override.js (3)
  • require (4-4)
  • requireUncached (3-3)
  • config (11-11)
test/makeImmutable-shared-refs.js (1)
  • require (3-3)
test/3-deferred-configs.js (2)
  • require (4-4)
  • requireUncached (3-3)
test/10-gitcrypt-test.js (2)
  • require (4-4)
  • requireUncached (3-3)
test/x-config-test-ts.js (2)
  • require (4-4)
  • requireUncached (3-3)
test/9-raw-configs.js (2)
  • require (8-8)
  • requireUncached (7-7)
test/0-util.js (2)
test/8-config-extending.js (2)
  • require (5-5)
  • require (7-7)
test/util.js (1)
  • testCases (94-116)
test/x-deferred-configs-ts.js (4)
test/6-strict-mode.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/x-config-test-ts.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/15-async-configs.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/9-raw-configs.js (3)
  • requireUncached (7-7)
  • assert (9-9)
  • CONFIG (11-11)
test/11-regexp.js (1)
lib/config.js (2)
  • require (8-8)
  • config (1030-1030)
test/9-raw-configs.js (4)
test/6-strict-mode.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/x-config-js-test-transpiled.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/23-serialize.js (4)
  • requireUncached (5-5)
  • require (6-6)
  • assert (7-7)
  • CONFIG (8-8)
lib/config.js (1)
  • require (8-8)
test/util.js (3)
lib/config.js (3)
  • require (8-8)
  • config (1030-1030)
  • util (114-114)
test/1-protected-test.js (4)
  • require (4-4)
  • requireUncached (3-3)
  • assert (5-5)
  • config (30-30)
test/2-config-test.js (16)
  • require (4-4)
  • requireUncached (3-3)
  • assert (5-5)
  • config (18-18)
  • config (61-61)
  • config (138-138)
  • config (181-181)
  • config (197-197)
  • config (213-213)
  • config (261-261)
  • config (294-294)
  • config (316-316)
  • config (344-344)
  • config (418-418)
  • config (467-467)
  • config (565-565)
test/22-files-order.js (7)
test/22-binary.js (5)
  • requireUncached (7-7)
  • require (8-8)
  • require (11-11)
  • assert (9-9)
  • config (15-15)
test/17-toml.js (4)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
  • config (10-10)
test/21-date.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/8-config-extending.js (4)
  • requireUncached (4-4)
  • require (5-5)
  • require (7-7)
  • assert (6-6)
test/9-raw-configs.js (3)
  • requireUncached (7-7)
  • require (8-8)
  • assert (9-9)
test/x-config-js-test-transpiled.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/23-serialize.js (3)
  • requireUncached (5-5)
  • require (6-6)
  • assert (7-7)
test/3-deferred-configs.js (1)
lib/config.js (1)
  • require (8-8)
test/23-serialize.js (7)
test/11-regexp.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/22-binary.js (4)
  • requireUncached (7-7)
  • require (8-8)
  • require (11-11)
  • assert (9-9)
test/21-date.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/22-files-order.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/9-raw-configs.js (4)
  • requireUncached (7-7)
  • require (8-8)
  • assert (9-9)
  • CONFIG (11-11)
test/x-config-js-test-transpiled.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
lib/config.js (1)
  • require (8-8)
test/12-node_env-override.js (4)
test/19-custom-environment-variables.js (6)
  • requireUncached (4-4)
  • require (5-5)
  • assert (6-6)
  • config (10-10)
  • config (30-30)
  • config (50-50)
test/5-getConfigSources.js (6)
  • requireUncached (4-4)
  • require (5-5)
  • assert (6-6)
  • config (22-22)
  • config (76-76)
  • config (101-101)
test/makeImmutable-shared-refs.js (2)
  • require (3-3)
  • assert (4-4)
lib/config.js (2)
  • require (8-8)
  • config (1030-1030)
🔇 Additional comments (57)
package.json (1)

60-60: Switch to node:test runner via c8 node --test looks good

Using Node’s built-in test harness aligns with the PR objective and modernizes the test execution path.

test/17-toml.js (1)

16-27: LGTM: per-test setup and strict structural assertion

beforeEach reloading of config provides isolation, and deepStrictEqual is the right choice for object arrays here.

test/8-config-extending.js (1)

22-34: LGTM: behavior preserved while migrating off Vows

The test intent (“does not throw on deep extend after attachProtoDeep”) remains intact under node:test.

test/22-binary.js (1)

13-27: LGTM: immutability enforcement verified cleanly

The throws assertion correctly validates that binary config values are immutable.

test/23-serialize.js (1)

12-17: LGTM: Serialization smoke test is correct and resilient

Using assert.doesNotThrow around JSON.stringify(CONFIG.get('level1')) is appropriate here. The migration to node:test looks clean.

test/x-config-js-test-transpiled.js (1)

26-34: LGTM: Initialization and basic assertions look good

Clearing env, loading via requireUncached, and verifying CONFIG availability and expected title are sound.

test/21-date.js (1)

21-27: LGTM: Date method invocation guarded properly

Using assert.doesNotThrow to validate toISOString() on the loaded Date object is appropriate.

test/1-protected-test.js (16)

1-6: LGTM!

The migration to Node's built-in test runner is clean and follows standard patterns.


14-14: Good preservation of test state isolation

Storing the original argv to restore later is essential for preventing test interference. This approach correctly ensures test isolation.


25-50: Well-structured test initialization with proper isolation

The beforeEach hook properly resets environment variables and reloads the config module uncached, ensuring each test runs with a clean state. This is particularly important given the singleton nature of the config module.


52-56: LGTM - Clear and focused test

The assertion correctly validates the library is loaded as an object.


58-75: Comprehensive isObject() test coverage

Good coverage of edge cases including null, undefined, arrays, and primitives. The assertions correctly validate the behavior of the isObject utility.


77-144: Thorough deep cloning tests with proper edge case coverage

Excellent test coverage for the cloneDeep functionality including:

  • Deep equality checks
  • Reference vs value copying for arrays and objects
  • Special object preservation (Date, RegExp)
  • Mutation isolation verification

The tests properly validate that modifications to cloned objects don't affect originals.


146-224: Comprehensive extendDeep() test coverage

The test suite thoroughly validates deep merging behavior including:

  • Normal extension
  • Non-object replacement
  • Deep object merging
  • Date handling
  • Partial object creation when mixing types
  • Array type preservation
  • Prototype method preservation

All assertions correctly test the expected merge semantics.


226-296: Well-structured equalsDeep() tests

Good coverage of equality comparison scenarios including:

  • Empty objects
  • Arrays
  • Object property order independence
  • Deep nested objects
  • Null/undefined edge cases
  • Size mismatches
  • Value differences

The tests properly validate deep equality semantics.


298-340: Thorough diffDeep() validation

Tests properly validate diff generation including:

  • Empty diff for identical objects
  • Deep comparison ignoring property order
  • Shallow diff extraction
  • Deep nested diff extraction

The assertions correctly check both the structure and values of generated diffs.


342-592: Comprehensive substituteDeep() test suite with excellent error handling

This test suite provides extensive coverage of variable substitution including:

  • Empty mappings
  • Non-existent variables
  • JSON content handling
  • Edge cases with undefined, null, empty strings
  • Type validation with proper error assertions for invalid leaf types (Array, Boolean, Number, null, undefined, NaN)
  • Parser error handling with descriptive error messages

The error cases are particularly well-tested using assert.throws with proper validation.


594-641: Good setPath() test coverage

Tests properly validate path setting behavior including:

  • Null value handling (ignored)
  • Top-level key creation
  • Sub-key creation
  • Parent object creation
  • Value overwriting

All assertions correctly validate the expected behavior.


643-716: Comprehensive parseFile() tests across multiple formats

Excellent coverage of file parsing for YAML, CSON, and .properties formats. Tests validate:

  • Correct object structure
  • Property values
  • Array types
  • Variable replacements in .properties files
  • Section support

The assertions properly validate parsing behavior for each format.


718-738: Basic but sufficient loadFileConfigs() test

Tests validate that the function exists, returns an object, and loads configuration correctly.


740-784: Thorough attachProtoDeep() validation

Tests properly validate:

  • Function existence
  • Original object preservation
  • Prototype method attachment at all levels (including sub-objects)
  • Prototype methods are hidden from JSON serialization

The JSON serialization test is particularly important to ensure prototype pollution doesn't affect object iteration.


786-824: Well-structured getCmdLineArg() tests with proper cleanup

Tests comprehensively validate command-line argument parsing including:

  • Basic argument retrieval
  • Missing argument handling
  • Alternative syntax support
  • First-match precedence
  • Proper restoration of original argv

The cleanup in line 819-823 properly restores the original process.argv state.


826-854: Good toObject() serialization tests

Tests validate:

  • Function existence
  • POJO conversion (not an instance of Config)
  • Serialization of current instance
  • Serialization of provided arguments

The assertions correctly validate the serialization behavior.

test/x-config-test-ts-module-exports.js (3)

1-5: LGTM - Consistent test framework migration

The migration to Node's built-in test runner follows the same pattern as other test files in this PR.


16-32: Well-structured test setup with proper environment isolation

The beforeEach hook properly:

  • Clears environment variables from previous tests
  • Sets the TypeScript config directory
  • Disables strict mode
  • Loads config uncached for test isolation

This ensures clean state between tests when working with TypeScript config files using module.exports.


34-44: Clear and focused TypeScript configuration tests

The tests validate:

  1. Library availability as an object
  2. Correct loading of TypeScript configuration values

Simple but sufficient for validating TypeScript config support with module.exports.

test/19-custom-environment-variables.js (5)

1-7: LGTM - Consistent test framework migration

The migration follows the established pattern with proper imports.


8-27: Good test for unset environment variables

The test properly validates that configuration values remain unchanged when environment variables are not set.


29-47: Important edge case: empty string environment variables

Good coverage of the edge case where environment variables are set to empty strings. The test correctly validates that empty strings don't override configuration values.


49-71: Comprehensive test for environment variable overrides

Tests properly validate that:

  • String environment variables override config values
  • JSON environment variables are parsed correctly

The JSON parsing test is particularly important for complex configuration overrides.


73-97: Good test coverage for getCustomEnvVars() utility

The test validates the utility function for retrieving custom environment variable overrides from a specific directory. Properly sets up environment and validates both string and JSON values.

test/15-async-configs.js (4)

1-5: LGTM - Clean migration to node:test

The test file properly migrates to Node's built-in test runner.


24-30: Proper async test setup

The beforeEach correctly uses async/await to resolve async configurations before each test, ensuring the config is ready for assertions.


32-44: Comprehensive async config validation

Tests properly validate:

  • AsyncConfig values are resolved by resolveAsyncConfigs
  • Regular functions remain untouched
  • this context binding in async functions

Good coverage of the async configuration feature.


46-60: Thorough testing of complex async scenarios

Tests validate:

  • Objects returned from promises are treated as single values
  • Original values are preserved
  • Local promise values override originals
  • Deferred functionality works with AsyncConfig

Excellent coverage of edge cases in async configuration resolution.

test/22-files-order.js (3)

1-5: LGTM - Consistent test framework migration

The migration follows the established pattern across the test suite.


16-31: Well-structured test setup for file order validation

The beforeEach properly sets up the multi-file configuration scenario with:

  • Custom config directory
  • NODE_ENV=test
  • NODE_APP_INSTANCE=instance

This setup allows testing the correct loading order of configuration files.


33-37: Clear validation of configuration file precedence

The test correctly validates that configuration files are loaded in the expected order:

  • prop1 from default config
  • prop2 from JSON instance config (overrides)
  • prop3 from YML instance config (overrides)

This ensures the file precedence system works as intended.

test/10-gitcrypt-test.js (1)

7-38: LGTM: Migration and core assertions look solid

The rewrite to node:test is clear, with per-test isolation via requireUncached and correct positive/negative paths for CONFIG_SKIP_GITCRYPT. Assertions are precise.

test/7-unicode-situations.js (3)

1-1: Good addition of 'use strict'

This brings the test file in line with modern JavaScript best practices.


3-6: Successful migration to Node.js built-in test framework

The imports have been properly updated to use the Node.js built-in test framework (node:test). The migration from vows to the native test runner is clean and follows the consistent pattern used across the PR.


22-42: Well-structured test organization

The migration to nested describe blocks with focused it tests improves readability and follows standard testing conventions. The test logic remains intact while adopting the more modern structure.

test/0-util.js (1)

7-7: Clean migration to Node.js built-in test framework

The imports have been properly updated from vows to the Node.js built-in test framework. Good job maintaining consistency across the test suite migration.

test/20-multiple-config.js (2)

1-2: Good addition of 'use strict'

Adding strict mode improves code quality and helps catch potential errors.


5-6: Successful migration to Node.js built-in test framework

The migration from vows to Node.js built-in test runner is clean and consistent with the overall PR objectives.

test/x-deferred-configs-ts.js (3)

1-1: Good addition of 'use strict'

This aligns with best practices and the pattern used across all migrated test files.


5-7: Clean migration to Node.js built-in test framework

The imports are properly updated from vows to Node.js built-in test runner, maintaining consistency with the PR-wide migration.


22-53: Well-structured test migration

The test structure has been successfully migrated to use nested describe blocks with individual it tests. The test logic and assertions remain unchanged, preserving the original test coverage.

test/6-strict-mode.js (1)

1-1: Good addition of 'use strict'

Consistent with the modernization effort across all test files.

test/9-raw-configs.js (1)

13-37: Migration looks good and preserves behavior.

The switch to node:test, uncached load, and assertions for raw values look correct and consistent with the suite.

test/util.js (1)

77-91: Promise detection tests are solid.

Good coverage for created, resolved, and rejected promises (with catch to avoid unhandled rejections).

test/makeImmutable-shared-refs.js (2)

73-81: Idempotency test is appropriate and valuable.

Verifies no “Cannot redefine property” on the second call—good guard against regressions.


57-60: Assertion is correct — keep the existing "Can not" message

The code in lib/config.js throws the exact message using "Can not ... runtime configuration property" so the test's regex is valid.

  • lib/config.js:381-384 — throw Error(Can not ${message} runtime configuration property: "${name}". Configuration objects are immutable unless ALLOW_CONFIG_MUTATIONS is set.)
  • test/makeImmutable-shared-refs.js:57-60 — assertion uses /Can not update runtime configuration property/ and matches the thrown message.

No change required; ignore the original suggestion.

Likely an incorrect or invalid review comment.

test/18-skipConfigSources.js (1)

8-12: Conversion to node:test and structure LGTM.

The migration reads well and keeps the semantics of the original vows tests intact.

test/3-deferred-configs.js (1)

22-62: Migration preserves deferred-config semantics.

Good use of requireUncached and environment setup; the assertions map 1:1 with prior behavior.

test/5-getConfigSources.js (1)

8-35: Good migration from Vows to node:test with isolated setup per scenario.

Use of requireUncached and per-scenario beforeEach improves test isolation. Path.delimiter for multi-dir config is also a nice cross-platform touch.

test/2-config-test.js (1)

4-5: Nice migration to node:test and requireUncached harness.

Test structure is much clearer than the old Vows topics/batches and isolates lib loads well. Solid coverage retained across formats and features.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (2)
test/1-protected-test.js (1)

839-853: This test doesn’t call toObject; assert the serialization of the provided argument.

The test name implies validating toObject on an input, but it only compares config.get('Customers') to a literal. Call toObject and compare against a canonical POJO to exercise the code under test.

-it('Returns a serialized version of whatever argument is provided', function() {
-      assert.notDeepStrictEqual(config.get('Customers'), {
-        dbHost: 'base',
-        dbName: 'from_default_json',
-        dbPort: 5999,
-        dbString: 'from_default_json:5999',
-        random: 0.08624527123827352,
-        dbPassword: 'real password',
-        dbPassword2: 'another password',
-        lang: ['en','de','es'],
-        altDbPort: 4400,
-        altDbPort1: 2209,
-        emptySub: null
-      });
-    });
+it('Returns a serialized version of whatever argument is provided', function() {
+      const customers = config.get('Customers');
+      const expected = JSON.parse(JSON.stringify(customers));
+      assert.deepStrictEqual(config.util.toObject(customers), expected);
+    });
test/10-gitcrypt-test.js (1)

41-56: Ensure the error-path test uses the encrypted fixtures (missing NODE_CONFIG_DIR).

This block doesn’t set NODE_CONFIG_DIR, so it may load default configs and not exercise the encrypted files. Set the directory and clear interfering overrides before requiring config.

   describe('initialization with encrypted files without CONFIG_SKIP_GITCRYPT', function() {
     it('An exception is thrown if CONFIG_SKIP_GITCRYPT is not set', function() {
       // Hard-code $NODE_ENV=encrypted for testing
       process.env.NODE_ENV='encrypted';
 
+      // Ensure we use the encrypted test fixtures and no other overrides interfere
+      process.env.NODE_CONFIG_DIR = __dirname + '/10-config';
+      delete process.env.NODE_APP_INSTANCE;
+      delete process.env["NODE_CONFIG"];
+      process.argv = [];
+
       // Test Environment Variable Substitution
       process.env.CONFIG_SKIP_GITCRYPT = 0;
       delete process.env["CONFIG_SKIP_GITCRYPT"];
 
       assert.throws(
         function () {
           let config = requireUncached(__dirname + '/../lib/config');
         },
         /Cannot parse config file/
       );
     });
   });
♻️ Duplicate comments (9)
test/5-getConfigSources.js (2)

20-20: Fix CLI argument quoting for --NODE_CONFIG (currently a parse error).

Passing '{}' preserves quotes and will cause JSON parsing to fail. This means the CLI override isn’t actually being tested.

-      process.argv = ["node","path/to/some.js","--NODE_CONFIG='{}'"]; //TODO: This is a parse error so not testing the right thing
+      process.argv = ["node", "path/to/some.js", "--NODE_CONFIG={}"];

31-34: Indexing bug: verify the last two overrides instead of fixed positions.

The env/CLI entries should be at the end, not at fixed indices. Using tail indices is more robust.

-    it("The environment variable and command line args are the last two overrides", function () {
-      assert.equal(sources[2].name,'$NODE_CONFIG');
-      assert.equal(sources[3].name,"--NODE_CONFIG argument");
-    });
+    it("The environment variable and command line args are the last two overrides", function () {
+      assert.equal(sources[sources.length - 2].name, '$NODE_CONFIG');
+      assert.equal(sources[sources.length - 1].name, '--NODE_CONFIG argument');
+    });
test/2-config-test.js (5)

183-185: Initialize $NODE_CONFIG explicitly in this suite to remove cross-suite dependency.

This suite currently relies on env state set elsewhere. Set NODE_CONFIG and clear argv locally before requiring config.

-      config = requireUncached(__dirname + '/../lib/config');
+      process.env.NODE_CONFIG = '{"EnvOverride":{"parm3":"overridden from $NODE_CONFIG","parm4":100}}';
+      process.argv = [];
+      config = requireUncached(__dirname + '/../lib/config');

199-201: Initialize --NODE_CONFIG via argv explicitly and clear conflicting env.

Provide the CLI arg directly and ensure $NODE_CONFIG isn’t also set for deterministic behavior.

-      config = requireUncached(__dirname + '/../lib/config');
+      delete process.env.NODE_CONFIG;
+      process.argv = ['node', 'test.js', '--NODE_CONFIG={"EnvOverride":{"parm5":"overridden from --NODE_CONFIG","parm6":101}}'];
+      config = requireUncached(__dirname + '/../lib/config');

215-217: Set custom env vars locally in this suite for determinism.

Avoid depending on env set by other suites. Define required vars here so the suite is order-independent.

-      config = requireUncached(__dirname + '/../lib/config');
+      process.env.CUSTOM_JSON_ENVIRONMENT_VAR = 'CUSTOM VALUE FROM JSON ENV MAPPING';
+      process.env.CUSTOM_BOOLEAN_TRUE_ENVIRONMENT_VAR = 'true';
+      process.env.CUSTOM_BOOLEAN_FALSE_ENVIRONMENT_VAR = 'false';
+      process.env.CUSTOM_BOOLEAN_ERROR_ENVIRONMENT_VAR = 'notProperBoolean';
+      process.env.CUSTOM_NUMBER_INTEGER_ENVIRONMENT_VAR = '1001';
+      process.env.CUSTOM_NUMBER_FLOAT_ENVIRONMENT_VAR = '3.14';
+      process.env.CUSTOM_NUMBER_EMPTY_ENVIRONMENT_VAR = '';
+      process.env.CUSTOM_NUMBER_STRING_ENVIRONMENT_VAR = 'String';
+      process.argv = [];
+      config = requireUncached(__dirname + '/../lib/config');

578-580: Same argv pollution here: assign argv instead of pushing.

Mirror the earlier fix to avoid accumulating CLI flags across tests.

-      process.argv.push('--NODE_CONFIG={"EnvOverride":{"parm5":"overridden from --NODE_CONFIG","parm6":101}}');
+      process.argv = ['node', 'test.js', '--NODE_CONFIG={"EnvOverride":{"parm5":"overridden from --NODE_CONFIG","parm6":101}}'];

569-570: Windows compatibility: use path.delimiter when composing NODE_CONFIG_DIR.

Hardcoding “:” breaks on Windows. Use the platform delimiter.

-      process.env.NODE_CONFIG_DIR = __dirname + '/config:' + __dirname + '/x-config';
+      process.env.NODE_CONFIG_DIR = [__dirname + '/config', __dirname + '/x-config'].join(require('path').delimiter);
test/12-node_env-override.js (2)

25-27: Fix test bug: parameter shadows suite’s config variable (asserting the TestContext).

The parameter named config shadows the suite variable; the assertion is not validating the loaded config.

-    it('Library is available', function (config) {
-      assert.strictEqual(typeof config, 'object');
-    });
+    it('Library is available', function () {
+      assert.strictEqual(typeof config, 'object');
+    });

79-82: Fix teardown: delete NODE_CONFIG_ENV, not NODE_ENV.

This suite sets NODE_CONFIG_ENV only. Deleting NODE_ENV here is a no-op and leaks NODE_CONFIG_ENV into following tests.

-    after(function () {
-      delete process.env.NODE_ENV;
-    });
+    after(function () {
+      delete process.env.NODE_CONFIG_ENV;
+    });
🧹 Nitpick comments (22)
test/8-config-extending.js (3)

5-5: Remove unused beforeEach import

beforeEach isn’t used in this file.

-const { describe, it, beforeEach } = require('node:test');
+const { describe, it } = require('node:test');

25-26: Avoid redundant env reassignment and use path.join for portability

  • The reassignment of NODE_CONFIG_DIR inside the test is redundant given the top-level setup.
  • Prefer path.join over manual concatenation for cross-platform correctness.
-      process.env.NODE_CONFIG_DIR = __dirname + '/8-config';
-      var base_config = require(process.env.NODE_CONFIG_DIR + path.sep + 'base-config.json');
+      const base_config = require(path.join(process.env.NODE_CONFIG_DIR, 'base-config.json'));

29-31: Remove unused variable in assertion block

result is unused. Just invoke Util.extendDeep to assert it doesn’t throw.

-      assert.doesNotThrow(function () {
-          let result = Util.extendDeep(base_config, CONFIG);
-      }, 'Extending a configuration with another configuration has an error');
+      assert.doesNotThrow(function () {
+          Util.extendDeep(base_config, CONFIG);
+      }, 'Extending a configuration with another configuration has an error');
test/20-multiple-config.js (2)

5-5: Import afterEach and use the hooks to restore NODE_CONFIG_DIR

You already import beforeEach but don’t use it. Add afterEach and use both to snapshot/restore NODE_CONFIG_DIR to minimize cross-file interference during parallel execution.

-const { describe, it, beforeEach } = require('node:test');
+const { describe, it, beforeEach, afterEach } = require('node:test');

8-9: Snapshot/restore env per scenario to reduce flakiness

Wrap scenarios with beforeEach/afterEach to restore NODE_CONFIG_DIR after each test run.

 describe('Tests for multiple config', function() {
+  let originalConfigDir;
+  beforeEach(() => {
+    originalConfigDir = process.env.NODE_CONFIG_DIR;
+  });
+  afterEach(() => {
+    process.env.NODE_CONFIG_DIR = originalConfigDir;
+  });
test/4-array-merging.js (3)

4-4: Remove unused before/beforeEach imports

They’re not used in this file.

-const { describe, it, before, beforeEach } = require('node:test');
+const { describe, it } = require('node:test');

27-27: Tighten the suite title

Minor wording cleanup.

-  describe('Array merging tests Tests', function() {
+  describe('Array merging tests', function() {

29-29: Prefer deepStrictEqual over deepEqual

Use the strict variant for clearer intent in assertions.

-        assert.deepEqual(CONFIG.arrayMerging.emptyArray, ['not empty anymore']);
+        assert.deepStrictEqual(CONFIG.arrayMerging.emptyArray, ['not empty anymore']);
-        assert.deepEqual(CONFIG.arrayMerging.oneItem, ['replaced']);
+        assert.deepStrictEqual(CONFIG.arrayMerging.oneItem, ['replaced']);
-        assert.deepEqual(CONFIG.arrayMerging.removeMe, []);
+        assert.deepStrictEqual(CONFIG.arrayMerging.removeMe, []);

Also applies to: 33-33, 37-37

test/9-raw-configs.js (3)

8-8: Remove unused before/beforeEach imports

They’re not used in this file.

-const { describe, it, before, beforeEach } = require('node:test');
+const { describe, it } = require('node:test');

16-16: Use strictEqual for reference equality checks

For object/stream identity checks, strictEqual communicates the intent explicitly. While == and === behave the same for objects, strictEqual is clearer.

-      assert.equal(CONFIG.get('circularReference'), process.stdout);
+      assert.strictEqual(CONFIG.get('circularReference'), process.stdout);
-      assert.equal(CONFIG.get('innerRaw').innerCircularReference, process.stdout);
-      assert.equal(CONFIG.get('innerRaw.innerCircularReference'), process.stdout);
+      assert.strictEqual(CONFIG.get('innerRaw').innerCircularReference, process.stdout);
+      assert.strictEqual(CONFIG.get('innerRaw.innerCircularReference'), process.stdout);
-      assert.equal(CONFIG.get('nestedRaw').nested.test, process.stdout);
-      assert.equal(CONFIG.get('nestedRaw.nested').test, process.stdout);
-      assert.equal(CONFIG.get('nestedRaw.nested.test'), process.stdout);
+      assert.strictEqual(CONFIG.get('nestedRaw').nested.test, process.stdout);
+      assert.strictEqual(CONFIG.get('nestedRaw.nested').test, process.stdout);
+      assert.strictEqual(CONFIG.get('nestedRaw.nested.test'), process.stdout);

Also applies to: 22-23, 27-29


11-12: Note on top-level load and env mutations

CONFIG is loaded at module import time based on top-level env setup. This can interact poorly with parallel test execution in Node’s test runner. If you see flakiness on Node 22.18+, consider moving env setup + requireUncached into a beforeEach and restoring afterward.

test/x-config-test-ts-module-exports.js (1)

21-24: Clear env by deleting keys rather than setting empty strings

Using empty strings can carry ambiguous semantics in downstream code. Deleting is closer to “unset.”

-    process.env.NODE_APP_INSTANCE = '';
-    process.env.NODE_ENV = '';
-    process.env.NODE_CONFIG = '';
+    delete process.env.NODE_APP_INSTANCE;
+    delete process.env.NODE_ENV;
+    delete process.env.NODE_CONFIG;
test/1-protected-test.js (2)

14-14: Avoid mutating the saved argv reference; copy it instead to prevent coupling.

Storing process.argv by reference means later mutations (push/assign) also mutate argvOrg. Save a shallow copy so you can reliably restore it later.

-const argvOrg = process.argv;
+const argvOrg = process.argv.slice();

819-823: Restore argv robustly using deep equality and a fresh copy.

This test currently checks identity and assigns the same array reference back. Prefer deepStrictEqual and assign a copy to avoid reusing the same array object.

-it('Revert original process arguments', function() {
-        assert.notEqual(process.argv, argvOrg);
-        process.argv = argvOrg;
-        assert.equal(process.argv, argvOrg);
-    });
+it('Revert original process arguments', function() {
+        assert.notDeepStrictEqual(process.argv, argvOrg);
+        process.argv = argvOrg.slice();
+        assert.deepStrictEqual(process.argv, argvOrg);
+    });
test/13-node_env-hostname.js (2)

35-37: Prefer strict assertions for string equality.

Use strictEqual to avoid type coercion in assertions.

-    it('HOSTNAME env variable is the winner', function() {
-      assert.equal(config.util.getEnv('HOSTNAME'), 'some.machine');
-    });
+    it('HOSTNAME env variable is the winner', function() {
+      assert.strictEqual(config.util.getEnv('HOSTNAME'), 'some.machine');
+    });

51-53: Prefer strict assertions for string equality.

Use strictEqual to avoid type coercion in assertions.

-    it('HOST env variable is the winner', function() {
-      assert.strictEqual(config.util.getEnv('HOSTNAME'), 'other.machine');
-    });
+    it('HOST env variable is the winner', function() {
+      assert.strictEqual(config.util.getEnv('HOSTNAME'), 'other.machine');
+    });
test/x-deferred-configs-ts.js (2)

20-20: Use const instead of var for module-level CONFIG.

The reference isn’t reassigned; prefer const.

-var CONFIG = requireUncached(__dirname + '/../lib/config');
+const CONFIG = requireUncached(__dirname + '/../lib/config');

49-51: Prefer strict equality against undefined.

Use strictEqual to avoid coercion.

-    it("second defer function return original value.", function () {
-      assert.equal(CONFIG.original.deferredOriginal, undefined);
-    });
+    it("second defer function return original value.", function () {
+      assert.strictEqual(CONFIG.original.deferredOriginal, undefined);
+    });
test/5-getConfigSources.js (2)

27-29: Update test description to match the expected count.

The assertion expects 6 entries; fix the description to avoid confusion.

-    it('Two files plus NODE_CONFIG in env and as command line args should result in four entries', function() {
+    it('Two config dirs plus NODE_CONFIG in env and as command line args should result in six entries', function() {
       assert.equal(sources.length,6);
     });

105-107: Update test description to match the expected count.

The test asserts 4 entries; fix the description to say four.

-  it('Three files should result in 3 entries', function() {
+  it('Four files should result in 4 entries', function() {
     assert.equal(sources.length,4);
   });
test/2-config-test.js (2)

31-33: Avoid argv pollution: assign argv instead of pushing.

Pushing accumulates flags across tests and can create order-dependent failures. Set argv deterministically for this suite.

-      process.argv.push('--NODE_CONFIG={"EnvOverride":{"parm5":"overridden from --NODE_CONFIG","parm6":101}}');
+      process.argv = ['node', 'test.js', '--NODE_CONFIG={"EnvOverride":{"parm5":"overridden from --NODE_CONFIG","parm6":101}}'];

43-45: Prefer string literals for process.env values for clarity.

process.env coerces non-strings to strings anyway. Using strings makes the intent explicit and avoids surprises with type coercion.

-      process.env.CUSTOM_NUMBER_INTEGER_ENVIRONMENT_VAR = 1001;
-      process.env.CUSTOM_NUMBER_FLOAT_ENVIRONMENT_VAR = 3.14;
+      process.env.CUSTOM_NUMBER_INTEGER_ENVIRONMENT_VAR = '1001';
+      process.env.CUSTOM_NUMBER_FLOAT_ENVIRONMENT_VAR = '3.14';
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 95723d2 and bf0c6b6.

📒 Files selected for processing (32)
  • package.json (1 hunks)
  • test/0-util.js (1 hunks)
  • test/1-protected-test.js (8 hunks)
  • test/10-gitcrypt-test.js (3 hunks)
  • test/11-regexp.js (1 hunks)
  • test/12-node_env-override.js (1 hunks)
  • test/13-node_env-hostname.js (1 hunks)
  • test/14-node_env-load-multiple-files.js (1 hunks)
  • test/15-async-configs.js (2 hunks)
  • test/16-custom-parser.js (1 hunks)
  • test/17-toml.js (2 hunks)
  • test/18-skipConfigSources.js (1 hunks)
  • test/19-custom-environment-variables.js (1 hunks)
  • test/2-config-test.js (4 hunks)
  • test/20-multiple-config.js (6 hunks)
  • test/21-date.js (1 hunks)
  • test/22-binary.js (1 hunks)
  • test/22-files-order.js (3 hunks)
  • test/23-serialize.js (1 hunks)
  • test/3-deferred-configs.js (2 hunks)
  • test/4-array-merging.js (2 hunks)
  • test/5-getConfigSources.js (3 hunks)
  • test/6-strict-mode.js (2 hunks)
  • test/7-unicode-situations.js (2 hunks)
  • test/8-config-extending.js (2 hunks)
  • test/9-raw-configs.js (1 hunks)
  • test/makeImmutable-shared-refs.js (1 hunks)
  • test/util.js (1 hunks)
  • test/x-config-js-test-transpiled.js (2 hunks)
  • test/x-config-test-ts-module-exports.js (2 hunks)
  • test/x-config-test-ts.js (2 hunks)
  • test/x-deferred-configs-ts.js (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (20)
  • test/23-serialize.js
  • test/19-custom-environment-variables.js
  • test/17-toml.js
  • test/16-custom-parser.js
  • package.json
  • test/0-util.js
  • test/18-skipConfigSources.js
  • test/6-strict-mode.js
  • test/x-config-js-test-transpiled.js
  • test/7-unicode-situations.js
  • test/3-deferred-configs.js
  • test/makeImmutable-shared-refs.js
  • test/x-config-test-ts.js
  • test/14-node_env-load-multiple-files.js
  • test/22-files-order.js
  • test/15-async-configs.js
  • test/11-regexp.js
  • test/util.js
  • test/21-date.js
  • test/22-binary.js
🧰 Additional context used
🧬 Code Graph Analysis (10)
test/13-node_env-hostname.js (4)
test/3-deferred-configs.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/1-protected-test.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/8-config-extending.js (4)
  • requireUncached (4-4)
  • require (5-5)
  • require (7-7)
  • assert (6-6)
lib/config.js (2)
  • require (8-8)
  • config (1030-1030)
test/8-config-extending.js (2)
lib/config.js (2)
  • path (213-213)
  • require (8-8)
test/0-util.js (5)
  • require (7-7)
  • result (413-413)
  • result (418-418)
  • result (500-500)
  • result (510-510)
test/4-array-merging.js (3)
test/6-strict-mode.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/2-config-test.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/9-raw-configs.js (4)
  • requireUncached (7-7)
  • require (8-8)
  • assert (9-9)
  • CONFIG (11-11)
test/1-protected-test.js (2)
test/3-deferred-configs.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
lib/config.js (3)
  • require (8-8)
  • config (1030-1030)
  • diff (815-815)
test/20-multiple-config.js (1)
test/16-custom-parser.js (3)
  • require (4-4)
  • requireUncached (3-3)
  • assert (5-5)
test/5-getConfigSources.js (15)
test/19-custom-environment-variables.js (4)
  • Path (3-3)
  • require (5-5)
  • requireUncached (4-4)
  • assert (6-6)
lib/config.js (4)
  • Path (9-9)
  • require (8-8)
  • sources (978-978)
  • config (1030-1030)
test/6-strict-mode.js (3)
  • require (4-4)
  • requireUncached (3-3)
  • assert (5-5)
test/12-node_env-override.js (4)
  • require (4-4)
  • requireUncached (3-3)
  • assert (5-5)
  • config (11-11)
test/2-config-test.js (16)
  • require (4-4)
  • requireUncached (3-3)
  • assert (5-5)
  • config (18-18)
  • config (61-61)
  • config (138-138)
  • config (181-181)
  • config (197-197)
  • config (213-213)
  • config (261-261)
  • config (294-294)
  • config (316-316)
  • config (344-344)
  • config (418-418)
  • config (467-467)
  • config (565-565)
test/makeImmutable-shared-refs.js (2)
  • require (3-3)
  • assert (4-4)
test/3-deferred-configs.js (3)
  • require (4-4)
  • requireUncached (3-3)
  • assert (5-5)
test/10-gitcrypt-test.js (3)
  • require (4-4)
  • requireUncached (3-3)
  • assert (5-5)
test/1-protected-test.js (4)
  • require (4-4)
  • requireUncached (3-3)
  • assert (5-5)
  • config (30-30)
test/x-config-test-ts.js (3)
  • require (4-4)
  • requireUncached (3-3)
  • assert (5-5)
test/4-array-merging.js (3)
  • require (4-4)
  • requireUncached (3-3)
  • assert (5-5)
test/7-unicode-situations.js (3)
  • require (5-5)
  • requireUncached (4-4)
  • assert (6-6)
test/x-deferred-configs-ts.js (3)
  • require (6-6)
  • requireUncached (5-5)
  • assert (7-7)
test/8-config-extending.js (4)
  • require (5-5)
  • require (7-7)
  • requireUncached (4-4)
  • assert (6-6)
test/9-raw-configs.js (3)
  • require (8-8)
  • requireUncached (7-7)
  • assert (9-9)
test/2-config-test.js (1)
test/3-deferred-configs.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/12-node_env-override.js (4)
test/5-getConfigSources.js (6)
  • requireUncached (4-4)
  • require (5-5)
  • assert (6-6)
  • config (22-22)
  • config (76-76)
  • config (101-101)
test/19-custom-environment-variables.js (6)
  • requireUncached (4-4)
  • require (5-5)
  • assert (6-6)
  • config (10-10)
  • config (30-30)
  • config (50-50)
test/makeImmutable-shared-refs.js (2)
  • require (3-3)
  • assert (4-4)
lib/config.js (2)
  • require (8-8)
  • config (1030-1030)
test/x-config-test-ts-module-exports.js (6)
test/10-gitcrypt-test.js (5)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
  • config (9-9)
  • config (51-51)
test/14-node_env-load-multiple-files.js (6)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
  • config (9-9)
  • config (30-30)
  • config (47-47)
test/x-config-test-ts.js (5)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
  • config (17-17)
  • config (51-51)
test/15-async-configs.js (4)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
  • config (26-26)
test/x-deferred-configs-ts.js (3)
  • requireUncached (5-5)
  • require (6-6)
  • assert (7-7)
lib/config.js (2)
  • require (8-8)
  • config (1030-1030)
test/x-deferred-configs-ts.js (2)
test/2-config-test.js (1)
  • require (4-4)
lib/config.js (1)
  • require (8-8)
🔇 Additional comments (4)
test/20-multiple-config.js (1)

9-19: LGTM on the migration and scenarios

The scenarios (relative, absolute, mixed, and empty string) correctly cover the previous Vows intent, and requireUncached usage within assert.doesNotThrow is appropriate.

Also applies to: 21-31, 33-42, 44-53, 55-65

test/x-config-test-ts-module-exports.js (1)

16-45: Overall migration looks solid

Good use of beforeEach for per-test setup, requireUncached for isolation, and strict assertions. This aligns with the suite-wide move off Vows.

test/12-node_env-override.js (2)

3-5: LGTM on harness migration.

Switch to node:test and requireUncached is clean and consistent with the rest of the PR.


104-108: LGTM on final teardown.

Cleaning NODE_CONFIG_DIR, NODE_CONFIG_ENV, and NODE_ENV at the end is good hygiene.

@jdmarshall jdmarshall force-pushed the avow branch 2 times, most recently from 423555a to d7203e2 Compare August 15, 2025 05:27
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🔭 Outside diff range comments (5)
test/20-multiple-config.js (5)

8-19: Assertions run during describe() registration instead of in tests

The assert.doesNotThrow call runs at suite definition time, not inside an it(), so it won't be reported as a proper test. Wrap it in an it() and restore env after.

 describe('Adding multiple relative configuration paths should work without error', function () {
-  process.env.NODE_CONFIG_DIR =  [
-    './test/20-config',
-    './test/20-extra-config',
-  ].join(path.delimiter)
-
-  assert.doesNotThrow(function () {
-    const CONFIG = requireUncached(__dirname + '/../lib/config');
-  }, 'Adding multiple relative configuration paths has an error');
+  it('does not throw when using multiple relative paths', function () {
+    const prev = process.env.NODE_CONFIG_DIR;
+    try {
+      process.env.NODE_CONFIG_DIR = [
+        './test/20-config',
+        './test/20-extra-config',
+      ].join(path.delimiter);
+      assert.doesNotThrow(function () {
+        const CONFIG = requireUncached(__dirname + '/../lib/config');
+      }, 'Adding multiple relative configuration paths has an error');
+    } finally {
+      if (prev === undefined) delete process.env.NODE_CONFIG_DIR;
+      else process.env.NODE_CONFIG_DIR = prev;
+    }
+  });
 });

21-31: Same issue: wrap in it() and restore env (absolute paths case)

This block has the same problem as above.

 describe('Adding multiple absolute configuration paths should work without error', function () {
-  process.env.NODE_CONFIG_DIR =  [
-    __dirname + '/20-config',
-    __dirname + '/20-extra-config',
-  ].join(path.delimiter)
-
-  assert.doesNotThrow(function () {
-    const CONFIG = requireUncached(__dirname + '/../lib/config');
-  }, 'Adding multiple absolute configuration paths has an error');
+  it('does not throw when using multiple absolute paths', function () {
+    const prev = process.env.NODE_CONFIG_DIR;
+    try {
+      process.env.NODE_CONFIG_DIR = [
+        __dirname + '/20-config',
+        __dirname + '/20-extra-config',
+      ].join(path.delimiter);
+      assert.doesNotThrow(function () {
+        const CONFIG = requireUncached(__dirname + '/../lib/config');
+      }, 'Adding multiple absolute configuration paths has an error');
+    } finally {
+      if (prev === undefined) delete process.env.NODE_CONFIG_DIR;
+      else process.env.NODE_CONFIG_DIR = prev;
+    }
+  });
 });

33-42: Same issue: wrap in it() and restore env (absolute + relative paths)

This block has the same problem as above.

 describe('Adding one absolute and one relative configuration paths should work without error', function () {
-  process.env.NODE_CONFIG_DIR =  [
-    __dirname + '/20-config',
-    './test/20-extra-config',
-  ].join(path.delimiter)
-
-  assert.doesNotThrow(function () {
-    const CONFIG = requireUncached(__dirname + '/../lib/config');
-  }, 'Adding one absolute and one relative configuration paths has an error');
+  it('does not throw when using absolute + relative paths', function () {
+    const prev = process.env.NODE_CONFIG_DIR;
+    try {
+      process.env.NODE_CONFIG_DIR = [
+        __dirname + '/20-config',
+        './test/20-extra-config',
+      ].join(path.delimiter);
+      assert.doesNotThrow(function () {
+        const CONFIG = requireUncached(__dirname + '/../lib/config');
+      }, 'Adding one absolute and one relative configuration paths has an error');
+    } finally {
+      if (prev === undefined) delete process.env.NODE_CONFIG_DIR;
+      else process.env.NODE_CONFIG_DIR = prev;
+    }
+  });
 });

44-53: Same issue: wrap in it() and restore env (relative + absolute paths)

This block has the same problem as above.

 describe('Adding one relative and one absolute configuration paths should work without error', function () {
-  process.env.NODE_CONFIG_DIR =  [
-    './test/20-config',
-    __dirname + '/20-extra-config',
-  ].join(path.delimiter)
-
-  assert.doesNotThrow(function () {
-    const CONFIG = requireUncached(__dirname + '/../lib/config');
-  }, 'Adding one relative and one absolute configuration paths has an error');
+  it('does not throw when using relative + absolute paths', function () {
+    const prev = process.env.NODE_CONFIG_DIR;
+    try {
+      process.env.NODE_CONFIG_DIR = [
+        './test/20-config',
+        __dirname + '/20-extra-config',
+      ].join(path.delimiter);
+      assert.doesNotThrow(function () {
+        const CONFIG = requireUncached(__dirname + '/../lib/config');
+      }, 'Adding one relative and one absolute configuration paths has an error');
+    } finally {
+      if (prev === undefined) delete process.env.NODE_CONFIG_DIR;
+      else process.env.NODE_CONFIG_DIR = prev;
+    }
+  });
 });

55-64: Same issue: wrap in it() and restore env (empty string case)

This block has the same problem as above.

 describe('Empty string should not blow up', function () {
-  process.env.NODE_CONFIG_DIR =  [
-    './test/20-config',
-    ''
-  ].join(path.delimiter)
-
-  assert.doesNotThrow(function () {
-    const CONFIG = requireUncached(__dirname + '/../lib/config');
-  }, 'Adding an empty string does not result in an error');
+  it('does not throw when one path is empty', function () {
+    const prev = process.env.NODE_CONFIG_DIR;
+    try {
+      process.env.NODE_CONFIG_DIR = [
+        './test/20-config',
+        ''
+      ].join(path.delimiter);
+      assert.doesNotThrow(function () {
+        const CONFIG = requireUncached(__dirname + '/../lib/config');
+      }, 'Adding an empty string does not result in an error');
+    } finally {
+      if (prev === undefined) delete process.env.NODE_CONFIG_DIR;
+      else process.env.NODE_CONFIG_DIR = prev;
+    }
+  });
 });
♻️ Duplicate comments (5)
test/2-config-test.js (5)

215-217: Define custom env vars locally for determinism

Avoid relying on values from the “Library initialization” block; set only what this suite needs.

   beforeEach(function() {
-      config = requireUncached(__dirname + '/../lib/config');
+      process.env.CUSTOM_JSON_ENVIRONMENT_VAR = 'CUSTOM VALUE FROM JSON ENV MAPPING';
+      process.env.CUSTOM_BOOLEAN_TRUE_ENVIRONMENT_VAR = 'true';
+      process.env.CUSTOM_BOOLEAN_FALSE_ENVIRONMENT_VAR = 'false';
+      process.env.CUSTOM_BOOLEAN_ERROR_ENVIRONMENT_VAR = 'notProperBoolean';
+      process.env.CUSTOM_NUMBER_INTEGER_ENVIRONMENT_VAR = '1001';
+      process.env.CUSTOM_NUMBER_FLOAT_ENVIRONMENT_VAR = '3.14';
+      process.env.CUSTOM_NUMBER_EMPTY_ENVIRONMENT_VAR = '';
+      process.env.CUSTOM_NUMBER_STRING_ENVIRONMENT_VAR = 'String';
+      process.argv = [process.argv[0], process.argv[1]];
+      config = requireUncached(__dirname + '/../lib/config');
   });

183-201: Make this suite self-contained: initialize NODE_CONFIG/argv locally

These tests currently rely on prior suites having set NODE_CONFIG/argv. Initialize them here to remove order dependence and flakiness.

   beforeEach(function() {
-      config = requireUncached(__dirname + '/../lib/config');
+      // isolate from other suites
+      delete process.env.NODE_APP_INSTANCE;
+      process.env.NODE_CONFIG='{"EnvOverride":{"parm3":"overridden from $NODE_CONFIG","parm4":100}}';
+      process.argv = [process.argv[0], process.argv[1]];
+      config = requireUncached(__dirname + '/../lib/config');
   });

Optionally add an afterEach to restore original env/argv.


199-201: Initialize CLI override explicitly and clear conflicting env

Ensure this suite covers CLI precedence deterministically by setting argv and unsetting NODE_CONFIG before require.

   beforeEach(function() {
-      config = requireUncached(__dirname + '/../lib/config');
+      delete process.env.NODE_CONFIG;
+      process.argv = [process.argv[0], process.argv[1], '--NODE_CONFIG={"EnvOverride":{"parm5":"overridden from --NODE_CONFIG","parm6":101}}'];
+      config = requireUncached(__dirname + '/../lib/config');
   });

569-570: Windows compatibility: use path.delimiter when composing NODE_CONFIG_DIR

Hardcoding ":" breaks on Windows. Join with the platform-specific delimiter.

-      process.env.NODE_CONFIG_DIR = __dirname + '/config:' + __dirname + '/x-config';
+      process.env.NODE_CONFIG_DIR = [__dirname + '/config', __dirname + '/x-config'].join(require('path').delimiter);

578-580: Avoid accumulating CLI flags: assign argv instead of pushing

push will append on each beforeEach call, leaking state across tests. Reset argv explicitly.

-      process.argv.push('--NODE_CONFIG={"EnvOverride":{"parm5":"overridden from --NODE_CONFIG","parm6":101}}');
+      process.argv = [process.argv[0], process.argv[1], '--NODE_CONFIG={"EnvOverride":{"parm5":"overridden from --NODE_CONFIG","parm6":101}}'];
🧹 Nitpick comments (9)
test/17-toml.js (1)

4-4: Prevent env leakage: restore NODE_CONFIG_DIR after each test

The suite sets NODE_CONFIG_DIR in beforeEach but never restores it. Since node:test runs in a single process, this can bleed into other files. Add afterEach to restore the previous value.

-const { describe, it, beforeEach } = require('node:test');
+const { describe, it, beforeEach, afterEach } = require('node:test');
@@
-    let config;
-    beforeEach(function() {
-      process.env.NODE_CONFIG_DIR = __dirname + '/17-config';
-      config = requireUncached(__dirname + '/../lib/config');
-    });
+    let config;
+    let prevConfigDir;
+    beforeEach(function() {
+      prevConfigDir = process.env.NODE_CONFIG_DIR;
+      process.env.NODE_CONFIG_DIR = __dirname + '/17-config';
+      config = requireUncached(__dirname + '/../lib/config');
+    });
+    afterEach(function() {
+      if (prevConfigDir === undefined) {
+        delete process.env.NODE_CONFIG_DIR;
+      } else {
+        process.env.NODE_CONFIG_DIR = prevConfigDir;
+      }
+    });

Also applies to: 10-14

test/0-util.js (2)

71-73: Correct malformed array element in isPromise() negative test cases

“1.1” followed by “- 1,” becomes a single expression (1.1 - 1). The intention appears to be two separate elements: 1.1 and -1. Fixing improves clarity and avoids accidental arithmetic.

-        1.1
-        - 1,
+        1.1,
+        -1,

245-249: Consider deepStrictEqual over deepEqual for type-safe comparisons

Many assertions use deepEqual; switching to deepStrictEqual avoids loose equality pitfalls and aligns with the modern assert style you’re using elsewhere in this PR. Not mandatory if types are already controlled, but improves robustness.

Also applies to: 253-254, 266-270, 298-299, 305-306, 313-314, 321-322, 333-334, 343-345, 353-354, 678-679, 693-695, 827-829, 834-835, 885-889, 908-913, 923-931, 951-955

test/13-node_env-hostname.js (1)

24-34: Optionally clean up HOST/HOSTNAME after each test

Minor but safer: add afterEach blocks to remove or restore env vars to reduce risk of leakage across files.

-const { describe, it, before, beforeEach } = require('node:test');
+const { describe, it, before, beforeEach, afterEach } = require('node:test');
@@
   describe('When HOSTNAME env is set', function() {
     let config;
 
     beforeEach(function() {
       // Test HOST and HOSTNAME
       delete process.env.HOST;
       process.env.HOSTNAME = 'some.machine';
 
       config = requireUncached(__dirname + '/../lib/config');
     });
+    afterEach(function () {
+      delete process.env.HOSTNAME;
+    });
@@
   describe('When HOST env is set', function() {
     let config;
 
     beforeEach(function() {
       // Test HOST and HOSTNAME
       delete process.env.HOSTNAME;
       process.env.HOST = 'other.machine';
 
       config = requireUncached(__dirname + '/../lib/config');
     });
+    afterEach(function () {
+      delete process.env.HOST;
+    });

Also applies to: 40-50

test/x-deferred-configs-ts.js (1)

28-29: Prefer strict assertion variants (strictEqual/deepStrictEqual)

To match the rest of the migration and avoid loose equality, switch to strict forms.

-        assert.equal(CONFIG.welcomeEmail.subject, 'Welcome to New Instance!');
+        assert.strictEqual(CONFIG.welcomeEmail.subject, 'Welcome to New Instance!');
@@
-        assert.equal(CONFIG.welcomeEmail.aFunc(), 'Still just a function.');
+        assert.strictEqual(CONFIG.welcomeEmail.aFunc(), 'Still just a function.');
@@
-        assert.equal(CONFIG.welcomeEmail.justThis, 'Welcome to this New Instance!');
+        assert.strictEqual(CONFIG.welcomeEmail.justThis, 'Welcome to this New Instance!');
@@
-      assert.deepEqual(CONFIG.get('map.centerPoint'), { lat: 3, lon: 4 });
+      assert.deepStrictEqual(CONFIG.get('map.centerPoint'), { lat: 3, lon: 4 });
@@
-      assert.equal(CONFIG.original.original, 'an original value');
+      assert.strictEqual(CONFIG.original.original, 'an original value');
@@
-      assert.equal(CONFIG.original.deferredOriginal, undefined);
+      assert.strictEqual(CONFIG.original.deferredOriginal, undefined);

Also applies to: 32-34, 37-39, 41-43, 45-47, 49-51

test/6-strict-mode.js (1)

95-99: Remove unused variable assignment to avoid lint noise

config is not used; require for side effects is sufficient.

-        let config = requireUncached(__dirname + '/../lib/config');
+        requireUncached(__dirname + '/../lib/config');
test/util.js (1)

21-41: Stabilize initParam tests by restoring argv/env after each test

These tests mutate process.argv and process.env without restoration, which can leak into subsequent tests in this file. Add an afterEach to restore prior state.

Example (outside the shown range; adjust imports accordingly):

// add to imports: const { afterEach } = require('node:test');
const argvBackup = process.argv.slice();
const envBackup = { ...process.env };

afterEach(() => {
  process.argv = argvBackup.slice();
  process.env = { ...envBackup };
});
test/16-custom-parser.js (1)

82-85: Remove unused parameter from after() hook

topic isn’t used; drop the param for clarity.

-  after(function (topic) {
+  after(function () {
     delete process.env.NODE_CONFIG_PARSER;
     requireUncached(__dirname + '/../parser');
   });
test/2-config-test.js (1)

32-33: Use realistic argv entries instead of undefined placeholders

Some code may read process.argv[0]/[1]; use actual entries or previous values.

-      process.argv = [undefined, undefined, '--NODE_CONFIG={"EnvOverride":{"parm5":"overridden from --NODE_CONFIG","parm6":101}}'];
+      process.argv = [process.argv[0], process.argv[1], '--NODE_CONFIG={"EnvOverride":{"parm5":"overridden from --NODE_CONFIG","parm6":101}}'];
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bf0c6b6 and d7203e2.

📒 Files selected for processing (32)
  • package.json (1 hunks)
  • test/0-util.js (1 hunks)
  • test/1-protected-test.js (8 hunks)
  • test/10-gitcrypt-test.js (3 hunks)
  • test/11-regexp.js (1 hunks)
  • test/12-node_env-override.js (1 hunks)
  • test/13-node_env-hostname.js (1 hunks)
  • test/14-node_env-load-multiple-files.js (1 hunks)
  • test/15-async-configs.js (2 hunks)
  • test/16-custom-parser.js (1 hunks)
  • test/17-toml.js (2 hunks)
  • test/18-skipConfigSources.js (1 hunks)
  • test/19-custom-environment-variables.js (1 hunks)
  • test/2-config-test.js (4 hunks)
  • test/20-multiple-config.js (6 hunks)
  • test/21-date.js (1 hunks)
  • test/22-binary.js (1 hunks)
  • test/22-files-order.js (3 hunks)
  • test/23-serialize.js (1 hunks)
  • test/3-deferred-configs.js (2 hunks)
  • test/4-array-merging.js (2 hunks)
  • test/5-getConfigSources.js (2 hunks)
  • test/6-strict-mode.js (2 hunks)
  • test/7-unicode-situations.js (2 hunks)
  • test/8-config-extending.js (2 hunks)
  • test/9-raw-configs.js (1 hunks)
  • test/makeImmutable-shared-refs.js (1 hunks)
  • test/util.js (1 hunks)
  • test/x-config-js-test-transpiled.js (2 hunks)
  • test/x-config-test-ts-module-exports.js (2 hunks)
  • test/x-config-test-ts.js (2 hunks)
  • test/x-deferred-configs-ts.js (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (22)
  • package.json
  • test/5-getConfigSources.js
  • test/9-raw-configs.js
  • test/10-gitcrypt-test.js
  • test/4-array-merging.js
  • test/22-binary.js
  • test/22-files-order.js
  • test/14-node_env-load-multiple-files.js
  • test/makeImmutable-shared-refs.js
  • test/21-date.js
  • test/19-custom-environment-variables.js
  • test/18-skipConfigSources.js
  • test/23-serialize.js
  • test/x-config-test-ts-module-exports.js
  • test/x-config-test-ts.js
  • test/11-regexp.js
  • test/3-deferred-configs.js
  • test/15-async-configs.js
  • test/8-config-extending.js
  • test/7-unicode-situations.js
  • test/12-node_env-override.js
  • test/x-config-js-test-transpiled.js
🧰 Additional context used
🧬 Code Graph Analysis (8)
test/16-custom-parser.js (1)
test/3-deferred-configs.js (2)
  • require (4-4)
  • assert (5-5)
test/util.js (3)
lib/config.js (3)
  • require (8-8)
  • config (1030-1030)
  • util (114-114)
test/2-config-test.js (16)
  • require (4-4)
  • requireUncached (3-3)
  • assert (5-5)
  • config (18-18)
  • config (61-61)
  • config (138-138)
  • config (181-181)
  • config (197-197)
  • config (213-213)
  • config (261-261)
  • config (294-294)
  • config (316-316)
  • config (344-344)
  • config (418-418)
  • config (467-467)
  • config (565-565)
test/1-protected-test.js (4)
  • require (4-4)
  • requireUncached (3-3)
  • assert (5-5)
  • config (30-30)
test/13-node_env-hostname.js (10)
test/2-config-test.js (16)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
  • config (18-18)
  • config (61-61)
  • config (138-138)
  • config (181-181)
  • config (197-197)
  • config (213-213)
  • config (261-261)
  • config (294-294)
  • config (316-316)
  • config (344-344)
  • config (418-418)
  • config (467-467)
  • config (565-565)
test/3-deferred-configs.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/10-gitcrypt-test.js (4)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
  • config (9-9)
test/15-async-configs.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/4-array-merging.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/1-protected-test.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/x-deferred-configs-ts.js (3)
  • requireUncached (5-5)
  • require (6-6)
  • assert (7-7)
test/7-unicode-situations.js (3)
  • requireUncached (4-4)
  • require (5-5)
  • assert (6-6)
test/8-config-extending.js (4)
  • requireUncached (4-4)
  • require (5-5)
  • require (7-7)
  • assert (6-6)
lib/config.js (2)
  • require (8-8)
  • config (1030-1030)
test/0-util.js (3)
test/8-config-extending.js (4)
  • require (5-5)
  • require (7-7)
  • assert (6-6)
  • result (30-30)
lib/util.js (1)
  • Path (8-8)
test/util.js (1)
  • testCases (94-116)
test/6-strict-mode.js (12)
test/18-skipConfigSources.js (9)
  • requireUncached (4-4)
  • require (5-5)
  • assert (6-6)
  • config (11-11)
  • config (36-36)
  • config (60-60)
  • config (94-94)
  • config (119-119)
  • config (144-144)
test/2-config-test.js (9)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
  • config (18-18)
  • config (61-61)
  • config (138-138)
  • config (181-181)
  • config (197-197)
  • config (213-213)
test/3-deferred-configs.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/10-gitcrypt-test.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/x-config-test-ts.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/14-node_env-load-multiple-files.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/4-array-merging.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/1-protected-test.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/x-deferred-configs-ts.js (3)
  • requireUncached (5-5)
  • require (6-6)
  • assert (7-7)
test/7-unicode-situations.js (3)
  • requireUncached (4-4)
  • require (5-5)
  • assert (6-6)
test/8-config-extending.js (4)
  • requireUncached (4-4)
  • require (5-5)
  • require (7-7)
  • assert (6-6)
test/9-raw-configs.js (3)
  • requireUncached (7-7)
  • require (8-8)
  • assert (9-9)
test/2-config-test.js (3)
test/5-getConfigSources.js (6)
  • requireUncached (4-4)
  • require (5-5)
  • assert (6-6)
  • config (22-22)
  • config (76-76)
  • config (101-101)
test/3-deferred-configs.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
lib/config.js (3)
  • require (8-8)
  • config (1030-1030)
  • moduleConfig (214-214)
test/17-toml.js (5)
test/21-date.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/23-serialize.js (3)
  • requireUncached (5-5)
  • require (6-6)
  • assert (7-7)
test/22-files-order.js (4)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
  • config (18-18)
test/x-config-js-test-transpiled.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/9-raw-configs.js (3)
  • requireUncached (7-7)
  • require (8-8)
  • assert (9-9)
test/1-protected-test.js (4)
test/util.js (5)
  • requireUncached (4-4)
  • require (5-5)
  • assert (6-6)
  • result (59-59)
  • result (66-66)
test/3-deferred-configs.js (3)
  • requireUncached (3-3)
  • require (4-4)
  • assert (5-5)
test/8-config-extending.js (5)
  • requireUncached (4-4)
  • require (5-5)
  • require (7-7)
  • assert (6-6)
  • result (30-30)
lib/config.js (2)
  • require (8-8)
  • config (1030-1030)
🔇 Additional comments (4)
test/1-protected-test.js (1)

786-824: Nice isolation for getCmdLineArg() tests

The suite resets process.argv and restores it, making the tests deterministic. Once argvOrg is snapped (see above), this becomes fully robust.

test/6-strict-mode.js (1)

7-70: Refactor to node:test and helper shape looks good

Switch from vows to describe/it with a properly shaped _expectException that returns a describe body is correct and should run under node:test.

test/util.js (1)

114-116: Good fix: restored intended numeric cases by adding the missing comma

The array now includes both 1.1 and -1 instead of evaluating an expression. This restores coverage.

test/16-custom-parser.js (1)

27-44: Scenario setup is clear and isolated

Each scenario sets NODE_CONFIG_DIR and NODE_CONFIG_PARSER before reloading config. Clean and deterministic.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
test/1-protected-test.js (2)

14-14: Avoid aliasing process.argv; snapshot instead to enable reliable restore

Aliasing means any push/mutation to process.argv also mutates argvOrg, defeating the purpose of “original argv.”

Apply this diff:

-const argvOrg = process.argv;
+const argvOrg = process.argv.slice();

42-45: Prevent argv pollution across tests: reset from snapshot each setup and then append

Using push on process.argv causes flags to accumulate across tests. Reset from a clean snapshot every beforeEach.

-    process.env.NODE_CONFIG = '{"EnvOverride":{"parm3":"overridden from $NODE_CONFIG","parm4":100}}';
-    process.argv.push('--NODE_CONFIG={"EnvOverride":{"parm5":"overridden from --NODE_CONFIG","parm6":101}}');
+    process.env.NODE_CONFIG = '{"EnvOverride":{"parm3":"overridden from $NODE_CONFIG","parm4":100}}';
+    // Reset argv to a clean snapshot to avoid cross-test accumulation
+    process.argv = argvOrg.slice();
+    process.argv.push('--NODE_CONFIG={"EnvOverride":{"parm5":"overridden from --NODE_CONFIG","parm6":101}}');
🧹 Nitpick comments (5)
test/1-protected-test.js (5)

25-25: Force serial execution within this file to avoid flakiness from shared global state

Given the heavy use of process.env and process.argv, run these tests serially within the file to minimize cross-test interference.

-describe('Protected (hackable) utilities test', function() {
+describe('Protected (hackable) utilities test', { concurrency: false }, function() {

If you prefer to keep concurrency, ensure rigorous snapshot/restore of env and argv around every test (see next comment).


13-15: Also snapshot process.env so it can be reliably restored

This guards against cross-file/test leakage when tests run concurrently in workers.

 // Make a copy of the command line args
-const argvOrg = process.argv;
+const argvOrg = process.argv.slice();
+// Snapshot of the original environment to restore after tests in this file
+const envOrg = { ...process.env };

49-51: Add afterEach to restore argv and targeted env vars to the original baseline

Restoring avoids state leaking into subsequent tests/files. Import afterEach from node:test at the top.

   config = requireUncached(__dirname + '/../lib/config');
 });
 
+afterEach(function () {
+  // Restore argv to baseline snapshot
+  process.argv = argvOrg.slice();
+
+  // Restore only the env keys this file mutates
+  const touched = [
+    'NODE_CONFIG_DIR',
+    'NODE_ENV',
+    'NODE_APP_INSTANCE',
+    'NODE_CONFIG',
+    'CUSTOM_JSON_ENVIRONMENT_VAR',
+  ];
+  for (const k of touched) {
+    if (envOrg[k] === undefined) {
+      delete process.env[k];
+    } else {
+      process.env[k] = envOrg[k];
+    }
+  }
+});

And update the import:

-const { describe, it, beforeEach } = require('node:test');
+const { describe, it, beforeEach, afterEach } = require('node:test');

819-823: Fix “revert original process arguments” to avoid re-aliasing and assert by value

Assigning process.argv = argvOrg reintroduces aliasing. Restore from a copy and assert deep equality of contents instead of identity.

-    assert.notEqual(process.argv, argvOrg);
-    process.argv = argvOrg;
-    assert.equal(process.argv, argvOrg);
+    assert.notStrictEqual(process.argv, argvOrg);
+    process.argv = argvOrg.slice();
+    assert.deepStrictEqual(process.argv, argvOrg);

4-5: Consider adopting assert.strict APIs for consistency and stronger checks*

Using assert.equal/deepEqual can hide subtle coercion issues; assert.strictEqual/deepStrictEqual is preferable. This can be a follow-up codemod across the suite.

If you want, I can generate a codemod script to replace equal/notEqual/deepEqual with their strict counterparts across test files.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d7203e2 and 0556b9d.

📒 Files selected for processing (3)
  • package.json (1 hunks)
  • test/1-protected-test.js (8 hunks)
  • test/2-config-test.js (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/2-config-test.js
  • package.json
🔇 Additional comments (1)
test/1-protected-test.js (1)

1-6: Migration to node:test looks good overall

Nice cleanup moving off Vows, keeping tests focused on config.util behavior and requiring the module under test per-setup to avoid require-time side effects.

@jdmarshall
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Aug 15, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Collaborator

@markstos markstos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Glad to see vows go.

@jdmarshall
Copy link
Copy Markdown
Collaborator Author

I was surprised it was a block on ESM progress. Kinda wish I’d found a better way to do the transformation. This was all grunt work.

@jdmarshall jdmarshall merged commit 29a7bfd into node-config:master Aug 15, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

vows needs to go

2 participants