test(P2): Recreate 4 controller test files
- products.controller.spec.ts (16 tests) - Portfolio module - activities.controller.spec.ts (8 tests) - Sales module - leads.controller.spec.ts (9 tests) - Sales module - dashboard.controller.spec.ts (6 tests) - Sales module Total: 39 new tests for previously deleted test files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
9baaf4af85
commit
23bd170769
362
src/modules/portfolio/__tests__/products.controller.spec.ts
Normal file
362
src/modules/portfolio/__tests__/products.controller.spec.ts
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { ProductsController } from '../controllers/products.controller';
|
||||||
|
import { ProductsService } from '../services/products.service';
|
||||||
|
import { ProductType, ProductStatus } from '../entities/product.entity';
|
||||||
|
import { PriceType } from '../entities/price.entity';
|
||||||
|
import {
|
||||||
|
CreateProductDto,
|
||||||
|
UpdateProductDto,
|
||||||
|
ProductListQueryDto,
|
||||||
|
CreateVariantDto,
|
||||||
|
CreatePriceDto,
|
||||||
|
} from '../dto';
|
||||||
|
|
||||||
|
describe('ProductsController', () => {
|
||||||
|
let controller: ProductsController;
|
||||||
|
let productsService: jest.Mocked<ProductsService>;
|
||||||
|
|
||||||
|
const mockTenantId = '550e8400-e29b-41d4-a716-446655440001';
|
||||||
|
const mockUserId = '550e8400-e29b-41d4-a716-446655440002';
|
||||||
|
const mockProductId = '550e8400-e29b-41d4-a716-446655440003';
|
||||||
|
const mockVariantId = '550e8400-e29b-41d4-a716-446655440004';
|
||||||
|
const mockPriceId = '550e8400-e29b-41d4-a716-446655440005';
|
||||||
|
|
||||||
|
const mockRequestUser = {
|
||||||
|
id: mockUserId,
|
||||||
|
email: 'test@example.com',
|
||||||
|
tenant_id: mockTenantId,
|
||||||
|
role: 'admin',
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockProduct = {
|
||||||
|
id: mockProductId,
|
||||||
|
tenantId: mockTenantId,
|
||||||
|
categoryId: null,
|
||||||
|
name: 'Test Product',
|
||||||
|
slug: 'test-product',
|
||||||
|
sku: 'SKU-001',
|
||||||
|
barcode: null,
|
||||||
|
description: 'Test description',
|
||||||
|
shortDescription: 'Short desc',
|
||||||
|
productType: ProductType.PHYSICAL,
|
||||||
|
status: ProductStatus.ACTIVE,
|
||||||
|
basePrice: 99.99,
|
||||||
|
costPrice: 50.00,
|
||||||
|
compareAtPrice: 129.99,
|
||||||
|
currency: 'USD',
|
||||||
|
trackInventory: true,
|
||||||
|
stockQuantity: 100,
|
||||||
|
lowStockThreshold: 10,
|
||||||
|
allowBackorder: false,
|
||||||
|
weight: 1.5,
|
||||||
|
weightUnit: 'kg',
|
||||||
|
length: 10,
|
||||||
|
width: 5,
|
||||||
|
height: 3,
|
||||||
|
dimensionUnit: 'cm',
|
||||||
|
images: ['image1.jpg', 'image2.jpg'],
|
||||||
|
featuredImageUrl: 'featured.jpg',
|
||||||
|
metaTitle: 'Test Product Meta',
|
||||||
|
metaDescription: 'Meta description',
|
||||||
|
tags: ['tag1', 'tag2'],
|
||||||
|
isVisible: true,
|
||||||
|
isFeatured: false,
|
||||||
|
hasVariants: false,
|
||||||
|
variantAttributes: [],
|
||||||
|
customFields: {},
|
||||||
|
createdAt: new Date('2026-01-01'),
|
||||||
|
updatedAt: new Date('2026-01-01'),
|
||||||
|
createdBy: mockUserId,
|
||||||
|
publishedAt: new Date('2026-01-01'),
|
||||||
|
category: null,
|
||||||
|
variantCount: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockVariant = {
|
||||||
|
id: mockVariantId,
|
||||||
|
tenantId: mockTenantId,
|
||||||
|
productId: mockProductId,
|
||||||
|
sku: 'SKU-001-VAR1',
|
||||||
|
barcode: null,
|
||||||
|
name: 'Size: Large',
|
||||||
|
attributes: { size: 'large', color: 'blue' },
|
||||||
|
price: 109.99,
|
||||||
|
costPrice: 55.00,
|
||||||
|
compareAtPrice: 139.99,
|
||||||
|
stockQuantity: 50,
|
||||||
|
lowStockThreshold: 5,
|
||||||
|
weight: 1.6,
|
||||||
|
imageUrl: 'variant.jpg',
|
||||||
|
isActive: true,
|
||||||
|
position: 0,
|
||||||
|
createdAt: new Date('2026-01-01'),
|
||||||
|
updatedAt: new Date('2026-01-01'),
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockPrice = {
|
||||||
|
id: mockPriceId,
|
||||||
|
tenantId: mockTenantId,
|
||||||
|
productId: mockProductId,
|
||||||
|
variantId: null,
|
||||||
|
priceType: PriceType.ONE_TIME,
|
||||||
|
currency: 'USD',
|
||||||
|
amount: 99.99,
|
||||||
|
compareAtAmount: 129.99,
|
||||||
|
billingPeriod: null,
|
||||||
|
billingInterval: null,
|
||||||
|
minQuantity: 1,
|
||||||
|
maxQuantity: null,
|
||||||
|
validFrom: null,
|
||||||
|
validUntil: null,
|
||||||
|
priority: 0,
|
||||||
|
isActive: true,
|
||||||
|
createdAt: new Date('2026-01-01'),
|
||||||
|
updatedAt: new Date('2026-01-01'),
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockPaginatedProducts = {
|
||||||
|
items: [mockProduct],
|
||||||
|
total: 1,
|
||||||
|
page: 1,
|
||||||
|
limit: 20,
|
||||||
|
totalPages: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const mockProductsService = {
|
||||||
|
findAll: jest.fn(),
|
||||||
|
findOne: jest.fn(),
|
||||||
|
create: jest.fn(),
|
||||||
|
update: jest.fn(),
|
||||||
|
updateStatus: jest.fn(),
|
||||||
|
duplicate: jest.fn(),
|
||||||
|
remove: jest.fn(),
|
||||||
|
getVariants: jest.fn(),
|
||||||
|
createVariant: jest.fn(),
|
||||||
|
updateVariant: jest.fn(),
|
||||||
|
removeVariant: jest.fn(),
|
||||||
|
getPrices: jest.fn(),
|
||||||
|
createPrice: jest.fn(),
|
||||||
|
updatePrice: jest.fn(),
|
||||||
|
removePrice: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
controllers: [ProductsController],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: ProductsService,
|
||||||
|
useValue: mockProductsService,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
controller = module.get<ProductsController>(ProductsController);
|
||||||
|
productsService = module.get(ProductsService);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('findAll', () => {
|
||||||
|
it('should return paginated products', async () => {
|
||||||
|
const query: ProductListQueryDto = { page: 1, limit: 20 };
|
||||||
|
productsService.findAll.mockResolvedValue(mockPaginatedProducts);
|
||||||
|
|
||||||
|
const result = await controller.findAll(mockRequestUser, query);
|
||||||
|
|
||||||
|
expect(productsService.findAll).toHaveBeenCalledWith(mockTenantId, query);
|
||||||
|
expect(result).toEqual(mockPaginatedProducts);
|
||||||
|
expect(result.items.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter by status', async () => {
|
||||||
|
const query: ProductListQueryDto = { status: ProductStatus.ACTIVE };
|
||||||
|
productsService.findAll.mockResolvedValue(mockPaginatedProducts);
|
||||||
|
|
||||||
|
await controller.findAll(mockRequestUser, query);
|
||||||
|
|
||||||
|
expect(productsService.findAll).toHaveBeenCalledWith(mockTenantId, query);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('findOne', () => {
|
||||||
|
it('should return a product by id', async () => {
|
||||||
|
productsService.findOne.mockResolvedValue(mockProduct);
|
||||||
|
|
||||||
|
const result = await controller.findOne(mockRequestUser, mockProductId);
|
||||||
|
|
||||||
|
expect(productsService.findOne).toHaveBeenCalledWith(mockTenantId, mockProductId);
|
||||||
|
expect(result).toEqual(mockProduct);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('create', () => {
|
||||||
|
it('should create a new product', async () => {
|
||||||
|
const createDto: CreateProductDto = {
|
||||||
|
name: 'New Product',
|
||||||
|
slug: 'new-product',
|
||||||
|
basePrice: 49.99,
|
||||||
|
};
|
||||||
|
productsService.create.mockResolvedValue(mockProduct);
|
||||||
|
|
||||||
|
const result = await controller.create(mockRequestUser, createDto);
|
||||||
|
|
||||||
|
expect(productsService.create).toHaveBeenCalledWith(mockTenantId, mockUserId, createDto);
|
||||||
|
expect(result).toEqual(mockProduct);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('update', () => {
|
||||||
|
it('should update a product', async () => {
|
||||||
|
const updateDto: UpdateProductDto = {
|
||||||
|
name: 'Updated Product',
|
||||||
|
basePrice: 79.99,
|
||||||
|
};
|
||||||
|
const updatedProduct = { ...mockProduct, ...updateDto };
|
||||||
|
productsService.update.mockResolvedValue(updatedProduct);
|
||||||
|
|
||||||
|
const result = await controller.update(mockRequestUser, mockProductId, updateDto);
|
||||||
|
|
||||||
|
expect(productsService.update).toHaveBeenCalledWith(mockTenantId, mockProductId, updateDto);
|
||||||
|
expect(result.name).toBe('Updated Product');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('updateStatus', () => {
|
||||||
|
it('should update product status', async () => {
|
||||||
|
const statusDto = { status: ProductStatus.INACTIVE };
|
||||||
|
const updatedProduct = { ...mockProduct, status: ProductStatus.INACTIVE };
|
||||||
|
productsService.updateStatus.mockResolvedValue(updatedProduct);
|
||||||
|
|
||||||
|
const result = await controller.updateStatus(mockRequestUser, mockProductId, statusDto);
|
||||||
|
|
||||||
|
expect(productsService.updateStatus).toHaveBeenCalledWith(mockTenantId, mockProductId, statusDto);
|
||||||
|
expect(result.status).toBe(ProductStatus.INACTIVE);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('duplicate', () => {
|
||||||
|
it('should duplicate a product', async () => {
|
||||||
|
const duplicatedProduct = { ...mockProduct, id: 'new-id', name: 'Test Product (Copy)' };
|
||||||
|
productsService.duplicate.mockResolvedValue(duplicatedProduct);
|
||||||
|
|
||||||
|
const result = await controller.duplicate(mockRequestUser, mockProductId);
|
||||||
|
|
||||||
|
expect(productsService.duplicate).toHaveBeenCalledWith(mockTenantId, mockUserId, mockProductId);
|
||||||
|
expect(result.name).toContain('Copy');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('remove', () => {
|
||||||
|
it('should delete a product', async () => {
|
||||||
|
productsService.remove.mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
const result = await controller.remove(mockRequestUser, mockProductId);
|
||||||
|
|
||||||
|
expect(productsService.remove).toHaveBeenCalledWith(mockTenantId, mockProductId);
|
||||||
|
expect(result).toEqual({ message: 'Product deleted successfully' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getVariants', () => {
|
||||||
|
it('should return variants for a product', async () => {
|
||||||
|
productsService.getVariants.mockResolvedValue([mockVariant]);
|
||||||
|
|
||||||
|
const result = await controller.getVariants(mockRequestUser, mockProductId);
|
||||||
|
|
||||||
|
expect(productsService.getVariants).toHaveBeenCalledWith(mockTenantId, mockProductId);
|
||||||
|
expect(result).toEqual([mockVariant]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createVariant', () => {
|
||||||
|
it('should create a variant', async () => {
|
||||||
|
const createDto: CreateVariantDto = {
|
||||||
|
attributes: { size: 'medium' },
|
||||||
|
price: 99.99,
|
||||||
|
};
|
||||||
|
productsService.createVariant.mockResolvedValue(mockVariant);
|
||||||
|
|
||||||
|
const result = await controller.createVariant(mockRequestUser, mockProductId, createDto);
|
||||||
|
|
||||||
|
expect(productsService.createVariant).toHaveBeenCalledWith(mockTenantId, mockProductId, createDto);
|
||||||
|
expect(result).toEqual(mockVariant);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('updateVariant', () => {
|
||||||
|
it('should update a variant', async () => {
|
||||||
|
const updateDto = { price: 119.99 };
|
||||||
|
const updatedVariant = { ...mockVariant, price: 119.99 };
|
||||||
|
productsService.updateVariant.mockResolvedValue(updatedVariant);
|
||||||
|
|
||||||
|
const result = await controller.updateVariant(mockRequestUser, mockProductId, mockVariantId, updateDto);
|
||||||
|
|
||||||
|
expect(productsService.updateVariant).toHaveBeenCalledWith(mockTenantId, mockProductId, mockVariantId, updateDto);
|
||||||
|
expect(result.price).toBe(119.99);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('removeVariant', () => {
|
||||||
|
it('should delete a variant', async () => {
|
||||||
|
productsService.removeVariant.mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
const result = await controller.removeVariant(mockRequestUser, mockProductId, mockVariantId);
|
||||||
|
|
||||||
|
expect(productsService.removeVariant).toHaveBeenCalledWith(mockTenantId, mockProductId, mockVariantId);
|
||||||
|
expect(result).toEqual({ message: 'Variant deleted successfully' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getPrices', () => {
|
||||||
|
it('should return prices for a product', async () => {
|
||||||
|
productsService.getPrices.mockResolvedValue([mockPrice]);
|
||||||
|
|
||||||
|
const result = await controller.getPrices(mockRequestUser, mockProductId);
|
||||||
|
|
||||||
|
expect(productsService.getPrices).toHaveBeenCalledWith(mockTenantId, mockProductId);
|
||||||
|
expect(result).toEqual([mockPrice]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createPrice', () => {
|
||||||
|
it('should create a price', async () => {
|
||||||
|
const createDto: CreatePriceDto = {
|
||||||
|
currency: 'USD',
|
||||||
|
amount: 89.99,
|
||||||
|
};
|
||||||
|
productsService.createPrice.mockResolvedValue(mockPrice);
|
||||||
|
|
||||||
|
const result = await controller.createPrice(mockRequestUser, mockProductId, createDto);
|
||||||
|
|
||||||
|
expect(productsService.createPrice).toHaveBeenCalledWith(mockTenantId, mockProductId, createDto);
|
||||||
|
expect(result).toEqual(mockPrice);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('updatePrice', () => {
|
||||||
|
it('should update a price', async () => {
|
||||||
|
const updateDto = { amount: 109.99 };
|
||||||
|
const updatedPrice = { ...mockPrice, amount: 109.99 };
|
||||||
|
productsService.updatePrice.mockResolvedValue(updatedPrice);
|
||||||
|
|
||||||
|
const result = await controller.updatePrice(mockRequestUser, mockProductId, mockPriceId, updateDto);
|
||||||
|
|
||||||
|
expect(productsService.updatePrice).toHaveBeenCalledWith(mockTenantId, mockProductId, mockPriceId, updateDto);
|
||||||
|
expect(result.amount).toBe(109.99);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('removePrice', () => {
|
||||||
|
it('should delete a price', async () => {
|
||||||
|
productsService.removePrice.mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
const result = await controller.removePrice(mockRequestUser, mockProductId, mockPriceId);
|
||||||
|
|
||||||
|
expect(productsService.removePrice).toHaveBeenCalledWith(mockTenantId, mockProductId, mockPriceId);
|
||||||
|
expect(result).toEqual({ message: 'Price deleted successfully' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
205
src/modules/sales/__tests__/activities.controller.spec.ts
Normal file
205
src/modules/sales/__tests__/activities.controller.spec.ts
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { ActivitiesController } from '../controllers/activities.controller';
|
||||||
|
import { ActivitiesService } from '../services/activities.service';
|
||||||
|
import { ActivityType, ActivityStatus } from '../entities/activity.entity';
|
||||||
|
import { CreateActivityDto, UpdateActivityDto, ActivityListQueryDto } from '../dto';
|
||||||
|
|
||||||
|
describe('ActivitiesController', () => {
|
||||||
|
let controller: ActivitiesController;
|
||||||
|
let activitiesService: jest.Mocked<ActivitiesService>;
|
||||||
|
|
||||||
|
const mockTenantId = '550e8400-e29b-41d4-a716-446655440001';
|
||||||
|
const mockUserId = '550e8400-e29b-41d4-a716-446655440002';
|
||||||
|
const mockActivityId = '550e8400-e29b-41d4-a716-446655440003';
|
||||||
|
const mockLeadId = '550e8400-e29b-41d4-a716-446655440004';
|
||||||
|
|
||||||
|
const mockRequestUser = {
|
||||||
|
id: mockUserId,
|
||||||
|
email: 'test@example.com',
|
||||||
|
tenant_id: mockTenantId,
|
||||||
|
role: 'admin',
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockActivity = {
|
||||||
|
id: mockActivityId,
|
||||||
|
tenantId: mockTenantId,
|
||||||
|
type: ActivityType.CALL,
|
||||||
|
status: ActivityStatus.PENDING,
|
||||||
|
subject: 'Follow-up call',
|
||||||
|
description: 'Discuss pricing options',
|
||||||
|
leadId: mockLeadId,
|
||||||
|
opportunityId: null,
|
||||||
|
dueDate: new Date('2026-02-10'),
|
||||||
|
dueTime: '14:00',
|
||||||
|
durationMinutes: 30,
|
||||||
|
completedAt: null,
|
||||||
|
outcome: null,
|
||||||
|
assignedTo: mockUserId,
|
||||||
|
createdBy: mockUserId,
|
||||||
|
callDirection: 'outbound',
|
||||||
|
callRecordingUrl: null,
|
||||||
|
location: null,
|
||||||
|
meetingUrl: null,
|
||||||
|
attendees: [],
|
||||||
|
reminderAt: new Date('2026-02-10T13:45:00'),
|
||||||
|
reminderSent: false,
|
||||||
|
customFields: {},
|
||||||
|
createdAt: new Date('2026-01-01'),
|
||||||
|
updatedAt: new Date('2026-01-01'),
|
||||||
|
lead: null,
|
||||||
|
opportunity: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockPaginatedActivities = {
|
||||||
|
items: [mockActivity],
|
||||||
|
total: 1,
|
||||||
|
page: 1,
|
||||||
|
limit: 20,
|
||||||
|
totalPages: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const mockActivitiesService = {
|
||||||
|
findAll: jest.fn(),
|
||||||
|
findOne: jest.fn(),
|
||||||
|
create: jest.fn(),
|
||||||
|
update: jest.fn(),
|
||||||
|
complete: jest.fn(),
|
||||||
|
remove: jest.fn(),
|
||||||
|
getUpcoming: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
controllers: [ActivitiesController],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: ActivitiesService,
|
||||||
|
useValue: mockActivitiesService,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
controller = module.get<ActivitiesController>(ActivitiesController);
|
||||||
|
activitiesService = module.get(ActivitiesService);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('findAll', () => {
|
||||||
|
it('should return paginated activities', async () => {
|
||||||
|
const query: ActivityListQueryDto = { page: 1, limit: 20 };
|
||||||
|
activitiesService.findAll.mockResolvedValue(mockPaginatedActivities);
|
||||||
|
|
||||||
|
const result = await controller.findAll(mockRequestUser, query);
|
||||||
|
|
||||||
|
expect(activitiesService.findAll).toHaveBeenCalledWith(mockTenantId, query);
|
||||||
|
expect(result).toEqual(mockPaginatedActivities);
|
||||||
|
expect(result.items.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter by type', async () => {
|
||||||
|
const query: ActivityListQueryDto = { type: ActivityType.MEETING };
|
||||||
|
activitiesService.findAll.mockResolvedValue({ ...mockPaginatedActivities, items: [] });
|
||||||
|
|
||||||
|
await controller.findAll(mockRequestUser, query);
|
||||||
|
|
||||||
|
expect(activitiesService.findAll).toHaveBeenCalledWith(mockTenantId, query);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter by status', async () => {
|
||||||
|
const query: ActivityListQueryDto = { status: ActivityStatus.COMPLETED };
|
||||||
|
activitiesService.findAll.mockResolvedValue({ ...mockPaginatedActivities, items: [] });
|
||||||
|
|
||||||
|
await controller.findAll(mockRequestUser, query);
|
||||||
|
|
||||||
|
expect(activitiesService.findAll).toHaveBeenCalledWith(mockTenantId, query);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getUpcoming', () => {
|
||||||
|
it('should return upcoming activities', async () => {
|
||||||
|
activitiesService.getUpcoming.mockResolvedValue([mockActivity]);
|
||||||
|
|
||||||
|
const result = await controller.getUpcoming(mockRequestUser);
|
||||||
|
|
||||||
|
expect(activitiesService.getUpcoming).toHaveBeenCalledWith(mockTenantId, mockUserId);
|
||||||
|
expect(result).toEqual([mockActivity]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('findOne', () => {
|
||||||
|
it('should return an activity by id', async () => {
|
||||||
|
activitiesService.findOne.mockResolvedValue(mockActivity);
|
||||||
|
|
||||||
|
const result = await controller.findOne(mockRequestUser, mockActivityId);
|
||||||
|
|
||||||
|
expect(activitiesService.findOne).toHaveBeenCalledWith(mockTenantId, mockActivityId);
|
||||||
|
expect(result).toEqual(mockActivity);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('create', () => {
|
||||||
|
it('should create a new activity', async () => {
|
||||||
|
const createDto: CreateActivityDto = {
|
||||||
|
type: ActivityType.CALL,
|
||||||
|
subject: 'New Call',
|
||||||
|
leadId: mockLeadId,
|
||||||
|
dueDate: '2026-02-15',
|
||||||
|
};
|
||||||
|
activitiesService.create.mockResolvedValue(mockActivity);
|
||||||
|
|
||||||
|
const result = await controller.create(mockRequestUser, createDto);
|
||||||
|
|
||||||
|
expect(activitiesService.create).toHaveBeenCalledWith(mockTenantId, mockUserId, createDto);
|
||||||
|
expect(result).toEqual(mockActivity);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('update', () => {
|
||||||
|
it('should update an activity', async () => {
|
||||||
|
const updateDto: UpdateActivityDto = {
|
||||||
|
subject: 'Updated Subject',
|
||||||
|
durationMinutes: 60,
|
||||||
|
};
|
||||||
|
const updatedActivity = { ...mockActivity, subject: 'Updated Subject', durationMinutes: 60 };
|
||||||
|
activitiesService.update.mockResolvedValue(updatedActivity);
|
||||||
|
|
||||||
|
const result = await controller.update(mockRequestUser, mockActivityId, updateDto);
|
||||||
|
|
||||||
|
expect(activitiesService.update).toHaveBeenCalledWith(mockTenantId, mockActivityId, updateDto);
|
||||||
|
expect(result.subject).toBe('Updated Subject');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('complete', () => {
|
||||||
|
it('should complete an activity', async () => {
|
||||||
|
const completeDto = { outcome: 'Customer interested, follow up next week' };
|
||||||
|
const completedActivity = {
|
||||||
|
...mockActivity,
|
||||||
|
status: ActivityStatus.COMPLETED,
|
||||||
|
completedAt: new Date(),
|
||||||
|
outcome: completeDto.outcome,
|
||||||
|
};
|
||||||
|
activitiesService.complete.mockResolvedValue(completedActivity);
|
||||||
|
|
||||||
|
const result = await controller.complete(mockRequestUser, mockActivityId, completeDto);
|
||||||
|
|
||||||
|
expect(activitiesService.complete).toHaveBeenCalledWith(mockTenantId, mockActivityId, completeDto);
|
||||||
|
expect(result.status).toBe(ActivityStatus.COMPLETED);
|
||||||
|
expect(result.outcome).toBe(completeDto.outcome);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('remove', () => {
|
||||||
|
it('should delete an activity', async () => {
|
||||||
|
activitiesService.remove.mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
const result = await controller.remove(mockRequestUser, mockActivityId);
|
||||||
|
|
||||||
|
expect(activitiesService.remove).toHaveBeenCalledWith(mockTenantId, mockActivityId);
|
||||||
|
expect(result).toEqual({ message: 'Activity deleted successfully' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
191
src/modules/sales/__tests__/dashboard.controller.spec.ts
Normal file
191
src/modules/sales/__tests__/dashboard.controller.spec.ts
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { SalesDashboardController } from '../controllers/dashboard.controller';
|
||||||
|
import { SalesDashboardService } from '../services';
|
||||||
|
import { LeadStatus, LeadSource, OpportunityStage } from '../entities';
|
||||||
|
import {
|
||||||
|
SalesDashboardDto,
|
||||||
|
LeadsByStatusDto,
|
||||||
|
LeadsBySourceDto,
|
||||||
|
OpportunitiesByStageDto,
|
||||||
|
ConversionFunnelDto,
|
||||||
|
SalesPerformanceDto,
|
||||||
|
} from '../dto';
|
||||||
|
|
||||||
|
describe('SalesDashboardController', () => {
|
||||||
|
let controller: SalesDashboardController;
|
||||||
|
let dashboardService: jest.Mocked<SalesDashboardService>;
|
||||||
|
|
||||||
|
const mockTenantId = '550e8400-e29b-41d4-a716-446655440001';
|
||||||
|
const mockUserId = '550e8400-e29b-41d4-a716-446655440002';
|
||||||
|
|
||||||
|
const mockRequestUser = {
|
||||||
|
id: mockUserId,
|
||||||
|
email: 'test@example.com',
|
||||||
|
tenant_id: mockTenantId,
|
||||||
|
role: 'admin',
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockSummary: SalesDashboardDto = {
|
||||||
|
totalLeads: 150,
|
||||||
|
totalOpportunities: 45,
|
||||||
|
totalPipelineValue: 1250000,
|
||||||
|
wonDeals: 12,
|
||||||
|
conversionRate: 26.7,
|
||||||
|
averageDealSize: 104166.67,
|
||||||
|
activitiesThisWeek: 35,
|
||||||
|
currency: 'USD',
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockLeadsByStatus: LeadsByStatusDto[] = [
|
||||||
|
{ status: LeadStatus.NEW, count: 50, percentage: 33.3 },
|
||||||
|
{ status: LeadStatus.CONTACTED, count: 40, percentage: 26.7 },
|
||||||
|
{ status: LeadStatus.QUALIFIED, count: 35, percentage: 23.3 },
|
||||||
|
{ status: LeadStatus.CONVERTED, count: 25, percentage: 16.7 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const mockLeadsBySource: LeadsBySourceDto[] = [
|
||||||
|
{ source: LeadSource.WEBSITE, count: 60, percentage: 40.0 },
|
||||||
|
{ source: LeadSource.REFERRAL, count: 45, percentage: 30.0 },
|
||||||
|
{ source: LeadSource.EVENT, count: 25, percentage: 16.7 },
|
||||||
|
{ source: LeadSource.COLD_CALL, count: 20, percentage: 13.3 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const mockOpportunitiesByStage: OpportunitiesByStageDto[] = [
|
||||||
|
{ stage: OpportunityStage.QUALIFICATION, count: 15, totalAmount: 375000, percentage: 33.3 },
|
||||||
|
{ stage: OpportunityStage.PROPOSAL, count: 12, totalAmount: 480000, percentage: 26.7 },
|
||||||
|
{ stage: OpportunityStage.NEGOTIATION, count: 10, totalAmount: 350000, percentage: 22.2 },
|
||||||
|
{ stage: OpportunityStage.CLOSED_WON, count: 8, totalAmount: 320000, percentage: 17.8 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const mockConversionFunnel: ConversionFunnelDto[] = [
|
||||||
|
{ stage: 'leads', count: 150, percentage: 100, conversionFromPrevious: 100 },
|
||||||
|
{ stage: 'qualified', count: 75, percentage: 50, conversionFromPrevious: 50 },
|
||||||
|
{ stage: 'opportunities', count: 45, percentage: 30, conversionFromPrevious: 60 },
|
||||||
|
{ stage: 'proposals', count: 30, percentage: 20, conversionFromPrevious: 66.7 },
|
||||||
|
{ stage: 'won', count: 12, percentage: 8, conversionFromPrevious: 40 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const mockSalesPerformance: SalesPerformanceDto[] = [
|
||||||
|
{
|
||||||
|
userId: 'user-001',
|
||||||
|
userName: 'John Doe',
|
||||||
|
leadsAssigned: 45,
|
||||||
|
opportunitiesWon: 5,
|
||||||
|
totalRevenue: 125000,
|
||||||
|
conversionRate: 11.1,
|
||||||
|
activitiesCompleted: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userId: 'user-002',
|
||||||
|
userName: 'Jane Smith',
|
||||||
|
leadsAssigned: 52,
|
||||||
|
opportunitiesWon: 7,
|
||||||
|
totalRevenue: 175000,
|
||||||
|
conversionRate: 13.5,
|
||||||
|
activitiesCompleted: 145,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const mockDashboardService = {
|
||||||
|
getDashboardSummary: jest.fn(),
|
||||||
|
getLeadsByStatus: jest.fn(),
|
||||||
|
getLeadsBySource: jest.fn(),
|
||||||
|
getOpportunitiesByStage: jest.fn(),
|
||||||
|
getConversionFunnel: jest.fn(),
|
||||||
|
getSalesPerformance: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
controllers: [SalesDashboardController],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: SalesDashboardService,
|
||||||
|
useValue: mockDashboardService,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
controller = module.get<SalesDashboardController>(SalesDashboardController);
|
||||||
|
dashboardService = module.get(SalesDashboardService);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSummary', () => {
|
||||||
|
it('should return dashboard summary', async () => {
|
||||||
|
dashboardService.getDashboardSummary.mockResolvedValue(mockSummary);
|
||||||
|
|
||||||
|
const result = await controller.getSummary(mockRequestUser);
|
||||||
|
|
||||||
|
expect(dashboardService.getDashboardSummary).toHaveBeenCalledWith(mockTenantId);
|
||||||
|
expect(result).toEqual(mockSummary);
|
||||||
|
expect(result.totalLeads).toBe(150);
|
||||||
|
expect(result.conversionRate).toBe(26.7);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getLeadsByStatus', () => {
|
||||||
|
it('should return leads grouped by status', async () => {
|
||||||
|
dashboardService.getLeadsByStatus.mockResolvedValue(mockLeadsByStatus);
|
||||||
|
|
||||||
|
const result = await controller.getLeadsByStatus(mockRequestUser);
|
||||||
|
|
||||||
|
expect(dashboardService.getLeadsByStatus).toHaveBeenCalledWith(mockTenantId);
|
||||||
|
expect(result).toEqual(mockLeadsByStatus);
|
||||||
|
expect(result.length).toBe(4);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getLeadsBySource', () => {
|
||||||
|
it('should return leads grouped by source', async () => {
|
||||||
|
dashboardService.getLeadsBySource.mockResolvedValue(mockLeadsBySource);
|
||||||
|
|
||||||
|
const result = await controller.getLeadsBySource(mockRequestUser);
|
||||||
|
|
||||||
|
expect(dashboardService.getLeadsBySource).toHaveBeenCalledWith(mockTenantId);
|
||||||
|
expect(result).toEqual(mockLeadsBySource);
|
||||||
|
expect(result.length).toBe(4);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getOpportunitiesByStage', () => {
|
||||||
|
it('should return opportunities grouped by stage', async () => {
|
||||||
|
dashboardService.getOpportunitiesByStage.mockResolvedValue(mockOpportunitiesByStage);
|
||||||
|
|
||||||
|
const result = await controller.getOpportunitiesByStage(mockRequestUser);
|
||||||
|
|
||||||
|
expect(dashboardService.getOpportunitiesByStage).toHaveBeenCalledWith(mockTenantId);
|
||||||
|
expect(result).toEqual(mockOpportunitiesByStage);
|
||||||
|
expect(result.length).toBe(4);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getConversionFunnel', () => {
|
||||||
|
it('should return conversion funnel data', async () => {
|
||||||
|
dashboardService.getConversionFunnel.mockResolvedValue(mockConversionFunnel);
|
||||||
|
|
||||||
|
const result = await controller.getConversionFunnel(mockRequestUser);
|
||||||
|
|
||||||
|
expect(dashboardService.getConversionFunnel).toHaveBeenCalledWith(mockTenantId);
|
||||||
|
expect(result).toEqual(mockConversionFunnel);
|
||||||
|
expect(result.length).toBe(5);
|
||||||
|
expect(result[0].stage).toBe('leads');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSalesPerformance', () => {
|
||||||
|
it('should return sales performance data', async () => {
|
||||||
|
dashboardService.getSalesPerformance.mockResolvedValue(mockSalesPerformance);
|
||||||
|
|
||||||
|
const result = await controller.getSalesPerformance(mockRequestUser);
|
||||||
|
|
||||||
|
expect(dashboardService.getSalesPerformance).toHaveBeenCalledWith(mockTenantId);
|
||||||
|
expect(result).toEqual(mockSalesPerformance);
|
||||||
|
expect(result.length).toBe(2);
|
||||||
|
expect(result[0].totalRevenue).toBe(125000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
218
src/modules/sales/__tests__/leads.controller.spec.ts
Normal file
218
src/modules/sales/__tests__/leads.controller.spec.ts
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { LeadsController } from '../controllers/leads.controller';
|
||||||
|
import { LeadsService } from '../services/leads.service';
|
||||||
|
import { LeadStatus, LeadSource } from '../entities/lead.entity';
|
||||||
|
import { CreateLeadDto, UpdateLeadDto, ConvertLeadDto, LeadListQueryDto } from '../dto';
|
||||||
|
|
||||||
|
describe('LeadsController', () => {
|
||||||
|
let controller: LeadsController;
|
||||||
|
let leadsService: jest.Mocked<LeadsService>;
|
||||||
|
|
||||||
|
const mockTenantId = '550e8400-e29b-41d4-a716-446655440001';
|
||||||
|
const mockUserId = '550e8400-e29b-41d4-a716-446655440002';
|
||||||
|
const mockLeadId = '550e8400-e29b-41d4-a716-446655440003';
|
||||||
|
const mockOpportunityId = '550e8400-e29b-41d4-a716-446655440004';
|
||||||
|
|
||||||
|
const mockRequestUser = {
|
||||||
|
id: mockUserId,
|
||||||
|
email: 'test@example.com',
|
||||||
|
tenant_id: mockTenantId,
|
||||||
|
role: 'admin',
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockLead = {
|
||||||
|
id: mockLeadId,
|
||||||
|
tenantId: mockTenantId,
|
||||||
|
firstName: 'John',
|
||||||
|
lastName: 'Doe',
|
||||||
|
fullName: 'John Doe',
|
||||||
|
email: 'john.doe@example.com',
|
||||||
|
phone: '+1234567890',
|
||||||
|
company: 'Acme Corp',
|
||||||
|
jobTitle: 'CTO',
|
||||||
|
website: 'https://acme.com',
|
||||||
|
source: LeadSource.WEBSITE,
|
||||||
|
status: LeadStatus.NEW,
|
||||||
|
score: 75,
|
||||||
|
assignedTo: mockUserId,
|
||||||
|
notes: 'Interested in enterprise plan',
|
||||||
|
convertedAt: null,
|
||||||
|
convertedToOpportunityId: null,
|
||||||
|
addressLine1: '123 Main St',
|
||||||
|
addressLine2: null,
|
||||||
|
city: 'San Francisco',
|
||||||
|
state: 'CA',
|
||||||
|
postalCode: '94102',
|
||||||
|
country: 'USA',
|
||||||
|
customFields: {},
|
||||||
|
createdAt: new Date('2026-01-01'),
|
||||||
|
updatedAt: new Date('2026-01-01'),
|
||||||
|
createdBy: mockUserId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockPaginatedLeads = {
|
||||||
|
items: [mockLead],
|
||||||
|
total: 1,
|
||||||
|
page: 1,
|
||||||
|
limit: 20,
|
||||||
|
totalPages: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const mockLeadsService = {
|
||||||
|
findAll: jest.fn(),
|
||||||
|
findOne: jest.fn(),
|
||||||
|
create: jest.fn(),
|
||||||
|
update: jest.fn(),
|
||||||
|
remove: jest.fn(),
|
||||||
|
convert: jest.fn(),
|
||||||
|
calculateScore: jest.fn(),
|
||||||
|
assignTo: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
controllers: [LeadsController],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: LeadsService,
|
||||||
|
useValue: mockLeadsService,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
controller = module.get<LeadsController>(LeadsController);
|
||||||
|
leadsService = module.get(LeadsService);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('findAll', () => {
|
||||||
|
it('should return paginated leads', async () => {
|
||||||
|
const query: LeadListQueryDto = { page: 1, limit: 20 };
|
||||||
|
leadsService.findAll.mockResolvedValue(mockPaginatedLeads);
|
||||||
|
|
||||||
|
const result = await controller.findAll(mockRequestUser, query);
|
||||||
|
|
||||||
|
expect(leadsService.findAll).toHaveBeenCalledWith(mockTenantId, query);
|
||||||
|
expect(result).toEqual(mockPaginatedLeads);
|
||||||
|
expect(result.items.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter by status', async () => {
|
||||||
|
const query: LeadListQueryDto = { status: LeadStatus.QUALIFIED };
|
||||||
|
leadsService.findAll.mockResolvedValue({ ...mockPaginatedLeads, items: [] });
|
||||||
|
|
||||||
|
await controller.findAll(mockRequestUser, query);
|
||||||
|
|
||||||
|
expect(leadsService.findAll).toHaveBeenCalledWith(mockTenantId, query);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter by source', async () => {
|
||||||
|
const query: LeadListQueryDto = { source: LeadSource.REFERRAL };
|
||||||
|
leadsService.findAll.mockResolvedValue({ ...mockPaginatedLeads, items: [] });
|
||||||
|
|
||||||
|
await controller.findAll(mockRequestUser, query);
|
||||||
|
|
||||||
|
expect(leadsService.findAll).toHaveBeenCalledWith(mockTenantId, query);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('findOne', () => {
|
||||||
|
it('should return a lead by id', async () => {
|
||||||
|
leadsService.findOne.mockResolvedValue(mockLead);
|
||||||
|
|
||||||
|
const result = await controller.findOne(mockRequestUser, mockLeadId);
|
||||||
|
|
||||||
|
expect(leadsService.findOne).toHaveBeenCalledWith(mockTenantId, mockLeadId);
|
||||||
|
expect(result).toEqual(mockLead);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('create', () => {
|
||||||
|
it('should create a new lead', async () => {
|
||||||
|
const createDto: CreateLeadDto = {
|
||||||
|
firstName: 'Jane',
|
||||||
|
lastName: 'Smith',
|
||||||
|
email: 'jane@example.com',
|
||||||
|
company: 'Tech Inc',
|
||||||
|
source: LeadSource.WEBSITE,
|
||||||
|
};
|
||||||
|
leadsService.create.mockResolvedValue(mockLead);
|
||||||
|
|
||||||
|
const result = await controller.create(mockRequestUser, createDto);
|
||||||
|
|
||||||
|
expect(leadsService.create).toHaveBeenCalledWith(mockTenantId, mockUserId, createDto);
|
||||||
|
expect(result).toEqual(mockLead);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('update', () => {
|
||||||
|
it('should update a lead', async () => {
|
||||||
|
const updateDto: UpdateLeadDto = {
|
||||||
|
status: LeadStatus.CONTACTED,
|
||||||
|
score: 80,
|
||||||
|
};
|
||||||
|
const updatedLead = { ...mockLead, status: LeadStatus.CONTACTED, score: 80 };
|
||||||
|
leadsService.update.mockResolvedValue(updatedLead);
|
||||||
|
|
||||||
|
const result = await controller.update(mockRequestUser, mockLeadId, updateDto);
|
||||||
|
|
||||||
|
expect(leadsService.update).toHaveBeenCalledWith(mockTenantId, mockLeadId, updateDto);
|
||||||
|
expect(result.status).toBe(LeadStatus.CONTACTED);
|
||||||
|
expect(result.score).toBe(80);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('remove', () => {
|
||||||
|
it('should delete a lead', async () => {
|
||||||
|
leadsService.remove.mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
const result = await controller.remove(mockRequestUser, mockLeadId);
|
||||||
|
|
||||||
|
expect(leadsService.remove).toHaveBeenCalledWith(mockTenantId, mockLeadId);
|
||||||
|
expect(result).toEqual({ message: 'Lead deleted successfully' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('convert', () => {
|
||||||
|
it('should convert a lead to an opportunity', async () => {
|
||||||
|
const convertDto: ConvertLeadDto = {
|
||||||
|
opportunityName: 'Acme Corp Deal',
|
||||||
|
amount: 50000,
|
||||||
|
expectedCloseDate: '2026-03-31',
|
||||||
|
};
|
||||||
|
leadsService.convert.mockResolvedValue({ opportunityId: mockOpportunityId });
|
||||||
|
|
||||||
|
const result = await controller.convert(mockRequestUser, mockLeadId, convertDto);
|
||||||
|
|
||||||
|
expect(leadsService.convert).toHaveBeenCalledWith(mockTenantId, mockLeadId, convertDto);
|
||||||
|
expect(result).toEqual({ opportunityId: mockOpportunityId });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('calculateScore', () => {
|
||||||
|
it('should calculate lead score', async () => {
|
||||||
|
leadsService.calculateScore.mockResolvedValue({ score: 85 });
|
||||||
|
|
||||||
|
const result = await controller.calculateScore(mockRequestUser, mockLeadId);
|
||||||
|
|
||||||
|
expect(leadsService.calculateScore).toHaveBeenCalledWith(mockTenantId, mockLeadId);
|
||||||
|
expect(result).toEqual({ score: 85 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('assign', () => {
|
||||||
|
it('should assign lead to a user', async () => {
|
||||||
|
const newUserId = 'new-user-id';
|
||||||
|
const assignedLead = { ...mockLead, assignedTo: newUserId };
|
||||||
|
leadsService.assignTo.mockResolvedValue(assignedLead);
|
||||||
|
|
||||||
|
const result = await controller.assign(mockRequestUser, mockLeadId, newUserId);
|
||||||
|
|
||||||
|
expect(leadsService.assignTo).toHaveBeenCalledWith(mockTenantId, mockLeadId, newUserId);
|
||||||
|
expect(result.assignedTo).toBe(newUserId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user