Skip to content

Commit 74f741c

Browse files
author
martin
committed
Add comprehensive automated tests and fix CredentialController auth
Backend Tests: - HealthControllerTest: Health check endpoint tests (6 tests) - RateLimitFilterTest: Rate limiting functionality tests (10 tests) - DatabaseControllerTest: SQL query validation & security tests (17 tests) - CodeControllerTest: Code execution validation tests (13 tests) - InputValidationTest: DTO validation tests for all request DTOs (25+ tests) Frontend Tests: - ErrorBoundary.test.tsx: Error boundary component tests (15 tests) - DashboardPage.test.tsx: Dashboard page tests (20+ tests) Fixes: - CredentialController: Fixed user ID extraction from JWT token (was trying to parse username as UUID, now properly looks up user) - DatabaseController: Added @Valid and validation to QueryRequest CI/CD: - Enhanced GitHub Actions workflow with proper environment variables - Added test reports and coverage artifacts - Created application-test.yml for test profile configuration
1 parent 6656626 commit 74f741c

14 files changed

Lines changed: 2322 additions & 8 deletions

File tree

.github/workflows/tests.yml

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,47 @@ jobs:
3939
- name: Run unit tests
4040
run: |
4141
cd backend
42-
mvn test -Dspring.datasource.url=jdbc:postgresql://localhost:5432/testdb
42+
mvn test -Dspring.profiles.active=test
43+
env:
44+
SPRING_DATASOURCE_URL: jdbc:postgresql://localhost:5432/testdb
45+
SPRING_DATASOURCE_USERNAME: test
46+
SPRING_DATASOURCE_PASSWORD: test
47+
APP_JWT_SECRET: test-jwt-secret-key-for-github-actions-must-be-at-least-64-characters-long
48+
APP_JWT_EXPIRATION: 86400000
49+
KAFKA_BOOTSTRAP_SERVERS: localhost:9092
4350

4451
- name: Run integration tests
4552
run: |
4653
cd backend
47-
mvn verify -Dspring.datasource.url=jdbc:postgresql://localhost:5432/testdb
54+
mvn verify -Dspring.profiles.active=test -DskipUnitTests
55+
env:
56+
SPRING_DATASOURCE_URL: jdbc:postgresql://localhost:5432/testdb
57+
SPRING_DATASOURCE_USERNAME: test
58+
SPRING_DATASOURCE_PASSWORD: test
59+
APP_JWT_SECRET: test-jwt-secret-key-for-github-actions-must-be-at-least-64-characters-long
60+
APP_JWT_EXPIRATION: 86400000
61+
KAFKA_BOOTSTRAP_SERVERS: localhost:9092
62+
63+
- name: Generate test report
64+
if: always()
65+
run: |
66+
cd backend
67+
echo "## Backend Test Results" >> $GITHUB_STEP_SUMMARY
68+
echo "" >> $GITHUB_STEP_SUMMARY
69+
if [ -d "task-service/target/surefire-reports" ]; then
70+
TESTS=$(find task-service/target/surefire-reports -name "*.xml" -exec grep -h "tests=" {} \; | head -1)
71+
echo "Unit tests completed" >> $GITHUB_STEP_SUMMARY
72+
fi
73+
74+
- name: Upload test results
75+
if: always()
76+
uses: actions/upload-artifact@v4
77+
with:
78+
name: backend-test-results
79+
path: |
80+
backend/task-service/target/surefire-reports/
81+
backend/task-service/target/failsafe-reports/
82+
retention-days: 7
4883

4984
frontend-tests:
5085
name: Frontend Tests
@@ -68,7 +103,27 @@ jobs:
68103
- name: Run unit tests
69104
run: |
70105
cd frontend
71-
npm test -- --watchAll=false --coverage
106+
npm test -- --watchAll=false --coverage --coverageReporters=text --coverageReporters=lcov
107+
env:
108+
CI: true
109+
110+
- name: Generate test summary
111+
if: always()
112+
run: |
113+
cd frontend
114+
echo "## Frontend Test Results" >> $GITHUB_STEP_SUMMARY
115+
echo "" >> $GITHUB_STEP_SUMMARY
116+
if [ -f "coverage/lcov-report/index.html" ]; then
117+
echo "Coverage report generated" >> $GITHUB_STEP_SUMMARY
118+
fi
119+
120+
- name: Upload coverage report
121+
if: always()
122+
uses: actions/upload-artifact@v4
123+
with:
124+
name: frontend-coverage
125+
path: frontend/coverage/
126+
retention-days: 7
72127

73128
- name: Build frontend
74129
run: |

backend/task-service/src/main/java/io/celox/taskflow/task/controller/CredentialController.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import io.celox.taskflow.task.domain.Credential;
44
import io.celox.taskflow.task.dto.CreateCredentialDto;
55
import io.celox.taskflow.task.dto.CredentialDto;
6+
import io.celox.taskflow.task.dto.UserDto;
67
import io.celox.taskflow.task.service.CredentialService;
8+
import io.celox.taskflow.task.service.UserService;
79
import lombok.RequiredArgsConstructor;
810
import lombok.extern.slf4j.Slf4j;
911
import org.springframework.http.ResponseEntity;
@@ -23,6 +25,7 @@
2325
public class CredentialController {
2426

2527
private final CredentialService credentialService;
28+
private final UserService userService;
2629

2730
@PostMapping
2831
public ResponseEntity<CredentialDto> createCredential(@Valid @RequestBody CreateCredentialDto request) {
@@ -71,19 +74,31 @@ public ResponseEntity<Void> deleteCredential(@PathVariable UUID id) {
7174
}
7275

7376
/**
74-
* Extract user ID from JWT token
77+
* Extract user ID from JWT token.
78+
* The token contains the username, so we need to look up the user to get the UUID.
7579
*/
7680
private UUID extractUserIdFromToken() {
7781
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
7882
if (authentication == null || authentication.getName() == null) {
7983
throw new IllegalStateException("User not authenticated");
8084
}
8185

86+
String username = authentication.getName();
87+
88+
// First try to parse as UUID (in case it's already a UUID)
8289
try {
83-
return UUID.fromString(authentication.getName());
90+
return UUID.fromString(username);
8491
} catch (IllegalArgumentException e) {
85-
log.error("Invalid user ID in token: {}", authentication.getName());
86-
throw new IllegalStateException("Invalid user ID in token");
92+
// Not a UUID, so it's a username - look up the user
93+
log.debug("Looking up user by username: {}", username);
94+
}
95+
96+
try {
97+
UserDto user = userService.getUserByUsername(username);
98+
return user.getId();
99+
} catch (Exception e) {
100+
log.error("Failed to find user by username: {}", username, e);
101+
throw new IllegalStateException("User not found: " + username);
87102
}
88103
}
89104

backend/task-service/src/main/java/io/celox/taskflow/task/controller/DatabaseController.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
import io.swagger.v3.oas.annotations.Operation;
44
import io.swagger.v3.oas.annotations.responses.ApiResponse;
55
import io.swagger.v3.oas.annotations.tags.Tag;
6+
import jakarta.validation.Valid;
7+
import jakarta.validation.constraints.NotBlank;
8+
import jakarta.validation.constraints.Size;
69
import lombok.Data;
710
import lombok.RequiredArgsConstructor;
811
import lombok.extern.slf4j.Slf4j;
@@ -41,7 +44,7 @@ public class DatabaseController {
4144
@PostMapping("/query")
4245
@Operation(summary = "Execute a SQL query")
4346
@ApiResponse(responseCode = "200", description = "Query executed successfully")
44-
public ResponseEntity<QueryResult> executeQuery(@RequestBody QueryRequest request) {
47+
public ResponseEntity<QueryResult> executeQuery(@Valid @RequestBody QueryRequest request) {
4548
long startTime = System.currentTimeMillis();
4649
String userId = getCurrentUserId();
4750

@@ -233,6 +236,8 @@ private static class SecurityValidationResult {
233236

234237
@Data
235238
public static class QueryRequest {
239+
@NotBlank(message = "Query is required")
240+
@Size(min = 1, max = 10000, message = "Query must be between 1 and 10000 characters")
236241
private String query;
237242
}
238243

backend/task-service/src/main/java/io/celox/taskflow/task/dto/CreateWorkflowDto.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
import lombok.Data;
88
import lombok.NoArgsConstructor;
99

10+
/**
11+
* DTO for creating workflows.
12+
* All fields are validated to prevent abuse and ensure data integrity.
13+
*/
1014
@Data
1115
@NoArgsConstructor
1216
@AllArgsConstructor
@@ -20,9 +24,12 @@ public class CreateWorkflowDto {
2024
@Size(max = 1000, message = "Description must not exceed 1000 characters")
2125
private String description;
2226

27+
@Size(max = 500000, message = "Workflow nodes JSON must not exceed 500KB")
2328
private String nodesJson;
2429

30+
@Size(max = 100000, message = "Workflow edges JSON must not exceed 100KB")
2531
private String edgesJson;
2632

33+
@Size(max = 50000, message = "Workflow triggers JSON must not exceed 50KB")
2734
private String triggersJson;
2835
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
package io.celox.taskflow.task.dto;
22

33
import jakarta.validation.constraints.NotBlank;
4+
import jakarta.validation.constraints.Size;
45
import lombok.AllArgsConstructor;
56
import lombok.Builder;
67
import lombok.Data;
78
import lombok.NoArgsConstructor;
89

10+
/**
11+
* DTO for user login requests.
12+
* All fields are validated to prevent abuse.
13+
*/
914
@Data
1015
@NoArgsConstructor
1116
@AllArgsConstructor
1217
@Builder
1318
public class LoginDto {
1419

1520
@NotBlank(message = "Username is required")
21+
@Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters")
1622
private String username;
1723

1824
@NotBlank(message = "Password is required")
25+
@Size(min = 6, max = 100, message = "Password must be between 6 and 100 characters")
1926
private String password;
2027
}

backend/task-service/src/main/java/io/celox/taskflow/task/dto/UpdateWorkflowDto.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
import lombok.Data;
88
import lombok.NoArgsConstructor;
99

10+
/**
11+
* DTO for updating workflows.
12+
* All fields are validated to prevent abuse and ensure data integrity.
13+
*/
1014
@Data
1115
@NoArgsConstructor
1216
@AllArgsConstructor
@@ -21,9 +25,12 @@ public class UpdateWorkflowDto {
2125

2226
private WorkflowStatus status;
2327

28+
@Size(max = 500000, message = "Workflow nodes JSON must not exceed 500KB")
2429
private String nodesJson;
2530

31+
@Size(max = 100000, message = "Workflow edges JSON must not exceed 100KB")
2632
private String edgesJson;
2733

34+
@Size(max = 50000, message = "Workflow triggers JSON must not exceed 50KB")
2835
private String triggersJson;
2936
}

0 commit comments

Comments
 (0)