Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: CI

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'

- name: Run Gradle build
run: ./gradlew build
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,18 @@ out/

### VS Code ###
.vscode/

# >>> ExecuteSpec — added missing vendored-path ignores at clone time (see VendoredPathGuard)
node_modules/
venv/
.venv/
__pycache__/
.next/
coverage/
bower_components/
vendor/
.pytest_cache/
.mypy_cache/
.tox/
.nyc_output/
# <<< ExecuteSpec
285 changes: 285 additions & 0 deletions src/test/java/com/navneet/executor/controller/TaskControllerTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
package com.navneet.executor.controller;

import com.navneet.executor.models.ServiceRequest;
import com.navneet.executor.models.TaskStatus;
import com.navneet.executor.models.UploadRequest;
import com.navneet.executor.models.UploadResponse;
import com.navneet.executor.service.UploadService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

/**
* Unit tests for TaskController
* Tests cover happy path and error cases for all public endpoints
*/
@DisplayName("TaskController Tests")
class TaskControllerTest {

@Mock
private UploadService uploadService;

@InjectMocks
private TaskController taskController;

@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}

// ==================== uploadCsvSingleThreaded Tests ====================

@Test
@DisplayName("uploadCsvSingleThreaded - Happy Path: Should return UploadResponse with valid data")
void testUploadCsvSingleThreadedSuccess() {
// Arrange
UploadRequest uploadRequest = new UploadRequest();
uploadRequest.setPath("/path/to/file.csv");

ServiceRequest<UploadRequest> serviceRequest = new ServiceRequest<>();
serviceRequest.setData(uploadRequest);

UploadResponse expectedResponse = UploadResponse.builder()
.count(100)
.taskId("task-123")
.timeTaken(5000L)
.build();

when(uploadService.uploadCsvSingleThreaded(any(UploadRequest.class)))
.thenReturn(expectedResponse);

// Act
UploadResponse actualResponse = taskController.uploadCsvSingleThreaded(serviceRequest);

// Assert
assertNotNull(actualResponse);
assertEquals(100, actualResponse.getCount());
assertEquals("task-123", actualResponse.getTaskId());
assertEquals(5000L, actualResponse.getTimeTaken());
verify(uploadService, times(1)).uploadCsvSingleThreaded(any(UploadRequest.class));
}

@Test
@DisplayName("uploadCsvSingleThreaded - Error Case: Should handle null response from service")
void testUploadCsvSingleThreadedNullResponse() {
// Arrange
UploadRequest uploadRequest = new UploadRequest();
uploadRequest.setPath("/invalid/path.csv");

ServiceRequest<UploadRequest> serviceRequest = new ServiceRequest<>();
serviceRequest.setData(uploadRequest);

when(uploadService.uploadCsvSingleThreaded(any(UploadRequest.class)))
.thenReturn(null);

// Act
UploadResponse actualResponse = taskController.uploadCsvSingleThreaded(serviceRequest);

// Assert
assertNull(actualResponse);
verify(uploadService, times(1)).uploadCsvSingleThreaded(any(UploadRequest.class));
}

// ==================== uploadCsvMultiThreaded Tests ====================

@Test
@DisplayName("uploadCsvMultiThreaded - Happy Path: Should return UploadResponse with valid data")
void testUploadCsvMultiThreadedSuccess() {
// Arrange
UploadRequest uploadRequest = new UploadRequest();
uploadRequest.setPath("/path/to/file.csv");

ServiceRequest<UploadRequest> serviceRequest = new ServiceRequest<>();
serviceRequest.setData(uploadRequest);

UploadResponse expectedResponse = UploadResponse.builder()
.count(500)
.taskId("task-456")
.timeTaken(2000L)
.build();

when(uploadService.uploadCsvMultiThreaded(any(UploadRequest.class)))
.thenReturn(expectedResponse);

// Act
UploadResponse actualResponse = taskController.uploadCsvMultiThreaded(serviceRequest);

// Assert
assertNotNull(actualResponse);
assertEquals(500, actualResponse.getCount());
assertEquals("task-456", actualResponse.getTaskId());
assertEquals(2000L, actualResponse.getTimeTaken());
verify(uploadService, times(1)).uploadCsvMultiThreaded(any(UploadRequest.class));
}

@Test
@DisplayName("uploadCsvMultiThreaded - Error Case: Should handle service returning null")
void testUploadCsvMultiThreadedNullResponse() {
// Arrange
UploadRequest uploadRequest = new UploadRequest();
uploadRequest.setPath("/invalid/path.csv");

ServiceRequest<UploadRequest> serviceRequest = new ServiceRequest<>();
serviceRequest.setData(uploadRequest);

when(uploadService.uploadCsvMultiThreaded(any(UploadRequest.class)))
.thenReturn(null);

// Act
UploadResponse actualResponse = taskController.uploadCsvMultiThreaded(serviceRequest);

// Assert
assertNull(actualResponse);
verify(uploadService, times(1)).uploadCsvMultiThreaded(any(UploadRequest.class));
}

// ==================== uploadCsvAsynchronous Tests ====================

@Test
@DisplayName("uploadCsvAsynchronous - Happy Path: Should return UploadResponse with valid data")
void testUploadCsvAsynchronousSuccess() {
// Arrange
UploadRequest uploadRequest = new UploadRequest();
uploadRequest.setPath("/path/to/file.csv");

ServiceRequest<UploadRequest> serviceRequest = new ServiceRequest<>();
serviceRequest.setData(uploadRequest);

UploadResponse expectedResponse = UploadResponse.builder()
.count(1000)
.taskId("task-789")
.timeTaken(1500L)
.build();

when(uploadService.uploadCsvAsynchronous(any(UploadRequest.class)))
.thenReturn(expectedResponse);

// Act
UploadResponse actualResponse = taskController.uploadCsvAsynchronous(serviceRequest);

// Assert
assertNotNull(actualResponse);
assertEquals(1000, actualResponse.getCount());
assertEquals("task-789", actualResponse.getTaskId());
assertEquals(1500L, actualResponse.getTimeTaken());
verify(uploadService, times(1)).uploadCsvAsynchronous(any(UploadRequest.class));
}

@Test
@DisplayName("uploadCsvAsynchronous - Error Case: Should handle service returning null")
void testUploadCsvAsynchronousNullResponse() {
// Arrange
UploadRequest uploadRequest = new UploadRequest();
uploadRequest.setPath("/invalid/path.csv");

ServiceRequest<UploadRequest> serviceRequest = new ServiceRequest<>();
serviceRequest.setData(uploadRequest);

when(uploadService.uploadCsvAsynchronous(any(UploadRequest.class)))
.thenReturn(null);

// Act
UploadResponse actualResponse = taskController.uploadCsvAsynchronous(serviceRequest);

// Assert
assertNull(actualResponse);
verify(uploadService, times(1)).uploadCsvAsynchronous(any(UploadRequest.class));
}

// ==================== checkUploadTaskStatus Tests ====================

@Test
@DisplayName("checkUploadTaskStatus - Happy Path: Should return TaskStatus with valid data")
void testCheckUploadTaskStatusSuccess() {
// Arrange
String taskId = "task-123";
TaskStatus expectedStatus = TaskStatus.builder()
.taskId(taskId)
.status("COMPLETED")
.processed(100)
.total(100)
.build();

when(uploadService.checkUploadTaskStatus(taskId))
.thenReturn(expectedStatus);

// Act
TaskStatus actualStatus = taskController.checkUploadTaskStatus(taskId);

// Assert
assertNotNull(actualStatus);
assertEquals(taskId, actualStatus.getTaskId());
assertEquals("COMPLETED", actualStatus.getStatus());
assertEquals(100, actualStatus.getProcessed());
assertEquals(100, actualStatus.getTotal());
verify(uploadService, times(1)).checkUploadTaskStatus(taskId);
}

@Test
@DisplayName("checkUploadTaskStatus - Happy Path: Should return TaskStatus with PROCESSING status")
void testCheckUploadTaskStatusProcessing() {
// Arrange
String taskId = "task-456";
TaskStatus expectedStatus = TaskStatus.builder()
.taskId(taskId)
.status("PROCESSING")
.processed(50)
.total(100)
.build();

when(uploadService.checkUploadTaskStatus(taskId))
.thenReturn(expectedStatus);

// Act
TaskStatus actualStatus = taskController.checkUploadTaskStatus(taskId);

// Assert
assertNotNull(actualStatus);
assertEquals(taskId, actualStatus.getTaskId());
assertEquals("PROCESSING", actualStatus.getStatus());
assertEquals(50, actualStatus.getProcessed());
assertEquals(100, actualStatus.getTotal());
verify(uploadService, times(1)).checkUploadTaskStatus(taskId);
}

@Test
@DisplayName("checkUploadTaskStatus - Error Case: Should handle invalid task ID")
void testCheckUploadTaskStatusInvalidTaskId() {
// Arrange
String invalidTaskId = "invalid-task-id";

when(uploadService.checkUploadTaskStatus(invalidTaskId))
.thenReturn(null);

// Act
TaskStatus actualStatus = taskController.checkUploadTaskStatus(invalidTaskId);

// Assert
assertNull(actualStatus);
verify(uploadService, times(1)).checkUploadTaskStatus(invalidTaskId);
}

@Test
@DisplayName("checkUploadTaskStatus - Error Case: Should handle empty task ID")
void testCheckUploadTaskStatusEmptyTaskId() {
// Arrange
String emptyTaskId = "";

when(uploadService.checkUploadTaskStatus(emptyTaskId))
.thenReturn(null);

// Act
TaskStatus actualStatus = taskController.checkUploadTaskStatus(emptyTaskId);

// Assert
assertNull(actualStatus);
verify(uploadService, times(1)).checkUploadTaskStatus(emptyTaskId);
}
}
Loading