core tests

This commit is contained in:
2026-01-24 01:53:04 +01:00
parent 5e12570442
commit 78c9c1ec75
4 changed files with 2155 additions and 0 deletions

View File

@@ -0,0 +1,303 @@
import { describe, expect, it } from 'vitest';
import { AdminDomainError, AdminDomainValidationError, AdminDomainInvariantError, AuthorizationError } from './AdminDomainError';
describe('AdminDomainError', () => {
describe('TDD - Test First', () => {
describe('AdminDomainError', () => {
it('should create an error with correct properties', () => {
// Arrange & Act
const error = new (class extends AdminDomainError {
readonly kind = 'validation' as const;
})('Test error message');
// Assert
expect(error.message).toBe('Test error message');
expect(error.type).toBe('domain');
expect(error.context).toBe('admin-domain');
expect(error.kind).toBe('validation');
});
it('should have correct error name', () => {
// Arrange & Act
const error = new (class extends AdminDomainError {
readonly kind = 'validation' as const;
})('Test error');
// Assert
expect(error.name).toBe('AdminDomainError');
});
it('should preserve prototype chain', () => {
// Arrange & Act
const error = new (class extends AdminDomainError {
readonly kind = 'validation' as const;
})('Test error');
// Assert
expect(error instanceof AdminDomainError).toBe(true);
expect(error instanceof Error).toBe(true);
});
it('should handle empty message', () => {
// Arrange & Act
const error = new (class extends AdminDomainError {
readonly kind = 'validation' as const;
})('');
// Assert
expect(error.message).toBe('');
});
it('should handle long message', () => {
// Arrange
const longMessage = 'This is a very long error message that contains many characters and should be handled correctly by the error class';
// Act
const error = new (class extends AdminDomainError {
readonly kind = 'validation' as const;
})(longMessage);
// Assert
expect(error.message).toBe(longMessage);
});
});
describe('AdminDomainValidationError', () => {
it('should create a validation error', () => {
// Arrange & Act
const error = new AdminDomainValidationError('Invalid email format');
// Assert
expect(error.message).toBe('Invalid email format');
expect(error.type).toBe('domain');
expect(error.context).toBe('admin-domain');
expect(error.kind).toBe('validation');
});
it('should have correct error name', () => {
// Arrange & Act
const error = new AdminDomainValidationError('Test error');
// Assert
expect(error.name).toBe('AdminDomainValidationError');
});
it('should be instance of AdminDomainError', () => {
// Arrange & Act
const error = new AdminDomainValidationError('Test error');
// Assert
expect(error instanceof AdminDomainError).toBe(true);
expect(error instanceof AdminDomainValidationError).toBe(true);
expect(error instanceof Error).toBe(true);
});
it('should handle empty message', () => {
// Arrange & Act
const error = new AdminDomainValidationError('');
// Assert
expect(error.message).toBe('');
});
it('should handle complex validation message', () => {
// Arrange
const message = 'Field "email" must be a valid email address. Received: "invalid-email"';
// Act
const error = new AdminDomainValidationError(message);
// Assert
expect(error.message).toBe(message);
});
});
describe('AdminDomainInvariantError', () => {
it('should create an invariant error', () => {
// Arrange & Act
const error = new AdminDomainInvariantError('User must have at least one role');
// Assert
expect(error.message).toBe('User must have at least one role');
expect(error.type).toBe('domain');
expect(error.context).toBe('admin-domain');
expect(error.kind).toBe('invariant');
});
it('should have correct error name', () => {
// Arrange & Act
const error = new AdminDomainInvariantError('Test error');
// Assert
expect(error.name).toBe('AdminDomainInvariantError');
});
it('should be instance of AdminDomainError', () => {
// Arrange & Act
const error = new AdminDomainInvariantError('Test error');
// Assert
expect(error instanceof AdminDomainError).toBe(true);
expect(error instanceof AdminDomainInvariantError).toBe(true);
expect(error instanceof Error).toBe(true);
});
it('should handle empty message', () => {
// Arrange & Act
const error = new AdminDomainInvariantError('');
// Assert
expect(error.message).toBe('');
});
it('should handle complex invariant message', () => {
// Arrange
const message = 'Invariant violation: User status "active" cannot be changed to "deleted" without proper authorization';
// Act
const error = new AdminDomainInvariantError(message);
// Assert
expect(error.message).toBe(message);
});
});
describe('AuthorizationError', () => {
it('should create an authorization error', () => {
// Arrange & Act
const error = new AuthorizationError('User does not have permission to perform this action');
// Assert
expect(error.message).toBe('User does not have permission to perform this action');
expect(error.type).toBe('domain');
expect(error.context).toBe('admin-domain');
expect(error.kind).toBe('authorization');
});
it('should have correct error name', () => {
// Arrange & Act
const error = new AuthorizationError('Test error');
// Assert
expect(error.name).toBe('AuthorizationError');
});
it('should be instance of AdminDomainError', () => {
// Arrange & Act
const error = new AuthorizationError('Test error');
// Assert
expect(error instanceof AdminDomainError).toBe(true);
expect(error instanceof AuthorizationError).toBe(true);
expect(error instanceof Error).toBe(true);
});
it('should handle empty message', () => {
// Arrange & Act
const error = new AuthorizationError('');
// Assert
expect(error.message).toBe('');
});
it('should handle complex authorization message', () => {
// Arrange
const message = 'Authorization failed: User "admin@example.com" (role: admin) attempted to modify role of user "owner@example.com" (role: owner)';
// Act
const error = new AuthorizationError(message);
// Assert
expect(error.message).toBe(message);
});
});
describe('Error hierarchy', () => {
it('should have correct inheritance chain for AdminDomainValidationError', () => {
// Arrange & Act
const error = new AdminDomainValidationError('Test');
// Assert
expect(error instanceof AdminDomainError).toBe(true);
expect(error instanceof Error).toBe(true);
});
it('should have correct inheritance chain for AdminDomainInvariantError', () => {
// Arrange & Act
const error = new AdminDomainInvariantError('Test');
// Assert
expect(error instanceof AdminDomainError).toBe(true);
expect(error instanceof Error).toBe(true);
});
it('should have correct inheritance chain for AuthorizationError', () => {
// Arrange & Act
const error = new AuthorizationError('Test');
// Assert
expect(error instanceof AdminDomainError).toBe(true);
expect(error instanceof Error).toBe(true);
});
it('should have consistent type and context across all error types', () => {
// Arrange
const errors = [
new AdminDomainValidationError('Test'),
new AdminDomainInvariantError('Test'),
new AuthorizationError('Test'),
];
// Assert
errors.forEach(error => {
expect(error.type).toBe('domain');
expect(error.context).toBe('admin-domain');
});
});
it('should have different kinds for different error types', () => {
// Arrange
const validationError = new AdminDomainValidationError('Test');
const invariantError = new AdminDomainInvariantError('Test');
const authorizationError = new AuthorizationError('Test');
// Assert
expect(validationError.kind).toBe('validation');
expect(invariantError.kind).toBe('invariant');
expect(authorizationError.kind).toBe('authorization');
});
});
describe('Error stack trace', () => {
it('should have a stack trace', () => {
// Arrange & Act
const error = new AdminDomainValidationError('Test error');
// Assert
expect(error.stack).toBeDefined();
expect(typeof error.stack).toBe('string');
expect(error.stack).toContain('AdminDomainValidationError');
});
it('should have stack trace for AdminDomainInvariantError', () => {
// Arrange & Act
const error = new AdminDomainInvariantError('Test error');
// Assert
expect(error.stack).toBeDefined();
expect(typeof error.stack).toBe('string');
expect(error.stack).toContain('AdminDomainInvariantError');
});
it('should have stack trace for AuthorizationError', () => {
// Arrange & Act
const error = new AuthorizationError('Test error');
// Assert
expect(error.stack).toBeDefined();
expect(typeof error.stack).toBe('string');
expect(error.stack).toContain('AuthorizationError');
});
});
});
});

View File

@@ -0,0 +1,721 @@
import { describe, expect, it, vi } from 'vitest';
import { AdminUser } from '../entities/AdminUser';
import { Email } from '../value-objects/Email';
import { UserId } from '../value-objects/UserId';
import { UserRole } from '../value-objects/UserRole';
import { UserStatus } from '../value-objects/UserStatus';
import type {
AdminUserRepository,
UserFilter,
UserSort,
UserPagination,
UserListQuery,
UserListResult,
StoredAdminUser
} from './AdminUserRepository';
describe('AdminUserRepository', () => {
describe('TDD - Test First', () => {
describe('UserFilter interface', () => {
it('should allow optional role filter', () => {
// Arrange
const filter: UserFilter = {
role: UserRole.fromString('admin'),
};
// Assert
expect(filter.role).toBeDefined();
expect(filter.role!.value).toBe('admin');
});
it('should allow optional status filter', () => {
// Arrange
const filter: UserFilter = {
status: UserStatus.fromString('active'),
};
// Assert
expect(filter.status).toBeDefined();
expect(filter.status!.value).toBe('active');
});
it('should allow optional email filter', () => {
// Arrange
const filter: UserFilter = {
email: Email.create('test@example.com'),
};
// Assert
expect(filter.email).toBeDefined();
expect(filter.email!.value).toBe('test@example.com');
});
it('should allow optional search filter', () => {
// Arrange
const filter: UserFilter = {
search: 'john',
};
// Assert
expect(filter.search).toBe('john');
});
it('should allow all filters combined', () => {
// Arrange
const filter: UserFilter = {
role: UserRole.fromString('admin'),
status: UserStatus.fromString('active'),
email: Email.create('admin@example.com'),
search: 'admin',
};
// Assert
expect(filter.role!.value).toBe('admin');
expect(filter.status!.value).toBe('active');
expect(filter.email!.value).toBe('admin@example.com');
expect(filter.search).toBe('admin');
});
});
describe('UserSort interface', () => {
it('should allow email field with asc direction', () => {
// Arrange
const sort: UserSort = {
field: 'email',
direction: 'asc',
};
// Assert
expect(sort.field).toBe('email');
expect(sort.direction).toBe('asc');
});
it('should allow email field with desc direction', () => {
// Arrange
const sort: UserSort = {
field: 'email',
direction: 'desc',
};
// Assert
expect(sort.field).toBe('email');
expect(sort.direction).toBe('desc');
});
it('should allow displayName field', () => {
// Arrange
const sort: UserSort = {
field: 'displayName',
direction: 'asc',
};
// Assert
expect(sort.field).toBe('displayName');
});
it('should allow createdAt field', () => {
// Arrange
const sort: UserSort = {
field: 'createdAt',
direction: 'desc',
};
// Assert
expect(sort.field).toBe('createdAt');
});
it('should allow lastLoginAt field', () => {
// Arrange
const sort: UserSort = {
field: 'lastLoginAt',
direction: 'asc',
};
// Assert
expect(sort.field).toBe('lastLoginAt');
});
it('should allow status field', () => {
// Arrange
const sort: UserSort = {
field: 'status',
direction: 'desc',
};
// Assert
expect(sort.field).toBe('status');
});
});
describe('UserPagination interface', () => {
it('should allow valid pagination', () => {
// Arrange
const pagination: UserPagination = {
page: 1,
limit: 10,
};
// Assert
expect(pagination.page).toBe(1);
expect(pagination.limit).toBe(10);
});
it('should allow pagination with different values', () => {
// Arrange
const pagination: UserPagination = {
page: 5,
limit: 50,
};
// Assert
expect(pagination.page).toBe(5);
expect(pagination.limit).toBe(50);
});
});
describe('UserListQuery interface', () => {
it('should allow query with all optional fields', () => {
// Arrange
const query: UserListQuery = {
filter: {
role: UserRole.fromString('admin'),
},
sort: {
field: 'email',
direction: 'asc',
},
pagination: {
page: 1,
limit: 10,
},
};
// Assert
expect(query.filter).toBeDefined();
expect(query.sort).toBeDefined();
expect(query.pagination).toBeDefined();
});
it('should allow query with only filter', () => {
// Arrange
const query: UserListQuery = {
filter: {
status: UserStatus.fromString('active'),
},
};
// Assert
expect(query.filter).toBeDefined();
expect(query.sort).toBeUndefined();
expect(query.pagination).toBeUndefined();
});
it('should allow query with only sort', () => {
// Arrange
const query: UserListQuery = {
sort: {
field: 'displayName',
direction: 'desc',
},
};
// Assert
expect(query.filter).toBeUndefined();
expect(query.sort).toBeDefined();
expect(query.pagination).toBeUndefined();
});
it('should allow query with only pagination', () => {
// Arrange
const query: UserListQuery = {
pagination: {
page: 2,
limit: 20,
},
};
// Assert
expect(query.filter).toBeUndefined();
expect(query.sort).toBeUndefined();
expect(query.pagination).toBeDefined();
});
it('should allow empty query', () => {
// Arrange
const query: UserListQuery = {};
// Assert
expect(query.filter).toBeUndefined();
expect(query.sort).toBeUndefined();
expect(query.pagination).toBeUndefined();
});
});
describe('UserListResult interface', () => {
it('should allow valid result with users', () => {
// Arrange
const user = AdminUser.create({
id: 'user-1',
email: 'test@example.com',
displayName: 'Test User',
roles: ['user'],
status: 'active',
});
const result: UserListResult = {
users: [user],
total: 1,
page: 1,
limit: 10,
totalPages: 1,
};
// Assert
expect(result.users).toHaveLength(1);
expect(result.total).toBe(1);
expect(result.page).toBe(1);
expect(result.limit).toBe(10);
expect(result.totalPages).toBe(1);
});
it('should allow result with multiple users', () => {
// Arrange
const user1 = AdminUser.create({
id: 'user-1',
email: 'user1@example.com',
displayName: 'User 1',
roles: ['user'],
status: 'active',
});
const user2 = AdminUser.create({
id: 'user-2',
email: 'user2@example.com',
displayName: 'User 2',
roles: ['user'],
status: 'active',
});
const result: UserListResult = {
users: [user1, user2],
total: 2,
page: 1,
limit: 10,
totalPages: 1,
};
// Assert
expect(result.users).toHaveLength(2);
expect(result.total).toBe(2);
});
it('should allow result with pagination info', () => {
// Arrange
const users = Array.from({ length: 50 }, (_, i) =>
AdminUser.create({
id: `user-${i}`,
email: `user${i}@example.com`,
displayName: `User ${i}`,
roles: ['user'],
status: 'active',
}),
);
const result: UserListResult = {
users: users.slice(0, 10),
total: 50,
page: 1,
limit: 10,
totalPages: 5,
};
// Assert
expect(result.users).toHaveLength(10);
expect(result.total).toBe(50);
expect(result.page).toBe(1);
expect(result.limit).toBe(10);
expect(result.totalPages).toBe(5);
});
});
describe('StoredAdminUser interface', () => {
it('should allow stored user with all required fields', () => {
// Arrange
const stored: StoredAdminUser = {
id: 'user-1',
email: 'test@example.com',
roles: ['admin'],
status: 'active',
displayName: 'Test User',
createdAt: new Date('2024-01-01'),
updatedAt: new Date('2024-01-02'),
};
// Assert
expect(stored.id).toBe('user-1');
expect(stored.email).toBe('test@example.com');
expect(stored.roles).toEqual(['admin']);
expect(stored.status).toBe('active');
expect(stored.displayName).toBe('Test User');
expect(stored.createdAt).toBeInstanceOf(Date);
expect(stored.updatedAt).toBeInstanceOf(Date);
});
it('should allow stored user with optional fields', () => {
// Arrange
const stored: StoredAdminUser = {
id: 'user-1',
email: 'test@example.com',
roles: ['admin'],
status: 'active',
displayName: 'Test User',
createdAt: new Date('2024-01-01'),
updatedAt: new Date('2024-01-02'),
lastLoginAt: new Date('2024-01-03'),
primaryDriverId: 'driver-123',
};
// Assert
expect(stored.lastLoginAt).toBeInstanceOf(Date);
expect(stored.primaryDriverId).toBe('driver-123');
});
it('should allow stored user with multiple roles', () => {
// Arrange
const stored: StoredAdminUser = {
id: 'user-1',
email: 'test@example.com',
roles: ['owner', 'admin', 'user'],
status: 'active',
displayName: 'Test User',
createdAt: new Date('2024-01-01'),
updatedAt: new Date('2024-01-02'),
};
// Assert
expect(stored.roles).toHaveLength(3);
expect(stored.roles).toContain('owner');
expect(stored.roles).toContain('admin');
expect(stored.roles).toContain('user');
});
});
describe('Repository interface methods', () => {
it('should define findById method signature', () => {
// Arrange
const mockRepository: AdminUserRepository = {
findById: vi.fn(),
findByEmail: vi.fn(),
emailExists: vi.fn(),
existsById: vi.fn(),
existsByEmail: vi.fn(),
list: vi.fn(),
count: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
toStored: vi.fn(),
fromStored: vi.fn(),
};
// Assert
expect(mockRepository.findById).toBeDefined();
expect(typeof mockRepository.findById).toBe('function');
});
it('should define findByEmail method signature', () => {
// Arrange
const mockRepository: AdminUserRepository = {
findById: vi.fn(),
findByEmail: vi.fn(),
emailExists: vi.fn(),
existsById: vi.fn(),
existsByEmail: vi.fn(),
list: vi.fn(),
count: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
toStored: vi.fn(),
fromStored: vi.fn(),
};
// Assert
expect(mockRepository.findByEmail).toBeDefined();
expect(typeof mockRepository.findByEmail).toBe('function');
});
it('should define emailExists method signature', () => {
// Arrange
const mockRepository: AdminUserRepository = {
findById: vi.fn(),
findByEmail: vi.fn(),
emailExists: vi.fn(),
existsById: vi.fn(),
existsByEmail: vi.fn(),
list: vi.fn(),
count: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
toStored: vi.fn(),
fromStored: vi.fn(),
};
// Assert
expect(mockRepository.emailExists).toBeDefined();
expect(typeof mockRepository.emailExists).toBe('function');
});
it('should define existsById method signature', () => {
// Arrange
const mockRepository: AdminUserRepository = {
findById: vi.fn(),
findByEmail: vi.fn(),
emailExists: vi.fn(),
existsById: vi.fn(),
existsByEmail: vi.fn(),
list: vi.fn(),
count: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
toStored: vi.fn(),
fromStored: vi.fn(),
};
// Assert
expect(mockRepository.existsById).toBeDefined();
expect(typeof mockRepository.existsById).toBe('function');
});
it('should define existsByEmail method signature', () => {
// Arrange
const mockRepository: AdminUserRepository = {
findById: vi.fn(),
findByEmail: vi.fn(),
emailExists: vi.fn(),
existsById: vi.fn(),
existsByEmail: vi.fn(),
list: vi.fn(),
count: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
toStored: vi.fn(),
fromStored: vi.fn(),
};
// Assert
expect(mockRepository.existsByEmail).toBeDefined();
expect(typeof mockRepository.existsByEmail).toBe('function');
});
it('should define list method signature', () => {
// Arrange
const mockRepository: AdminUserRepository = {
findById: vi.fn(),
findByEmail: vi.fn(),
emailExists: vi.fn(),
existsById: vi.fn(),
existsByEmail: vi.fn(),
list: vi.fn(),
count: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
toStored: vi.fn(),
fromStored: vi.fn(),
};
// Assert
expect(mockRepository.list).toBeDefined();
expect(typeof mockRepository.list).toBe('function');
});
it('should define count method signature', () => {
// Arrange
const mockRepository: AdminUserRepository = {
findById: vi.fn(),
findByEmail: vi.fn(),
emailExists: vi.fn(),
existsById: vi.fn(),
existsByEmail: vi.fn(),
list: vi.fn(),
count: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
toStored: vi.fn(),
fromStored: vi.fn(),
};
// Assert
expect(mockRepository.count).toBeDefined();
expect(typeof mockRepository.count).toBe('function');
});
it('should define create method signature', () => {
// Arrange
const mockRepository: AdminUserRepository = {
findById: vi.fn(),
findByEmail: vi.fn(),
emailExists: vi.fn(),
existsById: vi.fn(),
existsByEmail: vi.fn(),
list: vi.fn(),
count: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
toStored: vi.fn(),
fromStored: vi.fn(),
};
// Assert
expect(mockRepository.create).toBeDefined();
expect(typeof mockRepository.create).toBe('function');
});
it('should define update method signature', () => {
// Arrange
const mockRepository: AdminUserRepository = {
findById: vi.fn(),
findByEmail: vi.fn(),
emailExists: vi.fn(),
existsById: vi.fn(),
existsByEmail: vi.fn(),
list: vi.fn(),
count: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
toStored: vi.fn(),
fromStored: vi.fn(),
};
// Assert
expect(mockRepository.update).toBeDefined();
expect(typeof mockRepository.update).toBe('function');
});
it('should define delete method signature', () => {
// Arrange
const mockRepository: AdminUserRepository = {
findById: vi.fn(),
findByEmail: vi.fn(),
emailExists: vi.fn(),
existsById: vi.fn(),
existsByEmail: vi.fn(),
list: vi.fn(),
count: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
toStored: vi.fn(),
fromStored: vi.fn(),
};
// Assert
expect(mockRepository.delete).toBeDefined();
expect(typeof mockRepository.delete).toBe('function');
});
it('should define toStored method signature', () => {
// Arrange
const mockRepository: AdminUserRepository = {
findById: vi.fn(),
findByEmail: vi.fn(),
emailExists: vi.fn(),
existsById: vi.fn(),
existsByEmail: vi.fn(),
list: vi.fn(),
count: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
toStored: vi.fn(),
fromStored: vi.fn(),
};
// Assert
expect(mockRepository.toStored).toBeDefined();
expect(typeof mockRepository.toStored).toBe('function');
});
it('should define fromStored method signature', () => {
// Arrange
const mockRepository: AdminUserRepository = {
findById: vi.fn(),
findByEmail: vi.fn(),
emailExists: vi.fn(),
existsById: vi.fn(),
existsByEmail: vi.fn(),
list: vi.fn(),
count: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
toStored: vi.fn(),
fromStored: vi.fn(),
};
// Assert
expect(mockRepository.fromStored).toBeDefined();
expect(typeof mockRepository.fromStored).toBe('function');
});
it('should handle repository operations with mock implementation', async () => {
// Arrange
const user = AdminUser.create({
id: 'user-1',
email: 'test@example.com',
displayName: 'Test User',
roles: ['user'],
status: 'active',
});
const mockRepository: AdminUserRepository = {
findById: vi.fn().mockResolvedValue(user),
findByEmail: vi.fn().mockResolvedValue(user),
emailExists: vi.fn().mockResolvedValue(true),
existsById: vi.fn().mockResolvedValue(true),
existsByEmail: vi.fn().mockResolvedValue(true),
list: vi.fn().mockResolvedValue({
users: [user],
total: 1,
page: 1,
limit: 10,
totalPages: 1,
}),
count: vi.fn().mockResolvedValue(1),
create: vi.fn().mockResolvedValue(user),
update: vi.fn().mockResolvedValue(user),
delete: vi.fn().mockResolvedValue(undefined),
toStored: vi.fn().mockReturnValue({
id: 'user-1',
email: 'test@example.com',
roles: ['user'],
status: 'active',
displayName: 'Test User',
createdAt: new Date(),
updatedAt: new Date(),
}),
fromStored: vi.fn().mockReturnValue(user),
};
// Act
const foundUser = await mockRepository.findById(UserId.create('user-1'));
const emailExists = await mockRepository.emailExists(Email.create('test@example.com'));
const listResult = await mockRepository.list();
// Assert
expect(foundUser).toBe(user);
expect(emailExists).toBe(true);
expect(listResult.users).toHaveLength(1);
expect(mockRepository.findById).toHaveBeenCalledWith(UserId.create('user-1'));
expect(mockRepository.emailExists).toHaveBeenCalledWith(Email.create('test@example.com'));
});
});
});
});