Skip to content

Commit

Permalink
feat: add unit tests to the module to be able to add vue-oauth2-keycl…
Browse files Browse the repository at this point in the history
…oak module at the ci pipeline
  • Loading branch information
renanfranca committed Sep 23, 2024
1 parent b54516d commit c0b0d99
Show file tree
Hide file tree
Showing 6 changed files with 318 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class VueOAuth2KeycloakModulesFactory {
private static final JHipsterSource DOCUMENTATION_SOURCE = SOURCE.append("documentation");

private static final JHipsterDestination MAIN_DESTINATION = to("src/main/webapp/app");
private static final JHipsterDestination TEST_DESTINATION = to("src/test/webapp");

private static final String MAIN_TS_IMPORT_NEEDLE = "// jhipster-needle-main-ts-import";
private static final String MAIN_TS_PROVIDER_NEEDLE = "// jhipster-needle-main-ts-provider";
Expand Down Expand Up @@ -75,6 +76,14 @@ public JHipsterModule buildModule(JHipsterModuleProperties properties) {
)
.and()
.and()
.files()
.batch(APP_SOURCE.append("test/webapp/unit/auth/infrastructure/secondary"), TEST_DESTINATION.append("unit/auth/infrastructure/secondary"))
.addTemplate("KeycloakAuthRepository.spec.ts")
.addTemplate("KeycloakHttp.spec.ts")
.addTemplate("KeycloakHttpStub.ts")
.addTemplate("KeycloakStub.ts")
.and()
.and()
.build();
//@formatter:on
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { KeycloakAuthRepository } from '@/auth/infrastructure/secondary/KeycloakAuthRepository';
import type { KeycloakHttpStub } from './KeycloakHttpStub';
import { stubKeycloakHttp, fakeAuthenticatedUser } from './KeycloakHttpStub';

describe('KeycloakAuthRepository', () => {
let keycloakHttpStub: KeycloakHttpStub;
let authRepository: KeycloakAuthRepository;
beforeEach(() => {
keycloakHttpStub = stubKeycloakHttp();
authRepository = new KeycloakAuthRepository(keycloakHttpStub);
});

it('should authenticate a user', async () => {
const mockUser = fakeAuthenticatedUser();
keycloakHttpStub.currentUser.resolves(mockUser);
const user = await authRepository.currentUser();
expect(user).toEqual(mockUser);
expect(keycloakHttpStub.currentUser.calledOnce).toBe(true);
});

it('should propagate error when currentUser fails', async () => {
const error = new Error('Authentication failed');
keycloakHttpStub.currentUser.rejects(error);
await expect(authRepository.currentUser()).rejects.toThrow('Authentication failed');
expect(keycloakHttpStub.currentUser.calledOnce).toBe(true);
});

it('should login a user successfully', async () => {
keycloakHttpStub.login.resolves();
await authRepository.login();
expect(keycloakHttpStub.login.calledOnce).toBe(true);
});

it('should propagate error when login fails', async () => {
const error = new Error('Login failed');
keycloakHttpStub.login.rejects(error);
await expect(authRepository.login()).rejects.toThrow('Login failed');
expect(keycloakHttpStub.login.calledOnce).toBe(true);
});

it('should logout a user', async () => {
keycloakHttpStub.logout.resolves();
await authRepository.logout();
expect(keycloakHttpStub.logout.calledOnce).toBe(true);
});

it('should propagate error when logout fails', async () => {
const error = new Error('Logout failed');
keycloakHttpStub.logout.rejects(error);
await expect(authRepository.logout()).rejects.toThrow('Logout failed');
expect(keycloakHttpStub.logout.calledOnce).toBe(true);
});

it('should check if a user is authenticated', async () => {
keycloakHttpStub.authenticated.resolves(true);
const isAuthenticated = await authRepository.authenticated();
expect(isAuthenticated).toBe(true);
expect(keycloakHttpStub.authenticated.calledOnce).toBe(true);
});

it('should propagate error when authenticated check fails', async () => {
const error = new Error('Authentication check failed');
keycloakHttpStub.authenticated.rejects(error);
await expect(authRepository.authenticated()).rejects.toThrow('Authentication check failed');
expect(keycloakHttpStub.authenticated.calledOnce).toBe(true);
});

it('should refresh the token', async () => {
const newToken = 'new-test-token';
keycloakHttpStub.refreshToken.resolves(newToken);
const refreshedToken = await authRepository.refreshToken();
expect(refreshedToken).toBe(newToken);
expect(keycloakHttpStub.refreshToken.calledOnce).toBe(true);
});

it('should propagate error when refreshToken fails', async () => {
const error = new Error('Token refresh failed');
keycloakHttpStub.refreshToken.rejects(error);
await expect(authRepository.refreshToken()).rejects.toThrow('Token refresh failed');
expect(keycloakHttpStub.refreshToken.calledOnce).toBe(true);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { describe, it, expect } from 'vitest';
import { KeycloakHttp } from '@/auth/infrastructure/secondary/KeycloakHttp';
import { stubKeycloak } from './KeycloakStub';
import { fakeAuthenticatedUser } from './KeycloakHttpStub';

const createKeycloakHttp = () => {
const keycloakStub = stubKeycloak();
const keycloakHttp = new KeycloakHttp(keycloakStub);
return { keycloakStub, keycloakHttp };
};

describe('KeycloakHttp', () => {
describe('Authentication', () => {
it('should authenticate successfully', async () => {
const { keycloakStub, keycloakHttp } = createKeycloakHttp();
const fakeUser = fakeAuthenticatedUser();
keycloakStub.init.resolves(true);
keycloakStub.authenticated = true;
keycloakStub.tokenParsed = { preferred_username: fakeUser.username };
keycloakStub.token = fakeUser.token;

const result = await keycloakHttp.currentUser();

expect(result).toEqual(fakeUser);
expect(keycloakStub.init.calledOnce).toBe(true);
});

it('should handle authentication failure', async () => {
const { keycloakStub, keycloakHttp } = createKeycloakHttp();
keycloakStub.init.resolves(false);

const result = await keycloakHttp.currentUser();

expect(result).toEqual({ isAuthenticated: false, username: '', token: '' });
expect(keycloakStub.init.calledOnce).toBe(true);
});
});

describe('Initialization', () => {
it('should not reinitialize if already initialized', async () => {
const { keycloakStub, keycloakHttp } = createKeycloakHttp();
keycloakStub.init.resolves(true);

await keycloakHttp.currentUser();
await keycloakHttp.currentUser();

expect(keycloakStub.init.calledOnce).toBe(true);
});

it('should initialize only once across different method calls', async () => {
const { keycloakStub, keycloakHttp } = createKeycloakHttp();
keycloakStub.init.resolves(true);

await keycloakHttp.currentUser();
await keycloakHttp.login();
await keycloakHttp.logout();
await keycloakHttp.authenticated();
await keycloakHttp.refreshToken();

expect(keycloakStub.init.calledOnce).toBe(true);
});
});

it('should handle undefined preferred_username', async () => {
const { keycloakStub, keycloakHttp } = createKeycloakHttp();
keycloakStub.init.resolves(true);
keycloakStub.authenticated = true;
keycloakStub.tokenParsed = {};
keycloakStub.token = 'test-token';

const result = await keycloakHttp.currentUser();

expect(result).toEqual({
isAuthenticated: true,
username: '',
token: 'test-token'
});
});

it('should handle undefined token', async () => {
const { keycloakStub, keycloakHttp } = createKeycloakHttp();
keycloakStub.init.resolves(true);
keycloakStub.authenticated = true;
keycloakStub.tokenParsed = { preferred_username: 'test' };
keycloakStub.token = undefined;

const result = await keycloakHttp.currentUser();

expect(result).toEqual({
isAuthenticated: true,
username: 'test',
token: ''
});
});

it('should logout', async () => {
const { keycloakStub, keycloakHttp } = createKeycloakHttp();
keycloakStub.logout.resolves();

await keycloakHttp.logout();

expect(keycloakStub.logout.calledOnce).toBe(true);
});

it('should check if authenticated', async () => {
const { keycloakStub, keycloakHttp } = createKeycloakHttp();
keycloakStub.init.resolves(true);
keycloakStub.authenticated = true;
keycloakStub.token = 'valid-token';

const result = await keycloakHttp.authenticated();

expect(result).toBe(true);
expect(keycloakStub.init.calledOnce).toBe(true);
});

it('should refresh token', async () => {
const { keycloakStub, keycloakHttp } = createKeycloakHttp();
const newToken = 'new-test-token';
keycloakStub.updateToken.resolves();
keycloakStub.token = newToken;

const result = await keycloakHttp.refreshToken();

expect(result).toBe(newToken);
expect(keycloakStub.updateToken.calledOnce).toBe(true);
});
});

it('should login successfully', async () => {
const { keycloakStub, keycloakHttp } = createKeycloakHttp();
keycloakStub.init.resolves(true);
keycloakStub.login.resolves();

await keycloakHttp.login();

expect(keycloakStub.init.calledOnce).toBe(true);
expect(keycloakStub.login.calledOnce).toBe(true);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import sinon from 'sinon';
import type { SinonStub } from 'sinon';
import type { KeycloakHttp } from '@/auth/infrastructure/secondary/KeycloakHttp';
import type { AuthenticatedUser } from '@/auth/domain/AuthenticatedUser';

export interface KeycloakHttpStub extends KeycloakHttp {
currentUser: SinonStub;
login: SinonStub;
logout: SinonStub;
authenticated: SinonStub;
refreshToken: SinonStub;
getKeycloakInstance: SinonStub;
}

export const stubKeycloakHttp = (): KeycloakHttpStub => ({
currentUser: sinon.stub(),
login: sinon.stub(),
logout: sinon.stub(),
authenticated: sinon.stub(),
refreshToken: sinon.stub(),
getKeycloakInstance: sinon.stub(),
}) as KeycloakHttpStub;

export const fakeAuthenticatedUser = (): AuthenticatedUser => ({
isAuthenticated: true,
username: 'testuser',
token: 'test-token'
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import Keycloak from 'keycloak-js';
import sinon from 'sinon';
import type{ SinonStub } from 'sinon';

export interface KeycloakStub extends Keycloak {
init: SinonStub;
login: SinonStub;
logout: SinonStub;
register: SinonStub;
accountManagement: SinonStub;
updateToken: SinonStub;
clearToken: SinonStub;
hasRealmRole: SinonStub;
hasResourceRole: SinonStub;
loadUserProfile: SinonStub;
loadUserInfo: SinonStub;
authenticated?: boolean;
token?: string;
tokenParsed?: { preferred_username?: string };
}

export const stubKeycloak = (): KeycloakStub => ({
init: sinon.stub(),
login: sinon.stub(),
logout: sinon.stub(),
register: sinon.stub(),
accountManagement: sinon.stub(),
updateToken: sinon.stub(),
clearToken: sinon.stub(),
hasRealmRole: sinon.stub(),
hasResourceRole: sinon.stub(),
loadUserProfile: sinon.stub(),
loadUserInfo: sinon.stub(),
authenticated: false,
token: undefined,
tokenParsed: undefined,
}) as KeycloakStub;
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,12 @@ void shouldBuildVueOAuth2KeycloakModule() {
provideForAuth(keycloakHttp);
// jhipster-needle-main-ts-provider\
"""
);
)
.and()
.hasFiles("src/test/webapp/unit/auth/infrastructure/secondary/KeycloakAuthRepository.spec.ts")
.hasFiles("src/test/webapp/unit/auth/infrastructure/secondary/KeycloakHttp.spec.ts")
.hasFiles("src/test/webapp/unit/auth/infrastructure/secondary/KeycloakHttpStub.ts")
.hasFiles("src/test/webapp/unit/auth/infrastructure/secondary/KeycloakStub.ts");
//@formatter:on
}

Expand Down

0 comments on commit c0b0d99

Please sign in to comment.