diff --git a/eeg_backend/src/test/java/at/mueller/eeg/backend/dashboard/service/DashboardServiceIntegrationTest.java b/eeg_backend/src/test/java/at/mueller/eeg/backend/dashboard/service/DashboardServiceIntegrationTest.java new file mode 100644 index 0000000..ee65c42 --- /dev/null +++ b/eeg_backend/src/test/java/at/mueller/eeg/backend/dashboard/service/DashboardServiceIntegrationTest.java @@ -0,0 +1,137 @@ +package at.mueller.eeg.backend.dashboard.service; + +import at.mueller.eeg.backend.community.api.dto.MeteringDataRecordDto; +import at.mueller.eeg.backend.community.api.dto.MeteringDataUploadRequest; +import at.mueller.eeg.backend.community.api.dto.MeteringDataUploadResponse; +import at.mueller.eeg.backend.community.domain.*; +import at.mueller.eeg.backend.community.domain.state.MakoState; +import at.mueller.eeg.backend.community.repository.MeteringDataRepository; +import at.mueller.eeg.backend.community.repository.MeteringPointRepository; +import at.mueller.eeg.backend.community.service.MeteringDataService; +import at.mueller.eeg.backend.dashboard.api.dto.DashboardStatsDto; +import at.mueller.eeg.backend.iam.domain.ParticipantType; +import at.mueller.eeg.backend.iam.domain.RegistrationStatus; +import at.mueller.eeg.backend.iam.domain.User; +import at.mueller.eeg.backend.iam.domain.UserRole; +import at.mueller.eeg.backend.iam.repository.UserRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +@ActiveProfiles("test") +@Transactional +class DashboardServiceIntegrationTest { + + @Autowired + private DashboardService dashboardService; + + @Autowired + private MeteringDataService meteringDataService; + + @Autowired + private MeteringPointRepository meteringPointRepository; + + @Autowired + private MeteringDataRepository meteringDataRepository; + + @Autowired + private UserRepository userRepository; + + private User testUser; + private MeteringPoint testMeteringPoint; + + @BeforeEach + void setUp() { + testUser = new User(); + testUser.setEmail("test-dashboard-" + UUID.randomUUID() + "@example.com"); + testUser.setPasswordHash("encoded"); + testUser.setFirstName("Test"); + testUser.setLastName("Dashboard"); + testUser.setRole(UserRole.MEMBER); + testUser.setStatus(RegistrationStatus.APPROVED); + testUser.setParticipantType(ParticipantType.PRIVATE); + testUser.setEnabled(true); + testUser = userRepository.save(testUser); + + testMeteringPoint = new MeteringPoint(); + testMeteringPoint.setUserId(testUser.getId()); + testMeteringPoint.setAtNumber("AT0010000000000000000000001234567"); + testMeteringPoint.setType(PointType.CONSUMER); + testMeteringPoint.setMakoState(MakoState.ACTIVE); + testMeteringPoint = meteringPointRepository.save(testMeteringPoint); + } + + @Test + void getMeteringDataOverview_afterUpload_returnsNonEmptyTimeSeries() { + // Upload hourly data for 3 days + List records = new ArrayList<>(); + Instant base = Instant.parse("2026-06-01T00:00:00Z"); + for (int i = 0; i < 72; i++) { // 3 days * 24 hours + Instant start = base.plusSeconds(i * 3600L); + Instant end = start.plusSeconds(3600); + records.add(new MeteringDataRecordDto( + testMeteringPoint.getAtNumber(), MeteringDataType.CONSUMPTION, + start, end, 0.5 + (i % 10) * 0.1, null)); + } + + MeteringDataUploadRequest request = new MeteringDataUploadRequest( + DataSource.EMAIL_XLSX, "test.xlsx", records); + MeteringDataUploadResponse uploadResponse = meteringDataService.uploadMeteringData(request, testUser.getId()); + assertEquals(UploadStatus.COMPLETED, uploadResponse.status()); + + // Now query via dashboard service + Instant from = Instant.parse("2026-06-01T00:00:00Z"); + Instant to = Instant.parse("2026-06-04T00:00:00Z"); + + DashboardStatsDto.MeteringDataOverview overview = dashboardService.getMeteringDataOverview(testUser.getId(), from, to); + + assertNotNull(overview); + assertFalse(overview.meteringPoints().isEmpty(), "Should have metering points"); + assertFalse(overview.timeSeries().isEmpty(), "Should have time series data"); + // Data spans 3 full days, but timezone grouping may add 1 day + assertTrue(overview.timeSeries().size() >= 3, "Should have at least 3 days of data"); + assertEquals(1, overview.meteringPoints().size()); + assertTrue(overview.meteringPoints().get(0).totalKwh() > 0, "Total kWh should be > 0"); + } + + @Test + void getMeteringDataOverview_fullYearData_returnsTimeSeries() { + // Upload full year hourly data + List records = new ArrayList<>(); + Instant base = Instant.parse("2026-01-01T00:00:00Z"); + for (int i = 0; i < 8760; i++) { + Instant start = base.plusSeconds(i * 3600L); + Instant end = start.plusSeconds(3600); + records.add(new MeteringDataRecordDto( + testMeteringPoint.getAtNumber(), MeteringDataType.CONSUMPTION, + start, end, 0.5 + (i % 10) * 0.1, null)); + } + + MeteringDataUploadRequest request = new MeteringDataUploadRequest( + DataSource.EMAIL_XLSX, "fullyear.xlsx", records); + MeteringDataUploadResponse uploadResponse = meteringDataService.uploadMeteringData(request, testUser.getId()); + assertEquals(UploadStatus.COMPLETED, uploadResponse.status()); + + // Query last 30 days + Instant from = Instant.parse("2026-06-24T00:00:00Z"); + Instant to = Instant.parse("2026-07-24T00:00:00Z"); + + DashboardStatsDto.MeteringDataOverview overview = dashboardService.getMeteringDataOverview(testUser.getId(), from, to); + + assertNotNull(overview); + assertFalse(overview.meteringPoints().isEmpty(), "Should have metering points"); + assertFalse(overview.timeSeries().isEmpty(), "Should have time series data for the queried range"); + assertTrue(overview.timeSeries().size() >= 29, "Should have ~30 days of data"); + } +} diff --git a/eeg_frontend/src/app/app.config.ts b/eeg_frontend/src/app/app.config.ts index 3d9c586..1f2917b 100644 --- a/eeg_frontend/src/app/app.config.ts +++ b/eeg_frontend/src/app/app.config.ts @@ -6,6 +6,7 @@ import {BASE_PATH} from './api'; import {jwtInterceptor} from './interceptors/jwt.interceptor'; import {errorInterceptor} from './interceptors/error.interceptor'; import {environment} from '../environments/environment'; +import {provideEcharts} from 'ngx-echarts'; export const appConfig: ApplicationConfig = { providers: [ @@ -13,6 +14,7 @@ export const appConfig: ApplicationConfig = { provideRouter(routes), provideHttpClient(withFetch(), withInterceptors([jwtInterceptor, errorInterceptor])), - { provide: BASE_PATH, useValue: environment.apiUrl } + { provide: BASE_PATH, useValue: environment.apiUrl }, + provideEcharts() ] };