Skip to content

lib: added isNativeError check to assert.js - #51250

Closed
NiharPhansalkar wants to merge 3 commits into
nodejs:mainfrom
NiharPhansalkar:assert-ok-fix
Closed

lib: added isNativeError check to assert.js#51250
NiharPhansalkar wants to merge 3 commits into
nodejs:mainfrom
NiharPhansalkar:assert-ok-fix

Conversation

@NiharPhansalkar

Copy link
Copy Markdown
Contributor

Added the function for compliance with
frameworks such as Jest

Fixes: #50780

@nodejs-github-botnodejs-github-bot added assert Issues and PRs related to the assert subsystem. needs-ci PRs that need a full CI run. labels Dec 21, 2023
@NiharPhansalkar

Copy link
Copy Markdown
ContributorAuthor

I want to ask what kinds of tests can be written for this fix? The actual problem was occuring for frameworks such as Jest. Native code works just fine.

@apapirovski

Copy link
Copy Markdown
Contributor

I want to ask what kinds of tests can be written for this fix? The actual problem was occuring for frameworks such as Jest. Native code works just fine.

Look for how other tests that utilize isNativeError are testing this behavior, which will give you a hint on how to create an Error that doesn't pass an instanceof check but passes isNativeError check.

Here's one helpful link:

util.types.isNativeError(new(context('TypeError'))()),

@NiharPhansalkar

NiharPhansalkar commented Dec 22, 2023

Copy link
Copy Markdown
ContributorAuthor

The test for this is already being covered in the following:

https://github.com/nodejs/node/blob/ba957a61f83365d9df7126c23a1c78c320061414/test/parallel/test-assert.js#L50C5-L50C46

Where if the code is now run with Jest, instead of an AssertionError, the provided error will be thrown.

Do I still need to add any tests?

@apapirovski

apapirovski commented Dec 22, 2023

Copy link
Copy Markdown
Contributor

Where if the code is now run with Jest, instead of an AssertionError, the provided error will be thrown.

Do I still need to add any tests?

You need a test in Node's test suite that would fail w/ the old version of the code and succeed w/ the new one. Right now that doesn't exist. Running it with Jest isn't relevant since we don't run our test suite w/ Jest.

Writing tests is quite straightforward: https://github.com/nodejs/node/blob/main/doc/contributing/writing-tests.md

@NiharPhansalkar

Copy link
Copy Markdown
ContributorAuthor

I am a little confused @apapirovski. If you go through the discussion of the issue, you can see that the problem was never with NodeJS directly. The code was working fine when running with node even without the change I have made. The problem was occuring with frameworks such as Jest who have some issue with the instanceof operator.
So, this change is just to help out with that, if just in case someone does start using Jest (or some framework like it) and uses assert.ok() to throw a custom error, they should not fall into trouble.
So, I don't think it is possible for me to write a test which will fail for a version before this PR but pass for a version after this PR since there never was an issue in Node directly. (So it will pass even if this PR is not put into action)

@apapirovski

apapirovski commented Dec 22, 2023

Copy link
Copy Markdown
Contributor

Tests for Node.js are meant to reproduce the issue that we saw in another library. Here's a line of code that would fail on the Node.js before this change and one that will pass now:

assert.ok(false,new(context('Error'))('Custom Error'))

Pre-and-post output looks like this:

Uncaught AssertionError [ERR_ASSERTION]: Error: Custom Error
at REPL3:1:8
at Script.runInThisContext (node:vm:129:12)
at REPLServer.defaultEval (node:repl:566:29)
at bound (node:domain:421:15)
at REPLServer.runBound [as eval] (node:domain:432:12)
at REPLServer.onLine (node:repl:893:10)
at REPLServer.emit (node:events:538:35)
at REPLServer.emit (node:domain:475:12)
at REPLServer.Interface._onLine (node:readline:487:10)
at REPLServer.Interface._line (node:readline:864:8) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: false,
expected: true,
operator: '=='
}

Post:

Uncaught Error: Custom Error

@NiharPhansalkar
NiharPhansalkarforce-pushed the assert-ok-fix branch 2 times, most recently from ae91ee2 to 2d3421cCompareDecember 24, 2023 11:46
@NiharPhansalkar

Copy link
Copy Markdown
ContributorAuthor

@apapirovski Thank you so much for everything

@BridgeARBridgeAR left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

These tests are all passing in already released Node.js versions and therefore indicate that it's not testing the right thing.

What we need is something like this:

constcontext=vm.createContext();consterror=vm.runInContext('new TypeError("foo")',context);assert.throws(()=>assert(false,error),{message: 'foo',name: 'TypeError'});

Please always try to focus on what case is not yet covered and how to recreate that situation.

Comment threadtest/parallel/test-assert.js Outdated
Comment threadtest/parallel/test-assert.js Outdated
Comment threadlib/assert.js Outdated
@NiharPhansalkar

Copy link
Copy Markdown
ContributorAuthor

@BridgeAR I will make the changes, however can you please tell me if I am able to understand the reason for this?

My current commit, it checks for e to be an instanceof Error, however, this does not tell us whether what was thrown was an assertion error or a specific user mentioned error, which is why my tests are incorrect.

Do I understand?

Added the function for compliance with
frameworks such as Jest
Fixes: nodejs#50780
Added tests to check if any previous behaviour where instanceof was
not throwing a custom error is working with the isNativeError change
in assert.ok()
Changed the tests to check for correct throwing of
errors and combined if/else into one in assert.js
to make code concise.

@BridgeARBridgeAR left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

I left some suggestions to improve the code. We already have a utility function for testing this and we already use it in the file and one test case is redundant.

Comment threadlib/assert.js
generatedMessage = true;
message = getErrMessage(message, fn);
} else if (message instanceof Error) {
} else if (isNativeError(message) || message instanceof Error) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
}elseif(isNativeError(message)||messageinstanceofError){
}elseif(isError(message)){

Comment threadlib/assert.js
validateFunction,
} = require('internal/validators');
const { fileURLToPath } = require('internal/url');
const { isNativeError } = internalBinding('types');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
const { isNativeError } = internalBinding('types');


assert.ok(a.AssertionError.prototype instanceof Error,
'a.AssertionError instanceof Error');
'a.AssertionError instanceof Error');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
'a.AssertionError instanceof Error');
'a.AssertionError instanceof Error');

Comment on lines +58 to +68
// Thrown error should be the passed through error instance of the native error
{
const context = vm.createContext();
const error = vm.runInContext('new TypeError("custom error")', context);

assert.throws(() => assert(false, error), {
message: 'custom error',
name: 'TypeError'
});
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// Thrown error should be the passed through error instance of the native error
{
constcontext=vm.createContext();
consterror=vm.runInContext('new TypeError("custom error")',context);
assert.throws(()=>assert(false,error),{
message: 'custom error',
name: 'TypeError'
});
}

});
}

// Thrown error should be the passed through error instance of the native error

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// Thrown error should be the passed through error instance of the native error
// Errors created in different contexts are handled as any other custom error

@BridgeARBridgeAR added the request-ci Add this label to start a Jenkins CI on a PR. label May 4, 2024
@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label May 4, 2024
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@aduh95

Copy link
Copy Markdown
Contributor

Linter is failing with Expected indentation of 10 spaces but found 2.

@Ceres6

Copy link
Copy Markdown
Contributor

Closed by #54776

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

assertIssues and PRs related to the assert subsystem.needs-ciPRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

assert.ok() throwing AssertionError instead of provided Error object

7 participants

@NiharPhansalkar@apapirovski@nodejs-github-bot@aduh95@Ceres6@jasnell@BridgeAR