package com.dalab.discovery.client.rest; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import java.util.HashMap; import java.util.List; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MockMvc; import org.springframework.web.context.WebApplicationContext; import com.dalab.discovery.client.rest.dto.DiscoveryScanDetail; import com.dalab.discovery.client.rest.dto.DiscoveryScanRequest; import com.dalab.discovery.client.rest.dto.DiscoveryScanSummary; import com.dalab.discovery.common.model.DiscoveryJob; import com.dalab.discovery.common.model.enums.CloudProvider; import com.dalab.discovery.job.JobStatus; import com.dalab.discovery.job.JobType; import com.dalab.discovery.job.config.JobConfiguration; import com.dalab.discovery.job.service.IDiscoveryJobService; import com.dalab.discovery.mapper.DiscoveryScanApiMapper; import com.dalab.discovery.sd.config.TestDatabaseConfiguration; import com.fasterxml.jackson.databind.ObjectMapper; @SpringBootTest( classes = { com.dalab.discovery.application.DADiscoveryAgent.class }, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { "spring.autoconfigure.exclude=com.google.cloud.spring.autoconfigure.secretmanager.GcpSecretManagerAutoConfiguration,com.google.cloud.spring.autoconfigure.core.GcpAutoConfiguration" } ) @AutoConfigureMockMvc @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY) @WithMockUser(roles = { "ADMIN", "USER" }) @ActiveProfiles("test") @Import({TestWebSecurityConfiguration.class, TestDatabaseConfiguration.class}) class DiscoveryJobControllerTest { @Autowired private MockMvc mockMvc; @MockBean private IDiscoveryJobService jobService; @MockBean private DiscoveryScanApiMapper scanMapper; @Autowired private ObjectMapper objectMapper; private DiscoveryJob sampleJob; private DiscoveryScanRequest scanRequest; @BeforeEach void setUp(WebApplicationContext context) { UUID jobId = UUID.randomUUID(); sampleJob = new DiscoveryJob(); sampleJob.setJobId(jobId); sampleJob.setJobName("Test Scan Job"); sampleJob.setStatus(JobStatus.PENDING); sampleJob.setCloudProvider(CloudProvider.GCP); sampleJob.setAccountId("test-account"); sampleJob.setJobType(JobType.RESOURCE_CRAWLER); sampleJob.setParameters(new HashMap<>()); scanRequest = new DiscoveryScanRequest(); scanRequest.setScanName("My GCP Scan"); scanRequest.setCloudConnectionId("gcp-conn-123"); scanRequest.setScanType(DiscoveryScanRequest.ScanType.FULL); DiscoveryScanRequest.Scope scope = new DiscoveryScanRequest.Scope(); DiscoveryScanRequest.GcpScope gcpScope = new DiscoveryScanRequest.GcpScope(); gcpScope.setProjectIds(List.of("gcp-project-1")); scope.setGcp(gcpScope); scanRequest.setScope(scope); } @Test @WithMockUser(authorities = "ROLE_DATA_STEWARD") void triggerScan_AsDataSteward_ShouldSucceed() throws Exception { when(jobService.createJob(any(JobType.class), anyString(), any(CloudProvider.class), anyString())).thenReturn(sampleJob); when(jobService.saveJob(any(DiscoveryJob.class))).thenReturn(sampleJob); when(jobService.executeJob(any(DiscoveryJob.class))).thenReturn(CompletableFuture.completedFuture(null)); JobConfiguration mockJobConfiguration = mock(JobConfiguration.class); when(jobService.configureJob(any(DiscoveryJob.class))).thenReturn(mockJobConfiguration); when(mockJobConfiguration.withDefaultExecution(any())).thenReturn(mockJobConfiguration); mockMvc.perform(post("/api/v1/discovery/scans") .with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(scanRequest))) .andExpect(status().isAccepted()) .andExpect(jsonPath("$.scanId").value(sampleJob.getJobId().toString())) .andExpect(jsonPath("$.status").value(JobStatus.PENDING.name())); } @Test @WithMockUser(authorities = "ROLE_USER") void triggerScan_AsUser_ShouldBeForbidden() throws Exception { mockMvc.perform(post("/api/v1/discovery/scans") .with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(scanRequest))) .andExpect(status().isForbidden()); } @Test @WithMockUser(authorities = "ROLE_USER") void getAllDiscoveryScans_AsUser_ShouldSucceed() throws Exception { List jobList = List.of(sampleJob); DiscoveryScanSummary summary = DiscoveryScanSummary.builder() .scanId(sampleJob.getJobId().toString()) .scanName(sampleJob.getJobName()) .scanType(JobType.RESOURCE_CRAWLER.name()) .status(JobStatus.PENDING.name()) .build(); // Mock the actual methods called by the controller when(jobService.getAllJobs()).thenReturn(jobList); when(scanMapper.toDiscoveryScanSummary(eq(sampleJob), anyString())).thenReturn(summary); mockMvc.perform(get("/api/v1/discovery/scans").param("page", "0").param("size", "20")) .andExpect(status().isOk()) .andDo(print()) .andExpect(jsonPath("$.content[0].scanId").value(sampleJob.getJobId().toString())); } @Test @WithMockUser(authorities = "ROLE_USER") void getDiscoveryScanById_AsUser_ShouldSucceed() throws Exception { DiscoveryScanDetail detail = DiscoveryScanDetail.builder() .scanId(sampleJob.getJobId().toString()) .scanName(sampleJob.getJobName()) .status(sampleJob.getStatus().name()) .build(); when(jobService.getJob(sampleJob.getJobId())).thenReturn(Optional.of(sampleJob)); when(scanMapper.toDiscoveryScanDetail(eq(sampleJob), anyString())).thenReturn(detail); mockMvc.perform(get("/api/v1/discovery/scans/{scanId}", sampleJob.getJobId())) .andExpect(status().isOk()) .andExpect(jsonPath("$.scanId").value(sampleJob.getJobId().toString())); } @Test @WithMockUser(authorities = "ROLE_USER") void createLegacyJob_AsUser_ShouldBeForbidden() throws Exception { mockMvc.perform(post("/api/v1/discovery/gcp/jobs") .with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content("{ \"accountId\": \"test\", \"jobName\": \"legacy\" }")) .andExpect(status().isForbidden()); } }