tsoa
tsoa copied to clipboard
Cannot mock authentication function since v6.4.0
I'm using tsoa in conjunction with express and inversify and since 6.4.0 many of my tests are broken because I can no longer mock the authentication function. It's a bit convoluted (for reasons) but I'll try to describe the set up below:
My authentication logic is implemented as a method on a class that has a bunch of dependencies injected in the constructor via inversify. Since tsoa requires the authentication middleware to be a function, I have some code that rebinds and exports the relevant method on my authentication class as a function. That function is then referenced in the tsoa configuration. In my controller tests, I want to override the authentication code, and so I mock the relevant function using jest.spyOn.
This worked up until the change introduced in https://github.com/lukeautry/tsoa/commit/0dac399763fb077e960a1e1ee8ee648275c48ede where the authentication function is rebound and typecast to a local const in the generated code. I don't understand why rebinding the function to a locally scoped const should somehow un-mock the function in question, but that appears to be what's happening.
I'm open to the idea that I'm just holding this wrong, and there may be a simpler approach that isn't vulnerable to this problem.
Sorting
-
I'm submitting a ...
- [x] bug report
- [ ] feature request
- [ ] support request
-
I confirm that I
- [x] used the search to make sure that a similar issue hasn't already been submit
Expected Behavior
I should be able to mock the authentication code so that I can return a valid user, throw an error, whatever.
The class that implements authentication:
@injectable()
export class TsoaAuthentication {
constructor(@inject(SomeService) private readonly service: SomeService ) {}
async authenticate(request: ExpressRequest, securityName: string, scopes?: string[]) {
// call the service, do the checks, return the user or throw.
}
}
The file that makes the authentication code conform to the API described in https://tsoa-community.github.io/docs/authentication.html:
import { TsoaAuthentication } from './security';
import { iocContainer } from '../ioc/apiContainer';
const tsoaAuthentication = iocContainer.get(TsoaAuthentication);
/**
* NB: The name of this export must be `expressAuthentication`.
* See https://tsoa-community.github.io/docs/authentication.html
*/
export const expressAuthentication = tsoaAuthentication.authenticate.bind(tsoaAuthentication);
The test file:
import * as TsoaAuthenticationModule from '../../../authentication';
describe('the controller', () => {
const mockSecurityMiddleware = jest
.spyOn(TsoaAuthenticationModule, 'expressAuthentication')
.mockResolvedValue(authenticatedUser);
it('works', async () => {
// ... test code relying on the authentication function to return authenticatedUser
})
}
Current Behavior
The tests fail because the original implementation of the authentication function is executed at runtime, instead of the mock/spy. I know that it's related to the aforementioned commit, because I can fix the issue by opening the generated routes, commenting out the relevant line, and importing the original authentication function with the new name:
routesGen.ts:
import { expressAuthentication as expressAuthenticationRecasted } from './../authentication';
...
// const expressAuthenticationRecasted = expressAuthentication as (req: ExRequest, securityName: string, scopes?: string[], res?: ExResponse) => Promise<any>;
Possible Solution
I don't understand what's happening well enough to suggest a solution
Context (Environment)
Version of the libraries:
@tsoa/runtime:6.4.0@tsoa/cli:6.4.0jest:29.5.0inversify:6.0.1
Version of NodeJS: v18.20.2
Hello there jscn 👋
Thank you for opening your very first issue in this project.
We will try to get back to you as soon as we can.👀
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days
@jscn what was the solution for this?
So far my solution has been to not upgrade 😞