add tests

This commit is contained in:
admin 2025-08-24 23:39:57 +02:00
parent 1d9f3e8d5f
commit 1c5095eb93
10 changed files with 1222 additions and 2 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "@apihub24/token-authentication",
"version": "1.0.6",
"version": "2.0.0-alpha.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@apihub24/token-authentication",
"version": "1.0.6",
"version": "2.0.0-alpha.0",
"license": "MIT",
"dependencies": {
"@apihub24/authentication": "2.0.0-alpha.0",

View File

@ -0,0 +1,196 @@
import { ExecutionContext } from "@nestjs/common";
import { TokenGuard } from "./token.guard";
import { Reflector } from "@nestjs/core";
import {
IAccount,
IGroup,
IOrganization,
IRight,
ITokenService,
} from "@apihub24/authentication";
import { RIGHTS_KEY } from "./rights.decorator";
import { ROLES_KEY } from "./roles.decorator";
import { ORGANIZATIONS_KEY } from "./organization.decorator";
describe("TokenGuard Tests", () => {
let guard: TokenGuard;
const mockRight: IRight = { id: "r1", name: "read:users" };
const mockOrganization: IOrganization = { id: "o1", name: "TestOrg" };
const mockGroup: IGroup = {
id: "g1",
name: "admin",
rights: [mockRight],
organization: mockOrganization,
};
const mockAccount: IAccount = {
id: "a1",
accountName: "a1",
email: "a1@example.de",
passwordHash: "passwordhash",
active: true,
emailVerified: true,
groups: [mockGroup],
};
const mockReflector = {
getAllAndOverride: jest.fn(),
};
const mockTokenService = {
getAccount: jest.fn(),
};
const mockExecutionContext = {
getHandler: () => ({}),
getClass: () => ({}),
switchToHttp: () => ({
getRequest: () => ({
headers: {},
}),
}),
} as unknown as ExecutionContext;
beforeAll(async () => {
jest.clearAllMocks();
guard = new TokenGuard(
mockReflector as unknown as Reflector,
mockTokenService as unknown as ITokenService
);
});
it("should be defined", () => {
expect(guard).toBeDefined();
});
it("should return true if no metadata is required", async () => {
mockReflector.getAllAndOverride.mockReturnValue(undefined);
const result = await guard.canActivate(mockExecutionContext);
expect(result).toBe(true);
expect(mockTokenService.getAccount).not.toHaveBeenCalled();
});
describe("Token Validation", () => {
it("should return false if authorization header is missing", async () => {
mockReflector.getAllAndOverride.mockReturnValue(["read:users"]);
const context = {
getHandler: () => ({}),
getClass: () => ({}),
switchToHttp: () => ({ getRequest: () => ({ headers: {} }) }),
} as unknown as ExecutionContext;
const result = await guard.canActivate(context);
expect(result).toBe(false);
expect(mockTokenService.getAccount).not.toHaveBeenCalled();
});
it("should return false if token is invalid", async () => {
mockReflector.getAllAndOverride.mockReturnValue(["read:users"]);
mockTokenService.getAccount.mockResolvedValue(null);
const result = await guard.canActivate(mockExecutionContext);
expect(result).toBe(false);
});
});
describe("Successful Authorization", () => {
it("should return true if account has required right", async () => {
mockReflector.getAllAndOverride.mockImplementation((key: string) => {
if (key === RIGHTS_KEY) return ["read:users"];
return undefined;
});
mockTokenService.getAccount.mockResolvedValue(mockAccount);
const context = {
getHandler: () => ({}),
getClass: () => ({}),
switchToHttp: () => ({
getRequest: () => ({ headers: { authorization: "Bearer bla" } }),
}),
} as unknown as ExecutionContext;
const result = await guard.canActivate(context);
expect(result).toBe(true);
});
it("should return true if account has required role", async () => {
mockReflector.getAllAndOverride.mockImplementation((key: string) => {
if (key === ROLES_KEY) return ["admin"];
return undefined;
});
mockTokenService.getAccount.mockResolvedValue(mockAccount);
const context = {
getHandler: () => ({}),
getClass: () => ({}),
switchToHttp: () => ({
getRequest: () => ({ headers: { authorization: "Bearer bla" } }),
}),
} as unknown as ExecutionContext;
const result = await guard.canActivate(context);
expect(result).toBe(true);
});
it("should return true if account belongs to the required organization", async () => {
mockReflector.getAllAndOverride.mockImplementation((key: string) => {
if (key === ORGANIZATIONS_KEY) return ["TestOrg"];
return undefined;
});
mockTokenService.getAccount.mockResolvedValue(mockAccount);
const context = {
getHandler: () => ({}),
getClass: () => ({}),
switchToHttp: () => ({
getRequest: () => ({ headers: { authorization: "Bearer bla" } }),
}),
} as unknown as ExecutionContext;
const result = await guard.canActivate(context);
expect(result).toBe(true);
});
it("should return true if all requirements are met", async () => {
mockReflector.getAllAndOverride.mockImplementation((key: string) => {
if (key === RIGHTS_KEY) return ["read:users"];
if (key === ROLES_KEY) return ["admin"];
if (key === ORGANIZATIONS_KEY) return ["TestOrg"];
return undefined;
});
mockTokenService.getAccount.mockResolvedValue(mockAccount);
const context = {
getHandler: () => ({}),
getClass: () => ({}),
switchToHttp: () => ({
getRequest: () => ({ headers: { authorization: "Bearer bla" } }),
}),
} as unknown as ExecutionContext;
const result = await guard.canActivate(context);
expect(result).toBe(true);
});
});
describe("Failed Authorization", () => {
it("should return false if a required right is missing", async () => {
mockReflector.getAllAndOverride.mockImplementation((key: string) => {
if (key === RIGHTS_KEY) return ["write:users"];
return undefined;
});
mockTokenService.getAccount.mockResolvedValue(mockAccount);
const result = await guard.canActivate(mockExecutionContext);
expect(result).toBe(false);
});
it("should return false if a required role is missing", async () => {
mockReflector.getAllAndOverride.mockImplementation((key: string) => {
if (key === ROLES_KEY) return ["user"];
return undefined;
});
mockTokenService.getAccount.mockResolvedValue(mockAccount);
const result = await guard.canActivate(mockExecutionContext);
expect(result).toBe(false);
});
it("should return false if a required organization is missing", async () => {
mockReflector.getAllAndOverride.mockImplementation((key: string) => {
if (key === ORGANIZATIONS_KEY) return ["AnotherOrg"];
return undefined;
});
mockTokenService.getAccount.mockResolvedValue(mockAccount);
const result = await guard.canActivate(mockExecutionContext);
expect(result).toBe(false);
});
});
});

View File

@ -0,0 +1,112 @@
import { Test, TestingModule } from "@nestjs/testing";
import { MailServiceMockModule } from "../../test/mail.verification.service.mock";
import { PasswordServiceMockModule } from "../../test/password.service.mock";
import { SessionServiceMockModule } from "../../test/session.service.mock";
import { TokenServiceMockModule } from "../../test/token.service.mock";
import { AccountRepositoryMockModule } from "../../test/account.repository.mock";
import { GroupRepositoryMockModule } from "../../test/group.repository.mock";
import { RightRepositoryMockModule } from "../../test/right.repository.mock";
import { OrganizationRepositoryMockModule } from "../../test/organization.repository.mock";
import {
GroupFactoryService,
OrganizationService,
RightService,
TokenAuthenticationModule,
} from "..";
import { IOrganization, IRight } from "@apihub24/authentication";
import * as validator from "class-validator";
import { Group } from "../models/group";
describe("GroupFactoryService Tests", () => {
let module: TestingModule;
let service: GroupFactoryService;
let rightService: RightService;
let organizationService: OrganizationService;
beforeAll(async () => {
module = await Test.createTestingModule({
imports: [
MailServiceMockModule.forRoot(),
PasswordServiceMockModule.forRoot(),
SessionServiceMockModule.forRoot(),
TokenServiceMockModule.forRoot(),
AccountRepositoryMockModule.forRoot(),
GroupRepositoryMockModule.forRoot(),
RightRepositoryMockModule.forRoot(),
OrganizationRepositoryMockModule.forRoot(),
TokenAuthenticationModule.forRoot(),
],
}).compile();
service = module.get(GroupFactoryService);
rightService = module.get(RightService);
organizationService = module.get(OrganizationService);
jest.clearAllMocks();
});
it("should be defined", () => {
expect(service).toBeDefined();
});
it("should create a group successfully", async () => {
const rights: IRight[] = [
{ id: "1", name: "read" },
{ id: "2", name: "write" },
];
const organizations: IOrganization[] = [{ id: "org-1", name: "Test Org" }];
const groupName = "Admins";
const organizationId = "org-1";
const rightIds = ["1", "2"];
jest.spyOn(rightService, "getBy").mockResolvedValue(rights);
jest.spyOn(organizationService, "getBy").mockResolvedValue(organizations);
jest.spyOn(validator, "validate").mockResolvedValue([]);
const result = await service.createGroup(
groupName,
organizationId,
rightIds
);
expect(rightService.getBy).toHaveBeenCalledWith(expect.any(Function));
expect(organizationService.getBy).toHaveBeenCalledWith(
expect.any(Function)
);
expect(result).toBeInstanceOf(Group);
expect(result.name).toBe(groupName);
expect(result.organization).toBe(organizations[0]);
expect(result.rights).toBe(rights);
});
it("should throw an error if the organization does not exist", async () => {
const groupName = "Admins";
const organizationId = "org-1";
const rightIds = ["1", "2"];
jest.spyOn(rightService, "getBy").mockResolvedValue([]);
jest.spyOn(organizationService, "getBy").mockResolvedValue([]);
jest.spyOn(validator, "validate").mockResolvedValue([]);
await expect(
service.createGroup(groupName, organizationId, rightIds)
).rejects.toThrow(`organization with id (${organizationId}) not exists`);
});
it("should throw a validation error if the group data is invalid", async () => {
const organizations: IOrganization[] = [{ id: "org-1", name: "Test Org" }];
const validationError: validator.ValidationError[] = [
{ toString: () => "name must not be empty" } as validator.ValidationError,
];
jest.spyOn(rightService, "getBy").mockResolvedValue([]);
jest.spyOn(organizationService, "getBy").mockResolvedValue(organizations);
jest
.spyOn(validator, "validate")
.mockResolvedValue(Promise.resolve(validationError));
await expect(service.createGroup("Admins", "org-1", ["1"])).rejects.toThrow(
"name must not be empty"
);
});
});

View File

@ -0,0 +1,193 @@
import { Test, TestingModule } from "@nestjs/testing";
import { GroupService } from "./group.service";
import { IRepository } from "@apihub24/repository";
import {
APIHUB24_GROUP_REPOSITORY,
APIHUB24_RIGHT_REPOSITORY,
IGroup,
IRight,
} from "@apihub24/authentication";
import { MailServiceMockModule } from "../../test/mail.verification.service.mock";
import { PasswordServiceMockModule } from "../../test/password.service.mock";
import { SessionServiceMockModule } from "../../test/session.service.mock";
import { TokenServiceMockModule } from "../../test/token.service.mock";
import { AccountRepositoryMockModule } from "../../test/account.repository.mock";
import { GroupRepositoryMockModule } from "../../test/group.repository.mock";
import { RightRepositoryMockModule } from "../../test/right.repository.mock";
import { OrganizationRepositoryMockModule } from "../../test/organization.repository.mock";
import { TokenAuthenticationModule } from "..";
describe("GroupService Tests", () => {
let module: TestingModule;
let service: GroupService;
let groupRepository: IRepository<IGroup>;
let rightRepository: IRepository<IRight>;
const mockGroup: IGroup = {
id: "group1",
name: "Admins",
rights: [{ id: "right1", name: "read" }],
organization: { id: "org1", name: "Test Org" },
};
const mockRight: IRight = {
id: "right2",
name: "write",
};
beforeAll(async () => {
module = await Test.createTestingModule({
imports: [
MailServiceMockModule.forRoot(),
PasswordServiceMockModule.forRoot(),
SessionServiceMockModule.forRoot(),
TokenServiceMockModule.forRoot(),
AccountRepositoryMockModule.forRoot(),
GroupRepositoryMockModule.forRoot(),
RightRepositoryMockModule.forRoot(),
OrganizationRepositoryMockModule.forRoot(),
TokenAuthenticationModule.forRoot(),
],
}).compile();
service = module.get(GroupService);
groupRepository = module.get(APIHUB24_GROUP_REPOSITORY);
rightRepository = module.get(APIHUB24_RIGHT_REPOSITORY);
jest.clearAllMocks();
});
it("should be defined", () => {
expect(service).toBeDefined();
});
describe("getBy", () => {
it("should return groups matching the filter", async () => {
jest.spyOn(groupRepository, "getBy").mockResolvedValue([mockGroup]);
const result = await service.getBy((g) => g.id === "group1");
expect(result).toEqual([mockGroup]);
expect(groupRepository.getBy).toHaveBeenCalledWith(expect.any(Function));
});
it("should return an empty array if no groups match", async () => {
jest.spyOn(groupRepository, "getBy").mockResolvedValue([]);
const result = await service.getBy((g) => g.id === "non-existent");
expect(result).toEqual([]);
});
});
describe("save", () => {
it("should save a single group and return it", async () => {
jest.spyOn(groupRepository, "save").mockResolvedValue([mockGroup]);
const result = await service.save(mockGroup);
expect(result).toEqual(mockGroup);
expect(groupRepository.save).toHaveBeenCalledWith([mockGroup]);
});
it("should return null if save operation fails", async () => {
jest.spyOn(groupRepository, "save").mockResolvedValue([]);
const result = await service.save(mockGroup);
expect(result).toBeNull();
});
});
describe("delete", () => {
it("should delete groups and return true on success", async () => {
jest.spyOn(groupRepository, "deleteBy").mockResolvedValue(true);
const result = await service.delete((g) => g.id === "group1");
expect(result).toBe(true);
expect(groupRepository.deleteBy).toHaveBeenCalledWith(
expect.any(Function)
);
});
it("should return false on deletion failure", async () => {
jest.spyOn(groupRepository, "deleteBy").mockResolvedValue(false);
const result = await service.delete((g) => g.id === "non-existent");
expect(result).toBe(false);
});
});
describe("addRightToGroup", () => {
it("should add a right to a group and return the updated group", async () => {
const updatedGroup = {
...mockGroup,
rights: [...mockGroup.rights, mockRight],
};
jest.spyOn(groupRepository, "getBy").mockResolvedValue([mockGroup]);
jest.spyOn(rightRepository, "getBy").mockResolvedValue([mockRight]);
jest.spyOn(groupRepository, "save").mockResolvedValue([updatedGroup]);
const result = await service.addRightToGroup("group1", "right2");
expect(result).toEqual(updatedGroup);
expect(groupRepository.getBy).toHaveBeenCalledWith(expect.any(Function));
expect(rightRepository.getBy).toHaveBeenCalledWith(expect.any(Function));
expect(groupRepository.save).toHaveBeenCalledWith([
expect.objectContaining({
rights: expect.arrayContaining([mockRight]),
}),
]);
});
it("should throw an error if the group is not found", async () => {
jest.spyOn(groupRepository, "getBy").mockResolvedValue([]);
jest.spyOn(rightRepository, "getBy").mockResolvedValue([mockRight]);
await expect(
service.addRightToGroup("non-existent", "right2")
).rejects.toThrow("group with id non-existent not found");
});
it("should throw an error if the right is not found", async () => {
jest.spyOn(groupRepository, "getBy").mockResolvedValue([mockGroup]);
jest.spyOn(rightRepository, "getBy").mockResolvedValue([]);
await expect(
service.addRightToGroup("group1", "non-existent")
).rejects.toThrow("right with id non-existent not found");
});
it("should throw an error if the updated group cannot be saved", async () => {
jest.spyOn(groupRepository, "getBy").mockResolvedValue([mockGroup]);
jest.spyOn(rightRepository, "getBy").mockResolvedValue([mockRight]);
jest.spyOn(groupRepository, "save").mockResolvedValue([]);
await expect(service.addRightToGroup("group1", "right2")).rejects.toThrow(
"group Admins can not be saved"
);
});
});
describe("removeRightFromGroup", () => {
it("should remove a right from a group and return the updated group", async () => {
const groupWithMultipleRights: IGroup = {
...mockGroup,
rights: [...mockGroup.rights, mockRight],
};
const updatedGroup = { ...groupWithMultipleRights, rights: [mockRight] };
jest
.spyOn(groupRepository, "getBy")
.mockResolvedValue([groupWithMultipleRights]);
jest.spyOn(rightRepository, "getBy").mockResolvedValue([mockRight]);
jest.spyOn(groupRepository, "save").mockResolvedValue([updatedGroup]);
const result = await service.removeRightFromGroup("group1", "right1");
expect(result).toEqual(updatedGroup);
expect(groupRepository.getBy).toHaveBeenCalledWith(expect.any(Function));
expect(rightRepository.getBy).toHaveBeenCalledWith(expect.any(Function));
expect(groupRepository.save).toHaveBeenCalledWith([
expect.objectContaining({
rights: expect.not.arrayContaining([mockGroup.rights[0]]),
}),
]);
});
it("should throw an error if the group cannot be saved", async () => {
jest.spyOn(groupRepository, "getBy").mockResolvedValue([mockGroup]);
jest.spyOn(rightRepository, "getBy").mockResolvedValue([mockRight]);
jest.spyOn(groupRepository, "save").mockResolvedValue([]);
await expect(
service.removeRightFromGroup("group1", "right1")
).rejects.toThrow("group Admins can not be saved");
});
});
});

View File

@ -0,0 +1,150 @@
import { Test, TestingModule } from "@nestjs/testing";
import { LoginService } from "./login.service";
import { MailServiceMockModule } from "../../test/mail.verification.service.mock";
import { PasswordServiceMockModule } from "../../test/password.service.mock";
import { SessionServiceMockModule } from "../../test/session.service.mock";
import { TokenServiceMockModule } from "../../test/token.service.mock";
import { AccountRepositoryMockModule } from "../../test/account.repository.mock";
import { GroupRepositoryMockModule } from "../../test/group.repository.mock";
import { RightRepositoryMockModule } from "../../test/right.repository.mock";
import { OrganizationRepositoryMockModule } from "../../test/organization.repository.mock";
import { AccountService, TokenAuthenticationModule } from "..";
import { Account } from "../models/account";
import { Session } from "../models/session";
import { SignIn } from "../models/sign.in";
import {
APIHUB24_PASSWORD_SERVICE,
APIHUB24_SESSION_SERVICE,
APIHUB24_TOKEN_SERVICE,
IPasswordService,
ISessionService,
ITokenService,
} from "@apihub24/authentication";
describe("LoginService Test", () => {
let module: TestingModule;
let service: LoginService;
let accountService: AccountService;
let passwordService: IPasswordService;
let sessionService: ISessionService;
let tokenService: ITokenService;
const mockAccount: Account = {
id: "account1",
accountName: "testuser",
passwordHash: "hashedpassword",
email: "testuser@example.de",
emailVerified: true,
active: true,
groups: [],
};
const mockSession: Session = {
id: "session1",
account: mockAccount,
metaData: {},
};
const mockSignIn: SignIn = {
accountName: "testuser",
password: "password123",
};
beforeAll(async () => {
module = await Test.createTestingModule({
imports: [
MailServiceMockModule.forRoot(),
PasswordServiceMockModule.forRoot(),
SessionServiceMockModule.forRoot(),
TokenServiceMockModule.forRoot(),
AccountRepositoryMockModule.forRoot(),
GroupRepositoryMockModule.forRoot(),
RightRepositoryMockModule.forRoot(),
OrganizationRepositoryMockModule.forRoot(),
TokenAuthenticationModule.forRoot(),
],
}).compile();
service = module.get(LoginService);
accountService = module.get(AccountService);
passwordService = module.get(APIHUB24_PASSWORD_SERVICE);
sessionService = module.get(APIHUB24_SESSION_SERVICE);
tokenService = module.get(APIHUB24_TOKEN_SERVICE);
jest.clearAllMocks();
});
it("should be defined", () => {
expect(service).toBeDefined();
});
describe("signIn", () => {
it("should return a token for a valid account", async () => {
jest.spyOn(accountService, "getBy").mockResolvedValue([mockAccount]);
jest.spyOn(passwordService, "verify").mockResolvedValue(true);
jest.spyOn(sessionService, "create").mockResolvedValue(mockSession);
jest.spyOn(tokenService, "generate").mockResolvedValue("jwt_token");
const result = await service.signIn(mockSignIn);
expect(accountService.getBy).toHaveBeenCalledWith(expect.any(Function));
expect(passwordService.verify).toHaveBeenCalledWith(
mockSignIn.password,
mockAccount.passwordHash
);
expect(sessionService.create).toHaveBeenCalledWith(mockAccount);
expect(tokenService.generate).toHaveBeenCalledWith(
mockSession,
mockSignIn.accountName,
"1h",
"HS512"
);
expect(result).toBe("jwt_token");
});
it("should return an empty string if account is not found", async () => {
jest.spyOn(accountService, "getBy").mockResolvedValue([]);
const result = await service.signIn(mockSignIn);
expect(result).toBe("");
});
it("should return an empty string if password verification fails", async () => {
jest.spyOn(accountService, "getBy").mockResolvedValue([mockAccount]);
jest.spyOn(passwordService, "verify").mockResolvedValue(false);
const result = await service.signIn(mockSignIn);
expect(result).toBe("");
});
it("should return an empty string if the account has no password hash", async () => {
const noPasswordHashAccount = { ...mockAccount, passwordHash: "" };
jest
.spyOn(accountService, "getBy")
.mockResolvedValue([noPasswordHashAccount]);
const result = await service.signIn(mockSignIn);
expect(result).toBe("");
});
});
describe("signOut", () => {
it("should successfully remove a session", async () => {
jest.spyOn(accountService, "getBy").mockResolvedValue([mockAccount]);
jest.spyOn(sessionService, "getBy").mockResolvedValue([mockSession]);
jest.spyOn(sessionService, "remove").mockResolvedValue();
await service.signOut("account1");
expect(accountService.getBy).toHaveBeenCalledWith(expect.any(Function));
expect(sessionService.getBy).toHaveBeenCalledWith(expect.any(Function));
expect(sessionService.remove).toHaveBeenCalledWith(mockSession.id);
});
it("should do nothing if the account is not found", async () => {
jest.spyOn(accountService, "getBy").mockResolvedValue([]);
await service.signOut("non-existent-id");
});
it("should do nothing if no session is found for the account", async () => {
jest.spyOn(accountService, "getBy").mockResolvedValue([mockAccount]);
jest.spyOn(sessionService, "getBy").mockResolvedValue([]);
await service.signOut("account1");
});
});
});

View File

@ -0,0 +1,49 @@
import { Test, TestingModule } from "@nestjs/testing";
import { OrganizationFactoryService } from "./organization.factory.service";
import { MailServiceMockModule } from "../../test/mail.verification.service.mock";
import { PasswordServiceMockModule } from "../../test/password.service.mock";
import { SessionServiceMockModule } from "../../test/session.service.mock";
import { TokenServiceMockModule } from "../../test/token.service.mock";
import { AccountRepositoryMockModule } from "../../test/account.repository.mock";
import { GroupRepositoryMockModule } from "../../test/group.repository.mock";
import { RightRepositoryMockModule } from "../../test/right.repository.mock";
import { OrganizationRepositoryMockModule } from "../../test/organization.repository.mock";
import { TokenAuthenticationModule } from "..";
import { Organization } from "../models/organization";
describe("OrganizationFactoryService Tests", () => {
let module: TestingModule;
let service: OrganizationFactoryService;
beforeAll(async () => {
module = await Test.createTestingModule({
imports: [
MailServiceMockModule.forRoot(),
PasswordServiceMockModule.forRoot(),
SessionServiceMockModule.forRoot(),
TokenServiceMockModule.forRoot(),
AccountRepositoryMockModule.forRoot(),
GroupRepositoryMockModule.forRoot(),
RightRepositoryMockModule.forRoot(),
OrganizationRepositoryMockModule.forRoot(),
TokenAuthenticationModule.forRoot(),
],
}).compile();
service = module.get(OrganizationFactoryService);
jest.clearAllMocks();
});
it("should be defined", () => {
expect(service).toBeDefined();
});
it("should create an organization with the correct name", () => {
const orgName = "Test Organization";
const result = service.createFromName(orgName);
expect(result).toBeInstanceOf(Organization);
expect(result.name).toBe(orgName);
});
});

View File

@ -0,0 +1,130 @@
import { Test, TestingModule } from "@nestjs/testing";
import { OrganizationService } from "./organization.service";
import { IRepository } from "@apihub24/repository";
import {
APIHUB24_ORGANIZATION_REPOSITORY,
IOrganization,
} from "@apihub24/authentication";
import { MailServiceMockModule } from "../../test/mail.verification.service.mock";
import { PasswordServiceMockModule } from "../../test/password.service.mock";
import { SessionServiceMockModule } from "../../test/session.service.mock";
import { TokenServiceMockModule } from "../../test/token.service.mock";
import { AccountRepositoryMockModule } from "../../test/account.repository.mock";
import { GroupRepositoryMockModule } from "../../test/group.repository.mock";
import { RightRepositoryMockModule } from "../../test/right.repository.mock";
import { OrganizationRepositoryMockModule } from "../../test/organization.repository.mock";
import { TokenAuthenticationModule } from "../token.authentication.module";
describe("OrganizationService Tests", () => {
let module: TestingModule;
let service: OrganizationService;
let organizationRepository: IRepository<IOrganization>;
const mockOrganization: IOrganization = {
id: "org1",
name: "Test Organization",
};
beforeAll(async () => {
module = await Test.createTestingModule({
imports: [
MailServiceMockModule.forRoot(),
PasswordServiceMockModule.forRoot(),
SessionServiceMockModule.forRoot(),
TokenServiceMockModule.forRoot(),
AccountRepositoryMockModule.forRoot(),
GroupRepositoryMockModule.forRoot(),
RightRepositoryMockModule.forRoot(),
OrganizationRepositoryMockModule.forRoot(),
TokenAuthenticationModule.forRoot(),
],
}).compile();
service = module.get(OrganizationService);
organizationRepository = module.get(APIHUB24_ORGANIZATION_REPOSITORY);
jest.clearAllMocks();
});
it("should be defined", () => {
expect(service).toBeDefined();
});
describe("getBy", () => {
it("should return organizations matching the filter", async () => {
jest
.spyOn(organizationRepository, "getBy")
.mockResolvedValue([mockOrganization]);
const result = await service.getBy((org) => org.id === "org1");
expect(result).toEqual([mockOrganization]);
expect(organizationRepository.getBy).toHaveBeenCalledWith(
expect.any(Function)
);
});
it("should return an empty array if no organizations match", async () => {
jest.spyOn(organizationRepository, "getBy").mockResolvedValue([]);
const result = await service.getBy((org) => org.id === "non-existent-id");
expect(result).toEqual([]);
expect(organizationRepository.getBy).toHaveBeenCalledWith(
expect.any(Function)
);
});
});
describe("save", () => {
it("should save a single organization and return it", async () => {
jest
.spyOn(organizationRepository, "save")
.mockResolvedValue([mockOrganization]);
const result = await service.save(mockOrganization);
expect(result).toEqual(mockOrganization);
expect(organizationRepository.save).toHaveBeenCalledWith([
mockOrganization,
]);
});
it("should throw an error if the save operation fails", async () => {
jest.spyOn(organizationRepository, "save").mockResolvedValue([]);
await expect(service.save(mockOrganization)).rejects.toThrow(
`organization (${mockOrganization.name}) not saved`
);
expect(organizationRepository.save).toHaveBeenCalledWith([
mockOrganization,
]);
});
});
describe("deleteBy", () => {
it("should delete organizations and return true on success", async () => {
jest.spyOn(organizationRepository, "deleteBy").mockResolvedValue(true);
const result = await service.deleteBy((org) => org.id === "org1");
expect(result).toBe(true);
expect(organizationRepository.deleteBy).toHaveBeenCalledWith(
expect.any(Function)
);
});
it("should return false on deletion failure", async () => {
jest.spyOn(organizationRepository, "deleteBy").mockResolvedValue(false);
const result = await service.deleteBy(
(org) => org.id === "non-existent-id"
);
expect(result).toBe(false);
expect(organizationRepository.deleteBy).toHaveBeenCalledWith(
expect.any(Function)
);
});
});
});

View File

@ -0,0 +1,207 @@
import { Test, TestingModule } from "@nestjs/testing";
import { RegistrationService } from "./registration.service";
import { MailServiceMockModule } from "../../test/mail.verification.service.mock";
import { PasswordServiceMockModule } from "../../test/password.service.mock";
import { SessionServiceMockModule } from "../../test/session.service.mock";
import { TokenServiceMockModule } from "../../test/token.service.mock";
import { AccountRepositoryMockModule } from "../../test/account.repository.mock";
import { GroupRepositoryMockModule } from "../../test/group.repository.mock";
import { RightRepositoryMockModule } from "../../test/right.repository.mock";
import { OrganizationRepositoryMockModule } from "../../test/organization.repository.mock";
import {
AccountFactoryService,
AccountService,
TokenAuthenticationModule,
} from "..";
import { Account } from "../models/account";
import {
APIHUB24_MAIL_SERVICE,
IMailVerificationService,
IRegistration,
} from "@apihub24/authentication";
describe("RegistrationService Tests", () => {
let module: TestingModule;
let service: RegistrationService;
let accountFactoryService: AccountFactoryService;
let accountService: AccountService;
let mailVerificationService: IMailVerificationService;
const mockRegistration: IRegistration = {
accountName: "testuser",
email: "test@example.com",
password: "password123",
passwordComparer: "password123",
groupIds: [],
};
const mockAccount: Account = {
id: "account1",
accountName: "testuser",
email: "test@example.com",
emailVerified: false,
active: false,
passwordHash: "passwordhash",
groups: [],
};
beforeAll(async () => {
module = await Test.createTestingModule({
imports: [
MailServiceMockModule.forRoot(),
PasswordServiceMockModule.forRoot(),
SessionServiceMockModule.forRoot(),
TokenServiceMockModule.forRoot(),
AccountRepositoryMockModule.forRoot(),
GroupRepositoryMockModule.forRoot(),
RightRepositoryMockModule.forRoot(),
OrganizationRepositoryMockModule.forRoot(),
TokenAuthenticationModule.forRoot(),
],
}).compile();
service = module.get(RegistrationService);
accountFactoryService = module.get(AccountFactoryService);
accountService = module.get(AccountService);
mailVerificationService = module.get(APIHUB24_MAIL_SERVICE);
jest.clearAllMocks();
});
it("should be defined", () => {
expect(service).toBeDefined();
});
describe("registerAccount", () => {
it("should register a new account and return it", async () => {
jest
.spyOn(accountFactoryService, "createFromRegistration")
.mockResolvedValue(mockAccount);
jest.spyOn(accountService, "save").mockResolvedValue(mockAccount);
const result = await service.registerAccount(mockRegistration);
expect(accountFactoryService.createFromRegistration).toHaveBeenCalledWith(
mockRegistration
);
expect(accountService.save).toHaveBeenCalledWith(mockAccount);
expect(result).toEqual(mockAccount);
});
it("should return null if account saving fails", async () => {
jest
.spyOn(accountFactoryService, "createFromRegistration")
.mockResolvedValue(mockAccount);
jest.spyOn(accountService, "save").mockResolvedValue(null);
const result = await service.registerAccount(mockRegistration);
expect(result).toBeNull();
});
});
describe("verify", () => {
it("should verify an email and update unverified accounts", async () => {
const unverifiedAccount = { ...mockAccount, emailVerified: false };
const verifiedAccount = { ...mockAccount, emailVerified: true };
jest.spyOn(mailVerificationService, "verify").mockResolvedValue(true);
jest
.spyOn(accountService, "getBy")
.mockResolvedValue([unverifiedAccount]);
jest.spyOn(accountService, "save").mockResolvedValue(verifiedAccount);
const result = await service.verify("test@example.com", "123456");
expect(mailVerificationService.verify).toHaveBeenCalledWith(
"test@example.com",
"123456"
);
expect(accountService.getBy).toHaveBeenCalledWith(expect.any(Function));
expect(accountService.save).toHaveBeenCalledWith(
expect.objectContaining({ emailVerified: true })
);
expect(result).toBe(true);
});
it("should return false if verification code is invalid", async () => {
jest.spyOn(mailVerificationService, "verify").mockResolvedValue(false);
const result = await service.verify("test@example.com", "invalid-code");
expect(mailVerificationService.verify).toHaveBeenCalledWith(
"test@example.com",
"invalid-code"
);
expect(result).toBe(false);
});
});
describe("activateAccount", () => {
it("should activate an account and return true", async () => {
const activeAccount = { ...mockAccount, active: true };
jest.spyOn(accountService, "getBy").mockResolvedValue([mockAccount]);
jest.spyOn(accountService, "save").mockResolvedValue(activeAccount);
const result = await service.activateAccount("account1");
expect(accountService.getBy).toHaveBeenCalledWith(expect.any(Function));
expect(accountService.save).toHaveBeenCalledWith(
expect.objectContaining({ active: true })
);
expect(result).toBe(true);
});
it("should return false if the account is not found", async () => {
jest.spyOn(accountService, "getBy").mockResolvedValue([]);
const result = await service.activateAccount("non-existent-id");
expect(accountService.getBy).toHaveBeenCalledWith(expect.any(Function));
expect(result).toBe(false);
});
});
describe("deactivateAccount", () => {
it("should deactivate an account and return true", async () => {
const deactivatedAccount = { ...mockAccount, active: false };
jest.spyOn(accountService, "getBy").mockResolvedValue([mockAccount]);
jest.spyOn(accountService, "save").mockResolvedValue(deactivatedAccount);
const result = await service.deactivateAccount("account1");
expect(accountService.getBy).toHaveBeenCalledWith(expect.any(Function));
expect(accountService.save).toHaveBeenCalledWith(
expect.objectContaining({ active: false })
);
expect(result).toBe(true);
});
it("should return true if the account is not found", async () => {
jest.spyOn(accountService, "getBy").mockResolvedValue([]);
const result = await service.deactivateAccount("non-existent-id");
expect(accountService.getBy).toHaveBeenCalledWith(expect.any(Function));
expect(result).toBe(true);
});
});
describe("unregisterAccount", () => {
it("should delete an account if it exists", async () => {
jest.spyOn(accountService, "getBy").mockResolvedValue([mockAccount]);
jest.spyOn(accountService, "delete").mockResolvedValue(true);
await service.unregisterAccount("account1");
expect(accountService.getBy).toHaveBeenCalledWith(expect.any(Function));
expect(accountService.delete).toHaveBeenCalledWith(expect.any(Function));
});
it("should do nothing if the account is not found", async () => {
jest.spyOn(accountService, "getBy").mockResolvedValue([]);
await service.unregisterAccount("non-existent-id");
expect(accountService.getBy).toHaveBeenCalledWith(expect.any(Function));
});
});
});

View File

@ -0,0 +1,70 @@
import { Test, TestingModule } from "@nestjs/testing";
import { RightFactoryService } from "./right.factory.service";
import { MailServiceMockModule } from "../../test/mail.verification.service.mock";
import { PasswordServiceMockModule } from "../../test/password.service.mock";
import { SessionServiceMockModule } from "../../test/session.service.mock";
import { TokenServiceMockModule } from "../../test/token.service.mock";
import { AccountRepositoryMockModule } from "../../test/account.repository.mock";
import { GroupRepositoryMockModule } from "../../test/group.repository.mock";
import { RightRepositoryMockModule } from "../../test/right.repository.mock";
import { OrganizationRepositoryMockModule } from "../../test/organization.repository.mock";
import { TokenAuthenticationModule } from "..";
import * as validator from "class-validator";
import { Right } from "../models/right";
describe("RightFactoryService Tests", () => {
let module: TestingModule;
let service: RightFactoryService;
beforeAll(async () => {
module = await Test.createTestingModule({
imports: [
MailServiceMockModule.forRoot(),
PasswordServiceMockModule.forRoot(),
SessionServiceMockModule.forRoot(),
TokenServiceMockModule.forRoot(),
AccountRepositoryMockModule.forRoot(),
GroupRepositoryMockModule.forRoot(),
RightRepositoryMockModule.forRoot(),
OrganizationRepositoryMockModule.forRoot(),
TokenAuthenticationModule.forRoot(),
],
}).compile();
service = module.get(RightFactoryService);
jest.clearAllMocks();
});
it("should be defined", () => {
expect(service).toBeDefined();
});
describe("createRight", () => {
it("should create a right successfully with a valid name", async () => {
const rightName = "read:articles";
jest.spyOn(validator, "validate").mockResolvedValue([]);
const result = await service.createRight(rightName);
expect(validator.validate).toHaveBeenCalled();
expect(result).toBeInstanceOf(Right);
expect(result.name).toBe(rightName);
});
it("should throw an error if validation fails", async () => {
const rightName = "";
const validationError = [
{
toString: () => "name must not be empty",
} as validator.ValidationError,
];
jest.spyOn(validator, "validate").mockResolvedValue(validationError);
await expect(service.createRight(rightName)).rejects.toThrow(
"name must not be empty"
);
expect(validator.validate).toHaveBeenCalled();
});
});
});

View File

@ -0,0 +1,113 @@
import { Test, TestingModule } from "@nestjs/testing";
import { RightService } from "./right.service";
import { IRepository } from "@apihub24/repository";
import { APIHUB24_RIGHT_REPOSITORY, IRight } from "@apihub24/authentication";
import { MailServiceMockModule } from "../../test/mail.verification.service.mock";
import { PasswordServiceMockModule } from "../../test/password.service.mock";
import { SessionServiceMockModule } from "../../test/session.service.mock";
import { TokenServiceMockModule } from "../../test/token.service.mock";
import { AccountRepositoryMockModule } from "../../test/account.repository.mock";
import { GroupRepositoryMockModule } from "../../test/group.repository.mock";
import { RightRepositoryMockModule } from "../../test/right.repository.mock";
import { OrganizationRepositoryMockModule } from "../../test/organization.repository.mock";
import { TokenAuthenticationModule } from "..";
describe("RightService Tests", () => {
let module: TestingModule;
let service: RightService;
let rightRepository: IRepository<IRight>;
const mockRight: IRight = {
id: "right1",
name: "read:users",
};
beforeAll(async () => {
module = await Test.createTestingModule({
imports: [
MailServiceMockModule.forRoot(),
PasswordServiceMockModule.forRoot(),
SessionServiceMockModule.forRoot(),
TokenServiceMockModule.forRoot(),
AccountRepositoryMockModule.forRoot(),
GroupRepositoryMockModule.forRoot(),
RightRepositoryMockModule.forRoot(),
OrganizationRepositoryMockModule.forRoot(),
TokenAuthenticationModule.forRoot(),
],
}).compile();
service = module.get(RightService);
rightRepository = module.get(APIHUB24_RIGHT_REPOSITORY);
jest.clearAllMocks();
});
it("should be defined", () => {
expect(service).toBeDefined();
});
describe("getBy", () => {
it("should return rights that match the filter", async () => {
jest.spyOn(rightRepository, "getBy").mockResolvedValue([mockRight]);
const result = await service.getBy((r) => r.id === "right1");
expect(result).toEqual([mockRight]);
expect(rightRepository.getBy).toHaveBeenCalledWith(expect.any(Function));
});
it("should return an empty array if no rights match", async () => {
jest.spyOn(rightRepository, "getBy").mockResolvedValue([]);
const result = await service.getBy((r) => r.id === "non-existent-id");
expect(result).toEqual([]);
expect(rightRepository.getBy).toHaveBeenCalledWith(expect.any(Function));
});
});
describe("save", () => {
it("should save a right and return it on success", async () => {
jest.spyOn(rightRepository, "save").mockResolvedValue([mockRight]);
const result = await service.save(mockRight);
expect(result).toEqual(mockRight);
expect(rightRepository.save).toHaveBeenCalledWith([mockRight]);
});
it("should return null if the save operation fails", async () => {
jest.spyOn(rightRepository, "save").mockResolvedValue([]);
const result = await service.save(mockRight);
expect(result).toBeNull();
expect(rightRepository.save).toHaveBeenCalledWith([mockRight]);
});
});
describe("delete", () => {
it("should return true if the deletion is successful", async () => {
jest.spyOn(rightRepository, "deleteBy").mockResolvedValue(true);
const result = await service.delete((r) => r.id === "right1");
expect(result).toBe(true);
expect(rightRepository.deleteBy).toHaveBeenCalledWith(
expect.any(Function)
);
});
it("should return false if the deletion fails", async () => {
jest.spyOn(rightRepository, "deleteBy").mockResolvedValue(false);
const result = await service.delete((r) => r.id === "non-existent-id");
expect(result).toBe(false);
expect(rightRepository.deleteBy).toHaveBeenCalledWith(
expect.any(Function)
);
});
});
});