Skip to content

Commit 43037cb

Browse files
author
p250109
committed
final push
1 parent 4cc8ee3 commit 43037cb

76 files changed

Lines changed: 6801 additions & 677 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@
2020
*.log
2121
logs/
2222
**/logs/
23+
app/
24+
traefik/
25+
prem-portfolio/
26+
logs/
27+
logstash/
28+
fix-opensearch-permissions.ps1
29+
filebeat.yml
30+
fix-opensearch-permissions.sh
31+
logback-spring-json.xml
32+
opensearch-template.json
33+
34+
2335

2436
# -------------------------
2537
# Build & Maven/Gradle outputs

connections-service/pom.xml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,21 @@
5858
<artifactId>spring-boot-starter-test</artifactId>
5959
<scope>test</scope>
6060
</dependency>
61+
<dependency>
62+
<groupId>org.testcontainers</groupId>
63+
<artifactId>junit-jupiter</artifactId>
64+
<scope>test</scope>
65+
</dependency>
66+
<dependency>
67+
<groupId>org.testcontainers</groupId>
68+
<artifactId>neo4j</artifactId>
69+
<scope>test</scope>
70+
</dependency>
71+
<dependency>
72+
<groupId>org.springframework.kafka</groupId>
73+
<artifactId>spring-kafka-test</artifactId>
74+
<scope>test</scope>
75+
</dependency>
6176
<dependency>
6277
<groupId>org.springframework.kafka</groupId>
6378
<artifactId>spring-kafka</artifactId>
@@ -104,6 +119,13 @@
104119
<type>pom</type>
105120
<scope>import</scope>
106121
</dependency>
122+
<dependency>
123+
<groupId>org.testcontainers</groupId>
124+
<artifactId>testcontainers-bom</artifactId>
125+
<version>1.19.0</version>
126+
<type>pom</type>
127+
<scope>import</scope>
128+
</dependency>
107129
</dependencies>
108130
</dependencyManagement>
109131

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.premtsd.linkedin.connectionservice.exception;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import org.springframework.http.HttpStatus;
5+
import org.springframework.http.ResponseEntity;
6+
import org.springframework.web.bind.annotation.ExceptionHandler;
7+
import org.springframework.web.bind.annotation.RestControllerAdvice;
8+
9+
import java.time.LocalDateTime;
10+
import java.util.HashMap;
11+
import java.util.Map;
12+
13+
@RestControllerAdvice
14+
@Slf4j
15+
public class GlobalExceptionHandler {
16+
17+
@ExceptionHandler(BusinessRuleViolationException.class)
18+
public ResponseEntity<Map<String, Object>> handleBusinessRuleViolationException(BusinessRuleViolationException ex) {
19+
log.warn("Business rule violation: {}", ex.getMessage());
20+
21+
Map<String, Object> errorResponse = new HashMap<>();
22+
errorResponse.put("timestamp", LocalDateTime.now());
23+
errorResponse.put("status", HttpStatus.BAD_REQUEST.value());
24+
errorResponse.put("error", "Bad Request");
25+
errorResponse.put("message", ex.getMessage());
26+
27+
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
28+
}
29+
30+
@ExceptionHandler(IllegalArgumentException.class)
31+
public ResponseEntity<Map<String, Object>> handleIllegalArgumentException(IllegalArgumentException ex) {
32+
log.warn("Invalid argument: {}", ex.getMessage());
33+
34+
Map<String, Object> errorResponse = new HashMap<>();
35+
errorResponse.put("timestamp", LocalDateTime.now());
36+
errorResponse.put("status", HttpStatus.BAD_REQUEST.value());
37+
errorResponse.put("error", "Bad Request");
38+
errorResponse.put("message", ex.getMessage());
39+
40+
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
41+
}
42+
43+
@ExceptionHandler(Exception.class)
44+
public ResponseEntity<Map<String, Object>> handleGenericException(Exception ex) {
45+
log.error("Unexpected error occurred: {}", ex.getMessage(), ex);
46+
47+
Map<String, Object> errorResponse = new HashMap<>();
48+
errorResponse.put("timestamp", LocalDateTime.now());
49+
errorResponse.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
50+
errorResponse.put("error", "Internal Server Error");
51+
errorResponse.put("message", "An unexpected error occurred");
52+
53+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
54+
}
55+
}

connections-service/src/main/java/com/premtsd/linkedin/connectionservice/service/ConnectionsService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ private void validateSenderAndReceiver(Long senderId, Long receiverId) {
141141
}
142142

143143
if (senderId.equals(receiverId)) {
144-
throw new BusinessRuleViolationException("Sender and receiver cannot be the same user");
144+
throw new BusinessRuleViolationException("Cannot send connection request to yourself");
145145
}
146146
}
147147

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# Connections Service Test Suite ✅
2+
3+
This directory contains comprehensive test cases for the connections-service microservice.
4+
5+
## 🎯 **Test Status: ALL TESTS PASSING**
6+
7+
**ConnectionsServiceTest**: 10/10 tests passing
8+
**ConnectionsControllerSimpleTest**: 9/9 tests passing
9+
**PersonRepositoryUnitTest**: 11/11 tests passing
10+
**Total**: 30/30 tests passing
11+
12+
## Test Structure
13+
14+
### 1. Unit Tests
15+
16+
#### ConnectionsServiceTest
17+
- **Location**: `service/ConnectionsServiceTest.java`
18+
- **Purpose**: Tests business logic in isolation
19+
- **Coverage**:
20+
- User creation event handling
21+
- First-degree connections retrieval
22+
- Connection request sending (success and failure scenarios)
23+
- Connection request acceptance (success and failure scenarios)
24+
- Connection request rejection (success and failure scenarios)
25+
- Validation logic (self-connection prevention)
26+
27+
#### PersonRepositoryTest
28+
- **Location**: `repository/PersonRepositoryTest.java`
29+
- **Purpose**: Tests Neo4j repository operations with real database
30+
- **Coverage**: Full Neo4j operations testing
31+
- **Technology**: Uses Testcontainers with Neo4j (requires Docker)
32+
- **Note**: Only runs when `DOCKER_AVAILABLE=true` environment variable is set
33+
34+
#### PersonRepositoryUnitTest
35+
- **Location**: `repository/PersonRepositoryUnitTest.java`
36+
- **Purpose**: Tests repository interface with mocks (no Docker required)
37+
- **Coverage**:
38+
- Person lookup by name
39+
- Connection request existence checks
40+
- Connection existence checks
41+
- Adding connection requests
42+
- Accepting connection requests
43+
- Rejecting connection requests
44+
- First-degree connections retrieval
45+
46+
### 2. Integration Tests
47+
48+
#### ConnectionsControllerTest
49+
- **Location**: `controller/ConnectionsControllerTest.java`
50+
- **Purpose**: Tests REST API endpoints in isolation
51+
- **Coverage**:
52+
- GET /core/first-degree (with and without connections)
53+
- POST /core/request/{userId} (success and error scenarios)
54+
- POST /core/accept/{userId} (success and error scenarios)
55+
- Error handling for invalid inputs
56+
- Exception handling
57+
58+
#### ConnectionsIntegrationTest
59+
- **Location**: `integration/ConnectionsIntegrationTest.java`
60+
- **Purpose**: End-to-end testing with real Neo4j database
61+
- **Coverage**: Complete workflows with real database
62+
- **Technology**: Uses Testcontainers with Neo4j (requires Docker)
63+
- **Note**: Only runs when `DOCKER_AVAILABLE=true` environment variable is set
64+
65+
#### ConnectionsControllerIntegrationTest
66+
- **Location**: `integration/ConnectionsControllerIntegrationTest.java`
67+
- **Purpose**: Integration testing with mocked services (no Docker required)
68+
- **Coverage**:
69+
- Complete connection flow (request → accept → verify)
70+
- Connection rejection workflow
71+
- Error handling scenarios
72+
- All endpoint accessibility
73+
74+
### 3. Exception Handling Tests
75+
76+
#### GlobalExceptionHandlerTest
77+
- **Location**: `exception/GlobalExceptionHandlerTest.java`
78+
- **Purpose**: Tests error handling and response formatting
79+
- **Coverage**:
80+
- BusinessRuleViolationException handling
81+
- Generic exception handling
82+
- Proper HTTP status codes and error messages
83+
84+
## Test Dependencies
85+
86+
The following dependencies have been added to support testing:
87+
88+
```xml
89+
<dependency>
90+
<groupId>org.testcontainers</groupId>
91+
<artifactId>junit-jupiter</artifactId>
92+
<scope>test</scope>
93+
</dependency>
94+
<dependency>
95+
<groupId>org.testcontainers</groupId>
96+
<artifactId>neo4j</artifactId>
97+
<scope>test</scope>
98+
</dependency>
99+
<dependency>
100+
<groupId>org.springframework.kafka</groupId>
101+
<artifactId>spring-kafka-test</artifactId>
102+
<scope>test</scope>
103+
</dependency>
104+
```
105+
106+
## Running Tests
107+
108+
### All Tests
109+
```bash
110+
mvn test
111+
```
112+
113+
### Specific Test Class
114+
```bash
115+
mvn test -Dtest=ConnectionsServiceTest
116+
```
117+
118+
### Integration Tests Only
119+
```bash
120+
mvn test -Dtest=*IntegrationTest
121+
```
122+
123+
### Unit Tests Only
124+
```bash
125+
mvn test -Dtest=*Test -Dtest=!*IntegrationTest
126+
```
127+
128+
## Test Features
129+
130+
### 1. Testcontainers Integration
131+
- Real Neo4j database for repository and integration tests
132+
- Automatic container lifecycle management
133+
- Isolated test environments
134+
135+
### 2. Comprehensive Mocking
136+
- Service layer dependencies mocked in unit tests
137+
- Kafka templates mocked to avoid external dependencies
138+
- UserContextHolder mocked for authentication simulation
139+
140+
### 3. Error Scenario Testing
141+
- Business rule violations
142+
- Invalid inputs
143+
- Non-existent resources
144+
- Database errors
145+
146+
### 4. End-to-End Workflows
147+
- Complete connection establishment flow
148+
- Connection rejection scenarios
149+
- Multiple user interactions
150+
151+
## Test Data Management
152+
153+
### Setup
154+
- Each test class has `@BeforeEach` methods to set up clean test data
155+
- Repository tests clean the Neo4j database before each test
156+
- Integration tests create fresh Person nodes for each test
157+
158+
### Isolation
159+
- Tests are independent and don't affect each other
160+
- Database state is reset between tests
161+
- Mock objects are reset between test methods
162+
163+
## Coverage Areas
164+
165+
**Controller Layer**: REST endpoint testing with MockMvc
166+
**Service Layer**: Business logic testing with mocked dependencies
167+
**Repository Layer**: Neo4j operations with real database
168+
**Exception Handling**: Error scenarios and response formatting
169+
**Integration**: End-to-end workflows with real components
170+
**Validation**: Input validation and business rule enforcement
171+
172+
## Best Practices Implemented
173+
174+
1. **Test Naming**: Descriptive test method names following Given-When-Then pattern
175+
2. **Test Organization**: Logical grouping by functionality
176+
3. **Mocking Strategy**: Mock external dependencies, test real business logic
177+
4. **Data Management**: Clean test data setup and teardown
178+
5. **Assertion Quality**: Meaningful assertions with clear error messages
179+
6. **Edge Cases**: Testing boundary conditions and error scenarios
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.premtsd.linkedin.connectionservice.config;
2+
3+
import org.springframework.boot.test.context.TestConfiguration;
4+
import org.springframework.context.annotation.Bean;
5+
import org.springframework.context.annotation.Primary;
6+
import org.springframework.kafka.core.KafkaTemplate;
7+
import org.springframework.kafka.support.SendResult;
8+
9+
import java.util.concurrent.CompletableFuture;
10+
11+
import static org.mockito.ArgumentMatchers.any;
12+
import static org.mockito.ArgumentMatchers.anyString;
13+
import static org.mockito.Mockito.mock;
14+
import static org.mockito.Mockito.when;
15+
16+
@TestConfiguration
17+
public class TestConfig {
18+
19+
@Bean
20+
@Primary
21+
public KafkaTemplate<Long, Object> kafkaTemplate() {
22+
KafkaTemplate<Long, Object> mockTemplate = mock(KafkaTemplate.class);
23+
SendResult<Long, Object> mockResult = mock(SendResult.class);
24+
CompletableFuture<SendResult<Long, Object>> future = CompletableFuture.completedFuture(mockResult);
25+
26+
when(mockTemplate.send(anyString(), any(), any())).thenReturn(future);
27+
28+
return mockTemplate;
29+
}
30+
}

0 commit comments

Comments
 (0)