liu
昨天 43aab58c9ecb1203770ba382efbdbae299ca87ec
yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/energy/EnergyRecordServiceTest.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,178 @@
package cn.iocoder.yudao.module.mes.service.energy;
import cn.iocoder.yudao.module.mes.controller.admin.energy.vo.*;
import cn.iocoder.yudao.module.mes.dal.dataobject.energy.EnergyRecordDO;
import cn.iocoder.yudao.module.mes.dal.mysql.energy.EnergyRecordMapper;
import jakarta.validation.Validation;
import org.apache.ibatis.session.Configuration;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class EnergyRecordServiceTest {
    @Mock private org.springframework.context.ApplicationEventPublisher applicationEventPublisher;
    @Mock
    private EnergyRecordMapper mapper;
    @InjectMocks
    private EnergyRecordService service;
    @Test
    void changedDateChecksBothDaysWithOriginalTenant() {
        var req = request(); req.setId(77L);
        var old = new EnergyRecordDO(); old.setId(77L); old.setRecordDate(req.getRecordDate().minusDays(1));
        when(mapper.selectById(77L)).thenReturn(old);
        when(mapper.updateById(any(EnergyRecordDO.class))).thenReturn(1);
        var previousTenant = cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder.getTenantId();
        try {
            cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder.setTenantId(42L);
            service.update(req);
            var event = org.mockito.ArgumentCaptor.forClass(EnergyRecordChangedEvent.class);
            verify(applicationEventPublisher).publishEvent(event.capture());
            assertEquals(42L, event.getValue().tenantId());
            assertEquals(java.util.Set.of(old.getRecordDate(), req.getRecordDate()), event.getValue().dates());
        } finally { cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder.setTenantId(previousTenant); }
    }
    @Test
    void calculatesMoneyWithDecimalRoundingAndClearsRemark() {
        EnergyRecordSaveReqVO req = request();
        req.setQuantity(new BigDecimal("12.345"));
        req.setUnitPrice(new BigDecimal("0.6789"));
        EnergyRecordDO record = EnergyRecordService.toRecord(req);
        assertEquals(new BigDecimal("8.38"), record.getAmount());
        assertEquals("燕窝生产区", record.getUsageArea());
        assertEquals("", record.getRemark());
        req.setUnitPrice(BigDecimal.ZERO);
        assertEquals(new BigDecimal("0.00"), EnergyRecordService.toRecord(req).getAmount());
    }
    @Test
    void createIgnoresClientIdAndUpdateRequiresExistingRecord() {
        EnergyRecordSaveReqVO req = request();
        req.setId(77L);
        doAnswer(invocation -> {
            EnergyRecordDO record = invocation.getArgument(0);
            assertNull(record.getId());
            record.setId(9L);
            return 1;
        }).when(mapper).insert(any(EnergyRecordDO.class));
        assertEquals(9L, service.create(req));
        assertThrows(RuntimeException.class, () -> service.update(req));
        verify(mapper, never()).updateById(any(EnergyRecordDO.class));
        req.setId(null);
        assertThrows(RuntimeException.class, () -> service.update(req));
    }
    @Test
    void deleteMissingRecordDoesNotExecuteDelete() {
        assertThrows(RuntimeException.class, () -> service.delete(8L));
        verify(mapper, never()).deleteById(any(Long.class));
    }
    @Test
    void aggregatesSeparateUnitsAndMergesDatesAreasAndCosts() {
        EnergyAnalysisRespVO result = EnergyRecordService.aggregate(List.of(
                row("WATER", "2026-09-01", "燕窝生产区", "10", "30", 1),
                row("WATER", "2026-09-01", "原浆生产区", "5", "15", 2),
                row("ELECTRICITY", "2026-09-01", "燕窝生产区", "100", "80", 1),
                row("WATER", "2026-09-02", "燕窝生产区", "2", "0", 1)));
        assertEquals(new BigDecimal("125"), result.getTotalAmount());
        assertEquals(5, result.getRecordCount());
        assertEquals(new BigDecimal("17"), result.getSummary().get(0).getQuantity());
        assertEquals(new BigDecimal("100"), result.getSummary().get(1).getQuantity());
        assertEquals(BigDecimal.ZERO, result.getSummary().get(2).getQuantity());
        assertEquals(3, result.getTrend().size());
        assertEquals(new BigDecimal("15"), result.getTrend().get(0).getQuantity());
        assertEquals(3, result.getAreas().size());
        assertEquals(new BigDecimal("12"), result.getAreas().get(0).getQuantity());
    }
    @Test
    void emptyAnalysisReturnsZeroTotalsAndAllThreeEnergies() {
        EnergyAnalysisRespVO result = EnergyRecordService.aggregate(List.of());
        assertEquals(3, result.getSummary().size());
        assertEquals(BigDecimal.ZERO, result.getTotalAmount());
        assertTrue(result.getTrend().isEmpty());
        assertTrue(result.getAreas().isEmpty());
    }
    @Test
    void rejectsInvalidTypeNegativePriceZeroQuantityFutureDateAndExcessPrecision() {
        try (var factory = Validation.buildDefaultValidatorFactory()) {
            var validator = factory.getValidator();
            assertTrue(validator.validate(request()).isEmpty());
            EnergyRecordSaveReqVO req = request();
            req.setEnergyType("GAS");
            req.setUnitPrice(new BigDecimal("-1"));
            req.setQuantity(BigDecimal.ZERO);
            req.setRecordDate(LocalDate.now().plusDays(1));
            assertTrue(validator.validate(req).size() >= 4);
            req = request();
            req.setQuantity(new BigDecimal("1.0001"));
            assertFalse(validator.validate(req).isEmpty());
            EnergyAnalysisReqVO analysis = new EnergyAnalysisReqVO();
            analysis.setStartDate(LocalDate.of(2026, 1, 1));
            analysis.setEndDate(LocalDate.of(2025, 1, 1));
            assertFalse(validator.validate(analysis).isEmpty());
            analysis.setEndDate(LocalDate.of(2029, 1, 1));
            assertFalse(validator.validate(analysis).isEmpty());
        }
    }
    @Test
    void analysisSqlBindsFiltersAndSelectsDayOrMonthSafely() {
        Configuration config = new Configuration();
        config.addMapper(EnergyRecordMapper.class);
        var statement = config.getMappedStatement(EnergyRecordMapper.class.getName() + ".selectAnalysis");
        EnergyAnalysisReqVO req = new EnergyAnalysisReqVO();
        req.setStartDate(LocalDate.of(2026, 1, 1));
        req.setEndDate(LocalDate.of(2026, 1, 31));
        String daily = statement.getBoundSql(Map.of("req", req)).getSql();
        assertTrue(daily.contains("'%Y-%m-%d'"));
        assertTrue(daily.contains("deleted = 0"));
        req.setGroupBy("MONTH");
        req.setEnergyType("STEAM");
        req.setUsageArea("' OR 1=1 --");
        req.setProductCategory("燕窝");
        String monthly = statement.getBoundSql(Map.of("req", req)).getSql();
        assertTrue(monthly.contains("'%Y-%m'"));
        assertTrue(monthly.contains("energy_type = ?"));
        assertTrue(monthly.contains("product_category = ?"));
        assertFalse(monthly.contains(req.getUsageArea()));
    }
    private static EnergyRecordSaveReqVO request() {
        EnergyRecordSaveReqVO req = new EnergyRecordSaveReqVO();
        req.setRecordDate(LocalDate.now());
        req.setEnergyType("WATER");
        req.setUsageArea(" ç‡•窝生产区 ");
        req.setProductCategory("燕窝");
        req.setUsagePurpose("设备清洗");
        req.setQuantity(BigDecimal.ONE);
        req.setUnitPrice(BigDecimal.ONE);
        req.setRecorder("张三");
        return req;
    }
    private static EnergyRecordMapper.Aggregate row(String type, String period, String area,
                                                    String quantity, String amount, long count) {
        EnergyRecordMapper.Aggregate row = new EnergyRecordMapper.Aggregate();
        row.setEnergyType(type);
        row.setPeriod(period);
        row.setUsageArea(area);
        row.setQuantity(new BigDecimal(quantity));
        row.setAmount(new BigDecimal(amount));
        row.setRecordCount(count);
        return row;
    }
}