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); }); 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)); }); }); });