Skip to content

Commit b1e6bb4

Browse files
pranesh-sridhardhmlau
authored andcommitted
test(authentication): cover redirect handling and invalid decorator target
Add unit tests for the uncovered paths in the authentication package: - AuthenticateActionProvider: a strategy returning a RedirectRoute sets the redirect url and status, uses the default 303 status, and short-circuits the remaining strategies - @authenticate: throws when applied to a property Statement, function and line coverage of the package is now 100%. Signed-off-by: Pranesh Sridhar <68075088+pranesh-sridhar@users.noreply.github.com>
1 parent 06bf4af commit b1e6bb4

2 files changed

Lines changed: 84 additions & 1 deletion

File tree

‎packages/authentication/src/__tests__/unit/decorators/authenticate.decorator.unit.ts‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,17 @@ describe('Authentication', () => {
123123
options: {option1: 'valueA', option2: 'value2'},
124124
});
125125
});
126+
it('throws an error when used on a property', () => {
127+
expect(() => {
128+
class TestClass {
129+
@authenticate('my-strategy')
130+
prop: string;
131+
}
132+
return TestClass;
133+
}).to.throw(
134+
/@authenticate cannot be used on a property: TestClass\.prototype\.prop/,
135+
);
136+
});
126137
});
127138

128139
it('can skip authentication', () => {

‎packages/authentication/src/__tests__/unit/providers/authentication.provider.unit.ts‎

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// License text available at https://opensource.org/licenses/MIT
55

66
import {Context, instantiateClass} from '@loopback/core';
7-
import {Request} from '@loopback/rest';
7+
import {RedirectRoute, Request} from '@loopback/rest';
88
import {securityId, UserProfile} from '@loopback/security';
99
import {expect} from '@loopback/testlab';
1010
import {AuthenticateFn, AuthenticationBindings} from '../../..';
@@ -121,6 +121,78 @@ describe('AuthenticateActionProvider', () => {
121121
await expect(authenticate(request)).to.not.be.rejected();
122122
});
123123

124+
describe('redirect handling', () => {
125+
let redirectUrl: string | undefined;
126+
let redirectStatus: number | undefined;
127+
128+
function givenProviderWithStrategies(
129+
strategies: AuthenticationStrategy[],
130+
) {
131+
redirectUrl = undefined;
132+
redirectStatus = undefined;
133+
currentUser = undefined;
134+
provider = new AuthenticateActionProvider(
135+
() => Promise.resolve(strategies),
136+
u => (currentUser = u),
137+
url => (redirectUrl = url),
138+
status => (redirectStatus = status),
139+
);
140+
}
141+
142+
function givenRedirectStrategy(
143+
targetLocation: string,
144+
statusCode?: number,
145+
): AuthenticationStrategy {
146+
return {
147+
name: 'redirect-strategy',
148+
authenticate: async () =>
149+
new RedirectRoute('/auth/login', targetLocation, statusCode),
150+
};
151+
}
152+
153+
it('sets redirect url and status when a strategy returns a RedirectRoute', async () => {
154+
givenProviderWithStrategies([
155+
givenRedirectStrategy('/auth/provider', 302),
156+
]);
157+
const authenticate = await Promise.resolve(provider.value());
158+
159+
const result = await authenticate(<Request>{});
160+
161+
expect(result).to.be.undefined();
162+
expect(redirectUrl).to.equal('/auth/provider');
163+
expect(redirectStatus).to.equal(302);
164+
expect(currentUser).to.be.undefined();
165+
});
166+
167+
it('uses the default 303 status when the RedirectRoute has none', async () => {
168+
givenProviderWithStrategies([givenRedirectStrategy('/auth/provider')]);
169+
const authenticate = await Promise.resolve(provider.value());
170+
171+
await authenticate(<Request>{});
172+
173+
expect(redirectStatus).to.equal(303);
174+
});
175+
176+
it('does not try remaining strategies after a redirect', async () => {
177+
const second = new MockStrategy2();
178+
let secondCalled = false;
179+
second.authenticate = async () => {
180+
secondCalled = true;
181+
return {[securityId]: 'mock-id'};
182+
};
183+
givenProviderWithStrategies([
184+
givenRedirectStrategy('/auth/provider', 302),
185+
second,
186+
]);
187+
const authenticate = await Promise.resolve(provider.value());
188+
189+
await authenticate(<Request>{});
190+
191+
expect(secondCalled).to.be.false();
192+
expect(redirectUrl).to.equal('/auth/provider');
193+
});
194+
});
195+
124196
describe('context.get(provider_key)', () => {
125197
it('returns a function which authenticates a request and returns a user', async () => {
126198
const context: Context = new Context();

0 commit comments

Comments
 (0)