feat(admin): add Admin module with moderation, user mgmt, and dashboard
- Commands: ApproveListing, RejectListing, BanUser, AdjustSubscription
- Queries: GetModerationQueue, GetDashboardStats, GetRevenueStats
- Admin-only guards via @Roles('ADMIN') on all endpoints
- Prisma-based admin query repository for dashboard aggregations
- 14 unit tests covering all command handlers and query handlers
- Added activate() method to UserEntity for unban support
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import { SubscriptionEntity } from '../entities/subscription.entity';
|
||||
|
||||
describe('SubscriptionEntity', () => {
|
||||
const makeSub = (overrides?: Partial<Parameters<typeof SubscriptionEntity.createNew>[0]>) => {
|
||||
return SubscriptionEntity.createNew(
|
||||
'sub-1',
|
||||
'user-1',
|
||||
'plan-1',
|
||||
'AGENT_PRO',
|
||||
new Date('2026-01-01'),
|
||||
new Date('2026-02-01'),
|
||||
);
|
||||
};
|
||||
|
||||
it('creates a new subscription with ACTIVE status', () => {
|
||||
const sub = makeSub();
|
||||
expect(sub.id).toBe('sub-1');
|
||||
expect(sub.userId).toBe('user-1');
|
||||
expect(sub.planTier).toBe('AGENT_PRO');
|
||||
expect(sub.status).toBe('ACTIVE');
|
||||
expect(sub.isActive()).toBe(true);
|
||||
expect(sub.cancelledAt).toBeNull();
|
||||
});
|
||||
|
||||
it('emits SubscriptionCreatedEvent on creation', () => {
|
||||
const sub = makeSub();
|
||||
const events = sub.domainEvents;
|
||||
expect(events).toHaveLength(1);
|
||||
expect(events[0].eventName).toBe('subscription.created');
|
||||
});
|
||||
|
||||
it('upgrades plan tier', () => {
|
||||
const sub = makeSub();
|
||||
sub.clearDomainEvents();
|
||||
|
||||
sub.upgrade('plan-2', 'ENTERPRISE');
|
||||
|
||||
expect(sub.planId).toBe('plan-2');
|
||||
expect(sub.planTier).toBe('ENTERPRISE');
|
||||
const events = sub.domainEvents;
|
||||
expect(events).toHaveLength(1);
|
||||
expect(events[0].eventName).toBe('subscription.upgraded');
|
||||
});
|
||||
|
||||
it('throws when upgrading non-active subscription', () => {
|
||||
const sub = makeSub();
|
||||
sub.cancel();
|
||||
|
||||
expect(() => sub.upgrade('plan-2', 'ENTERPRISE')).toThrow();
|
||||
});
|
||||
|
||||
it('cancels subscription', () => {
|
||||
const sub = makeSub();
|
||||
sub.clearDomainEvents();
|
||||
|
||||
sub.cancel();
|
||||
|
||||
expect(sub.status).toBe('CANCELLED');
|
||||
expect(sub.cancelledAt).not.toBeNull();
|
||||
const events = sub.domainEvents;
|
||||
expect(events).toHaveLength(1);
|
||||
expect(events[0].eventName).toBe('subscription.cancelled');
|
||||
});
|
||||
|
||||
it('throws when cancelling already cancelled subscription', () => {
|
||||
const sub = makeSub();
|
||||
sub.cancel();
|
||||
|
||||
expect(() => sub.cancel()).toThrow('Subscription đã bị hủy');
|
||||
});
|
||||
|
||||
it('marks past due', () => {
|
||||
const sub = makeSub();
|
||||
sub.markPastDue();
|
||||
expect(sub.status).toBe('PAST_DUE');
|
||||
});
|
||||
|
||||
it('marks expired', () => {
|
||||
const sub = makeSub();
|
||||
sub.markExpired();
|
||||
expect(sub.status).toBe('EXPIRED');
|
||||
});
|
||||
|
||||
it('renews period', () => {
|
||||
const sub = makeSub();
|
||||
sub.markPastDue();
|
||||
|
||||
const newStart = new Date('2026-02-01');
|
||||
const newEnd = new Date('2026-03-01');
|
||||
sub.renewPeriod(newStart, newEnd);
|
||||
|
||||
expect(sub.status).toBe('ACTIVE');
|
||||
expect(sub.currentPeriodStart).toEqual(newStart);
|
||||
expect(sub.currentPeriodEnd).toEqual(newEnd);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user