rwadurian/backend/services/reward-service/test/integration/reward-calculation.service....

234 lines
10 KiB
TypeScript

import { Test, TestingModule } from '@nestjs/testing';
import {
RewardCalculationService,
REFERRAL_SERVICE_CLIENT,
AUTHORIZATION_SERVICE_CLIENT,
} from '../../src/domain/services/reward-calculation.service';
import { RightType } from '../../src/domain/value-objects/right-type.enum';
describe('RewardCalculationService (Integration)', () => {
let service: RewardCalculationService;
let mockReferralService: any;
let mockAuthorizationService: any;
beforeEach(async () => {
mockReferralService = {
getReferralChain: jest.fn(),
};
mockAuthorizationService = {
findNearestAuthorizedProvince: jest.fn(),
findNearestAuthorizedCity: jest.fn(),
findNearestCommunity: jest.fn(),
};
const module: TestingModule = await Test.createTestingModule({
providers: [
RewardCalculationService,
{
provide: REFERRAL_SERVICE_CLIENT,
useValue: mockReferralService,
},
{
provide: AUTHORIZATION_SERVICE_CLIENT,
useValue: mockAuthorizationService,
},
],
}).compile();
service = module.get<RewardCalculationService>(RewardCalculationService);
});
describe('calculateRewards', () => {
const baseParams = {
sourceOrderNo: 'ORDER001',
sourceUserId: BigInt(100),
treeCount: 10,
provinceCode: '440000',
cityCode: '440100',
};
it('should calculate all 6 types of rewards', async () => {
// Setup: referrer with planting status
mockReferralService.getReferralChain.mockResolvedValue({
ancestors: [{ userId: BigInt(200), hasPlanted: true }],
});
mockAuthorizationService.findNearestAuthorizedProvince.mockResolvedValue(BigInt(300));
mockAuthorizationService.findNearestAuthorizedCity.mockResolvedValue(BigInt(400));
mockAuthorizationService.findNearestCommunity.mockResolvedValue(BigInt(500));
const rewards = await service.calculateRewards(baseParams);
// Should return 6 rewards (one for each type)
expect(rewards).toHaveLength(6);
// Verify each reward type exists
const rightTypes = rewards.map((r) => r.rewardSource.rightType);
expect(rightTypes).toContain(RightType.SHARE_RIGHT);
expect(rightTypes).toContain(RightType.PROVINCE_TEAM_RIGHT);
expect(rightTypes).toContain(RightType.PROVINCE_AREA_RIGHT);
expect(rightTypes).toContain(RightType.CITY_TEAM_RIGHT);
expect(rightTypes).toContain(RightType.CITY_AREA_RIGHT);
expect(rightTypes).toContain(RightType.COMMUNITY_RIGHT);
});
it('should calculate share right reward (500 USDT) when referrer has planted', async () => {
mockReferralService.getReferralChain.mockResolvedValue({
ancestors: [{ userId: BigInt(200), hasPlanted: true }],
});
mockAuthorizationService.findNearestAuthorizedProvince.mockResolvedValue(null);
mockAuthorizationService.findNearestAuthorizedCity.mockResolvedValue(null);
mockAuthorizationService.findNearestCommunity.mockResolvedValue(null);
const rewards = await service.calculateRewards(baseParams);
const shareReward = rewards.find(
(r) => r.rewardSource.rightType === RightType.SHARE_RIGHT,
);
expect(shareReward).toBeDefined();
expect(shareReward?.usdtAmount.amount).toBe(500 * 10); // 500 USDT per tree
expect(shareReward?.userId).toBe(BigInt(200));
expect(shareReward?.isSettleable).toBe(true); // Already planted, so settleable
});
it('should create pending share right reward when referrer has not planted', async () => {
mockReferralService.getReferralChain.mockResolvedValue({
ancestors: [{ userId: BigInt(200), hasPlanted: false }],
});
mockAuthorizationService.findNearestAuthorizedProvince.mockResolvedValue(null);
mockAuthorizationService.findNearestAuthorizedCity.mockResolvedValue(null);
mockAuthorizationService.findNearestCommunity.mockResolvedValue(null);
const rewards = await service.calculateRewards(baseParams);
const shareReward = rewards.find(
(r) => r.rewardSource.rightType === RightType.SHARE_RIGHT,
);
expect(shareReward).toBeDefined();
expect(shareReward?.isPending).toBe(true); // Not planted, so pending with 24h countdown
expect(shareReward?.expireAt).toBeDefined();
});
it('should assign share right to headquarters when no referrer', async () => {
mockReferralService.getReferralChain.mockResolvedValue({
ancestors: [],
});
mockAuthorizationService.findNearestAuthorizedProvince.mockResolvedValue(null);
mockAuthorizationService.findNearestAuthorizedCity.mockResolvedValue(null);
mockAuthorizationService.findNearestCommunity.mockResolvedValue(null);
const rewards = await service.calculateRewards(baseParams);
const shareReward = rewards.find(
(r) => r.rewardSource.rightType === RightType.SHARE_RIGHT,
);
expect(shareReward).toBeDefined();
expect(shareReward?.userId).toBe(BigInt(1)); // Headquarters community ID
});
it('should calculate province team right (20 USDT)', async () => {
mockReferralService.getReferralChain.mockResolvedValue({ ancestors: [] });
mockAuthorizationService.findNearestAuthorizedProvince.mockResolvedValue(BigInt(300));
mockAuthorizationService.findNearestAuthorizedCity.mockResolvedValue(null);
mockAuthorizationService.findNearestCommunity.mockResolvedValue(null);
const rewards = await service.calculateRewards(baseParams);
const provinceTeamReward = rewards.find(
(r) => r.rewardSource.rightType === RightType.PROVINCE_TEAM_RIGHT,
);
expect(provinceTeamReward).toBeDefined();
expect(provinceTeamReward?.usdtAmount.amount).toBe(20 * 10);
expect(provinceTeamReward?.userId).toBe(BigInt(300));
});
it('should calculate province area right (15 USDT + 1% hashpower)', async () => {
mockReferralService.getReferralChain.mockResolvedValue({ ancestors: [] });
mockAuthorizationService.findNearestAuthorizedProvince.mockResolvedValue(null);
mockAuthorizationService.findNearestAuthorizedCity.mockResolvedValue(null);
mockAuthorizationService.findNearestCommunity.mockResolvedValue(null);
const rewards = await service.calculateRewards(baseParams);
const provinceAreaReward = rewards.find(
(r) => r.rewardSource.rightType === RightType.PROVINCE_AREA_RIGHT,
);
expect(provinceAreaReward).toBeDefined();
expect(provinceAreaReward?.usdtAmount.amount).toBe(15 * 10);
expect(provinceAreaReward?.hashpowerAmount.value).toBe(1 * 10); // 1% * 10 trees = 10
});
it('should calculate city team right (40 USDT)', async () => {
mockReferralService.getReferralChain.mockResolvedValue({ ancestors: [] });
mockAuthorizationService.findNearestAuthorizedProvince.mockResolvedValue(null);
mockAuthorizationService.findNearestAuthorizedCity.mockResolvedValue(BigInt(400));
mockAuthorizationService.findNearestCommunity.mockResolvedValue(null);
const rewards = await service.calculateRewards(baseParams);
const cityTeamReward = rewards.find(
(r) => r.rewardSource.rightType === RightType.CITY_TEAM_RIGHT,
);
expect(cityTeamReward).toBeDefined();
expect(cityTeamReward?.usdtAmount.amount).toBe(40 * 10);
expect(cityTeamReward?.userId).toBe(BigInt(400));
});
it('should calculate city area right (35 USDT + 2% hashpower)', async () => {
mockReferralService.getReferralChain.mockResolvedValue({ ancestors: [] });
mockAuthorizationService.findNearestAuthorizedProvince.mockResolvedValue(null);
mockAuthorizationService.findNearestAuthorizedCity.mockResolvedValue(null);
mockAuthorizationService.findNearestCommunity.mockResolvedValue(null);
const rewards = await service.calculateRewards(baseParams);
const cityAreaReward = rewards.find(
(r) => r.rewardSource.rightType === RightType.CITY_AREA_RIGHT,
);
expect(cityAreaReward).toBeDefined();
expect(cityAreaReward?.usdtAmount.amount).toBe(35 * 10);
expect(cityAreaReward?.hashpowerAmount.value).toBe(2 * 10); // 2% * 10 trees = 20
});
it('should calculate community right (80 USDT)', async () => {
mockReferralService.getReferralChain.mockResolvedValue({ ancestors: [] });
mockAuthorizationService.findNearestAuthorizedProvince.mockResolvedValue(null);
mockAuthorizationService.findNearestAuthorizedCity.mockResolvedValue(null);
mockAuthorizationService.findNearestCommunity.mockResolvedValue(BigInt(500));
const rewards = await service.calculateRewards(baseParams);
const communityReward = rewards.find(
(r) => r.rewardSource.rightType === RightType.COMMUNITY_RIGHT,
);
expect(communityReward).toBeDefined();
expect(communityReward?.usdtAmount.amount).toBe(80 * 10);
expect(communityReward?.userId).toBe(BigInt(500));
});
it('should assign to headquarters when no authorized holder found', async () => {
mockReferralService.getReferralChain.mockResolvedValue({ ancestors: [] });
mockAuthorizationService.findNearestAuthorizedProvince.mockResolvedValue(null);
mockAuthorizationService.findNearestAuthorizedCity.mockResolvedValue(null);
mockAuthorizationService.findNearestCommunity.mockResolvedValue(null);
const rewards = await service.calculateRewards(baseParams);
// Province team, city team, community should go to headquarters (ID=1)
const provinceTeamReward = rewards.find(
(r) => r.rewardSource.rightType === RightType.PROVINCE_TEAM_RIGHT,
);
const cityTeamReward = rewards.find(
(r) => r.rewardSource.rightType === RightType.CITY_TEAM_RIGHT,
);
const communityReward = rewards.find(
(r) => r.rewardSource.rightType === RightType.COMMUNITY_RIGHT,
);
expect(provinceTeamReward?.userId).toBe(BigInt(1));
expect(cityTeamReward?.userId).toBe(BigInt(1));
expect(communityReward?.userId).toBe(BigInt(1));
});
});
});