Skip to content

Commit ece0319

Browse files
Add integration tests for OSS, Container, and Secrets realtime scanners
1 parent fa9f1b5 commit ece0319

File tree

6 files changed

+755
-166
lines changed

6 files changed

+755
-166
lines changed
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
package com.checkmarx.ast;
2+
3+
import com.checkmarx.ast.containersrealtime.ContainersRealtimeImage;
4+
import com.checkmarx.ast.containersrealtime.ContainersRealtimeResults;
5+
import com.checkmarx.ast.containersrealtime.ContainersRealtimeVulnerability;
6+
import com.checkmarx.ast.wrapper.CxException;
7+
import org.junit.jupiter.api.*;
8+
9+
import java.nio.file.Files;
10+
import java.nio.file.Paths;
11+
import java.util.Optional;
12+
13+
import static org.junit.jupiter.api.Assertions.*;
14+
15+
/**
16+
* Integration and unit tests for Container Realtime scanner functionality.
17+
* Tests the complete workflow: CLI invocation -> JSON parsing -> domain object mapping.
18+
* Integration tests use Dockerfile as the scan target and are assumption-guarded for CI/local flexibility.
19+
*/
20+
class ContainersRealtimeResultsTest extends BaseTest {
21+
22+
private boolean isCliConfigured() {
23+
return Optional.ofNullable(getConfig().getPathToExecutable()).filter(s -> !s.isEmpty()).isPresent();
24+
}
25+
26+
/* ------------------------------------------------------ */
27+
/* Integration tests for Container Realtime scanning */
28+
/* ------------------------------------------------------ */
29+
30+
/**
31+
* Tests basic container realtime scan functionality on Dockerfile.
32+
* Verifies that the scan returns a valid results object with detected container images.
33+
* This test validates the end-to-end workflow from CLI execution to domain object creation.
34+
*/
35+
@Test
36+
@DisplayName("Basic container scan on Dockerfile returns detected images")
37+
void basicContainerRealtimeScan() throws Exception {
38+
Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test");
39+
String dockerfilePath = "src/test/resources/Dockerfile";
40+
Assumptions.assumeTrue(Files.exists(Paths.get(dockerfilePath)), "Dockerfile not found - cannot test container scanning");
41+
42+
ContainersRealtimeResults results = wrapper.containersRealtimeScan(dockerfilePath, "");
43+
44+
assertNotNull(results, "Scan should return non-null results");
45+
assertNotNull(results.getImages(), "Images list should be initialized");
46+
47+
// Verify that if images are detected, they have proper structure
48+
if (!results.getImages().isEmpty()) {
49+
results.getImages().forEach(image -> {
50+
assertNotNull(image.getImageName(), "Image name should be populated");
51+
assertNotNull(image.getVulnerabilities(), "Vulnerabilities list should be initialized");
52+
});
53+
}
54+
}
55+
56+
/**
57+
* Tests container scan with ignore file functionality.
58+
* Verifies that providing an ignore file doesn't break the scanning process
59+
* and produces consistent or reduced results compared to baseline scan.
60+
*/
61+
@Test
62+
@DisplayName("Container scan with ignore file works correctly")
63+
void containerRealtimeScanWithIgnoreFile() throws Exception {
64+
Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test");
65+
String dockerfilePath = "src/test/resources/Dockerfile";
66+
String ignoreFile = "src/test/resources/ignored-packages.json";
67+
Assumptions.assumeTrue(Files.exists(Paths.get(dockerfilePath)) && Files.exists(Paths.get(ignoreFile)),
68+
"Required test resources missing - cannot test ignore functionality");
69+
70+
ContainersRealtimeResults baseline = wrapper.containersRealtimeScan(dockerfilePath, "");
71+
ContainersRealtimeResults filtered = wrapper.containersRealtimeScan(dockerfilePath, ignoreFile);
72+
73+
assertNotNull(baseline, "Baseline scan should return results");
74+
assertNotNull(filtered, "Filtered scan should return results");
75+
76+
// Ignore file should not increase the number of detected issues
77+
if (baseline.getImages() != null && filtered.getImages() != null) {
78+
assertTrue(filtered.getImages().size() <= baseline.getImages().size(),
79+
"Filtered scan should not have more images than baseline");
80+
}
81+
}
82+
83+
/**
84+
* Tests scan consistency by running the same container scan multiple times.
85+
* Verifies that repeated scans of the same Dockerfile produce stable, deterministic results.
86+
* This is important for CI/CD pipelines where consistent results are crucial.
87+
*/
88+
@Test
89+
@DisplayName("Repeated container scans produce consistent results")
90+
void containerRealtimeScanConsistency() throws Exception {
91+
Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test");
92+
String dockerfilePath = "src/test/resources/Dockerfile";
93+
Assumptions.assumeTrue(Files.exists(Paths.get(dockerfilePath)), "Dockerfile not found - cannot test consistency");
94+
95+
ContainersRealtimeResults firstScan = wrapper.containersRealtimeScan(dockerfilePath, "");
96+
ContainersRealtimeResults secondScan = wrapper.containersRealtimeScan(dockerfilePath, "");
97+
98+
assertNotNull(firstScan, "First scan should return results");
99+
assertNotNull(secondScan, "Second scan should return results");
100+
101+
// Compare image counts for consistency
102+
int firstImageCount = (firstScan.getImages() != null) ? firstScan.getImages().size() : 0;
103+
int secondImageCount = (secondScan.getImages() != null) ? secondScan.getImages().size() : 0;
104+
105+
assertEquals(firstImageCount, secondImageCount,
106+
"Image count should be consistent across multiple scans");
107+
}
108+
109+
/**
110+
* Tests domain object mapping for container scan results.
111+
* Verifies that JSON responses are properly parsed into domain objects
112+
* and all expected fields are correctly mapped and initialized.
113+
*/
114+
@Test
115+
@DisplayName("Container domain objects are properly mapped from scan results")
116+
void containerDomainObjectMapping() throws Exception {
117+
Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test");
118+
String dockerfilePath = "src/test/resources/Dockerfile";
119+
Assumptions.assumeTrue(Files.exists(Paths.get(dockerfilePath)), "Dockerfile not found - cannot test mapping");
120+
121+
ContainersRealtimeResults results = wrapper.containersRealtimeScan(dockerfilePath, "");
122+
assertNotNull(results, "Scan results should not be null");
123+
124+
// If images are detected, validate their structure
125+
if (results.getImages() != null && !results.getImages().isEmpty()) {
126+
ContainersRealtimeImage sampleImage = results.getImages().get(0);
127+
128+
// Verify core image fields are mapped correctly
129+
assertNotNull(sampleImage.getImageName(), "Image name should always be present");
130+
assertNotNull(sampleImage.getVulnerabilities(), "Vulnerabilities list should be initialized");
131+
132+
// If vulnerabilities exist, validate their structure
133+
if (!sampleImage.getVulnerabilities().isEmpty()) {
134+
ContainersRealtimeVulnerability sampleVuln = sampleImage.getVulnerabilities().get(0);
135+
// CVE and Severity are the core fields that should be present
136+
assertTrue(sampleVuln.getCve() != null || sampleVuln.getSeverity() != null,
137+
"Vulnerability should have at least CVE or Severity information");
138+
}
139+
}
140+
}
141+
142+
/**
143+
* Tests error handling when scanning a non-existent file.
144+
* Verifies that the scanner properly throws a CxException with meaningful error message
145+
* when provided with invalid file paths, demonstrating proper error handling.
146+
*/
147+
@Test
148+
@DisplayName("Container scan throws appropriate exception for non-existent file")
149+
void containerScanHandlesInvalidPath() {
150+
Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test");
151+
152+
// Test with a non-existent file path
153+
String invalidPath = "src/test/resources/NonExistentDockerfile";
154+
155+
// The CLI should throw a CxException with a meaningful error message for invalid paths
156+
CxException exception = assertThrows(CxException.class, () ->
157+
wrapper.containersRealtimeScan(invalidPath, "")
158+
);
159+
160+
// Verify the exception contains information about the invalid file path
161+
String errorMessage = exception.getMessage();
162+
assertNotNull(errorMessage, "Exception should contain an error message");
163+
assertTrue(errorMessage.contains("invalid file path") || errorMessage.contains("file") || errorMessage.contains("path"),
164+
"Exception message should indicate the issue is related to file path: " + errorMessage);
165+
}
166+
167+
/* ------------------------------------------------------ */
168+
/* Unit tests for JSON parsing robustness */
169+
/* ------------------------------------------------------ */
170+
171+
/**
172+
* Tests JSON parsing with valid container scan response.
173+
* Verifies that well-formed JSON is correctly parsed into domain objects.
174+
*/
175+
@Test
176+
@DisplayName("Valid JSON parsing creates correct domain objects")
177+
void testFromLineWithValidJson() {
178+
String json = "{" +
179+
"\"Images\": [" +
180+
" {" +
181+
" \"ImageName\": \"nginx:latest\"," +
182+
" \"Vulnerabilities\": [" +
183+
" {" +
184+
" \"CVE\": \"CVE-2021-2345\"," +
185+
" \"Severity\": \"High\"" +
186+
" }" +
187+
" ]" +
188+
" }" +
189+
"]" +
190+
"}";
191+
ContainersRealtimeResults results = ContainersRealtimeResults.fromLine(json);
192+
assertNotNull(results);
193+
assertEquals(1, results.getImages().size());
194+
ContainersRealtimeImage image = results.getImages().get(0);
195+
assertEquals("nginx:latest", image.getImageName());
196+
assertEquals(1, image.getVulnerabilities().size());
197+
ContainersRealtimeVulnerability vulnerability = image.getVulnerabilities().get(0);
198+
assertEquals("CVE-2021-2345", vulnerability.getCve());
199+
assertEquals("High", vulnerability.getSeverity());
200+
}
201+
202+
/**
203+
* Tests parsing robustness with malformed JSON.
204+
* Verifies that the parser gracefully handles various edge cases.
205+
*/
206+
@Test
207+
@DisplayName("Malformed JSON is handled gracefully")
208+
void testFromLineWithEdgeCases() {
209+
// Missing Images key
210+
assertNull(ContainersRealtimeResults.fromLine("{\"some_other_key\": \"some_value\"}"));
211+
212+
// Invalid JSON structure
213+
assertNull(ContainersRealtimeResults.fromLine("{\"Images\": [}"));
214+
215+
// Blank/null inputs
216+
assertNull(ContainersRealtimeResults.fromLine(""));
217+
assertNull(ContainersRealtimeResults.fromLine(" "));
218+
assertNull(ContainersRealtimeResults.fromLine(null));
219+
}
220+
221+
/**
222+
* Tests parsing with empty or null image arrays.
223+
* Verifies that empty results are handled correctly.
224+
*/
225+
@Test
226+
@DisplayName("Empty and null image arrays are handled correctly")
227+
void testFromLineWithEmptyResults() {
228+
// Empty images array
229+
String emptyJson = "{\"Images\": []}";
230+
ContainersRealtimeResults emptyResults = ContainersRealtimeResults.fromLine(emptyJson);
231+
assertNotNull(emptyResults);
232+
assertTrue(emptyResults.getImages().isEmpty());
233+
234+
// Null images
235+
String nullJson = "{\"Images\": null}";
236+
ContainersRealtimeResults nullResults = ContainersRealtimeResults.fromLine(nullJson);
237+
assertNotNull(nullResults);
238+
assertNull(nullResults.getImages());
239+
}
240+
}
241+
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.checkmarx.ast;
2+
3+
import com.checkmarx.ast.iacrealtime.IacRealtimeResults;
4+
import org.junit.jupiter.api.Test;
5+
import static org.junit.jupiter.api.Assertions.*;
6+
7+
class IacRealtimeResultsTest {
8+
9+
@Test
10+
void testFromLineWithValidJsonArray() {
11+
String json = "[" +
12+
" {" +
13+
" \"Title\": \"My Issue\"," +
14+
" \"Severity\": \"High\"" +
15+
" }" +
16+
"]";
17+
IacRealtimeResults results = IacRealtimeResults.fromLine(json);
18+
assertNotNull(results);
19+
assertEquals(1, results.getResults().size());
20+
IacRealtimeResults.Issue issue = results.getResults().get(0);
21+
assertEquals("My Issue", issue.getTitle());
22+
assertEquals("High", issue.getSeverity());
23+
}
24+
25+
@Test
26+
void testFromLineWithValidJsonObject() {
27+
String json = "{" +
28+
" \"Title\": \"My Single Issue\"," +
29+
" \"Severity\": \"Medium\"" +
30+
"}";
31+
IacRealtimeResults results = IacRealtimeResults.fromLine(json);
32+
assertNotNull(results);
33+
assertEquals(1, results.getResults().size());
34+
IacRealtimeResults.Issue issue = results.getResults().get(0);
35+
assertEquals("My Single Issue", issue.getTitle());
36+
assertEquals("Medium", issue.getSeverity());
37+
}
38+
39+
@Test
40+
void testFromLineWithEmptyJsonArray() {
41+
String json = "[]";
42+
IacRealtimeResults results = IacRealtimeResults.fromLine(json);
43+
assertNotNull(results);
44+
assertTrue(results.getResults().isEmpty());
45+
}
46+
47+
@Test
48+
void testFromLineWithBlankLine() {
49+
assertNull(IacRealtimeResults.fromLine(""));
50+
assertNull(IacRealtimeResults.fromLine(" "));
51+
assertNull(IacRealtimeResults.fromLine(null));
52+
}
53+
54+
@Test
55+
void testFromLineWithInvalidJson() {
56+
String json = "[{]";
57+
assertNull(IacRealtimeResults.fromLine(json));
58+
}
59+
}
60+

0 commit comments

Comments
 (0)