Skip to content

Commit

Permalink
Merge pull request #924 from DependencyTrack/port-regression-test-for…
Browse files Browse the repository at this point in the history
…-parent-fieldin-/api/v1/project/uuid

Port test cases
  • Loading branch information
nscuro authored Sep 24, 2024
2 parents c87faef + 5945a6d commit 4d1d4ae
Show file tree
Hide file tree
Showing 2 changed files with 273 additions and 17 deletions.
246 changes: 229 additions & 17 deletions src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@
import alpine.model.IConfigProperty.PropertyType;
import alpine.server.filters.ApiFilter;
import alpine.server.filters.AuthenticationFilter;
import jakarta.json.Json;
import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import jakarta.json.JsonObjectBuilder;
import jakarta.ws.rs.HttpMethod;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.datanucleus.store.types.wrappers.Date;
import org.dependencytrack.JerseyTestRule;
import org.dependencytrack.ResourceTest;
Expand Down Expand Up @@ -60,18 +68,13 @@
import org.junit.ClassRule;
import org.junit.Test;

import jakarta.json.Json;
import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import jakarta.ws.rs.HttpMethod;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
Expand Down Expand Up @@ -1261,19 +1264,63 @@ public void getProjectChildrenConciseWithLatestMetricsTest() {

@Test
public void getProjectByUuidTest() {
Project project = qm.createProject("ABC", null, "1.0", null, null, null, true, false);
final var parentProject = new Project();
parentProject.setName("acme-app-parent");
parentProject.setVersion("1.0.0");
qm.persist(parentProject);

final var project = new Project();
project.setName("acme-app");
project.setVersion("1.0.0");
project.setParent(parentProject);
qm.persist(project);

final var childProject = new Project();
childProject.setName("acme-app-child");
childProject.setVersion("1.0.0");
childProject.setParent(project);
qm.persist(childProject);

Response response = jersey.target(V1_PROJECT + "/" + project.getUuid())
.request()
.header(X_API_KEY, apiKey)
.get(Response.class);
Assert.assertEquals(200, response.getStatus(), 0);
Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER));
JsonObject json = parseJsonObject(response);
Assert.assertNotNull(json);
Assert.assertEquals("ABC", json.getString("name"));
Assert.assertEquals(1, json.getJsonArray("versions").size());
Assert.assertEquals(project.getUuid().toString(), json.getJsonArray("versions").getJsonObject(0).getJsonString("uuid").getString());
Assert.assertEquals("1.0", json.getJsonArray("versions").getJsonObject(0).getJsonString("version").getString());
.get();
assertThat(response.getStatus()).isEqualTo(200);
assertThat(response.getHeaderString(TOTAL_COUNT_HEADER)).isNull();
assertThatJson(getPlainTextBody(response))
.withMatcher("projectUuid", equalTo(project.getUuid().toString()))
.withMatcher("parentUuid", equalTo(parentProject.getUuid().toString()))
.withMatcher("childUuid", equalTo(childProject.getUuid().toString()))
.isEqualTo("""
{
"name": "acme-app",
"version": "1.0.0",
"uuid": "${json-unit.matches:projectUuid}",
"parent": {
"name": "acme-app-parent",
"version": "1.0.0",
"uuid": "${json-unit.matches:parentUuid}"
},
"children": [
{
"name": "acme-app-child",
"version": "1.0.0",
"uuid": "${json-unit.matches:childUuid}",
"active": true
}
],
"properties": [],
"tags": [],
"active": true,
"versions": [
{
"uuid": "${json-unit.matches:projectUuid}",
"version": "1.0.0",
"active":true
}
]
}
""");
}

@Test
Expand Down Expand Up @@ -2182,4 +2229,169 @@ public void validateProjectVersionsActiveInactiveTest() {
Assert.assertEquals("3.0", json.getJsonArray("versions").getJsonObject(2).getJsonString("version").getString());
Assert.assertTrue(json.getJsonArray("versions").getJsonObject(2).getBoolean("active"));
}

@Test // https://github.com/DependencyTrack/dependency-track/issues/4048
public void issue4048RegressionTest() {
final int projectsPerLevel = 10;
final int maxDepth = 5;

final Map<Integer, List<UUID>> projectUuidsByLevel = new HashMap<>();

// Create multiple parent-child hierarchies of projects.
for (int i = 0; i < maxDepth; i++) {
final List<UUID> parentUuids = projectUuidsByLevel.get(i - 1);

for (int j = 0; j < projectsPerLevel; j++) {
final UUID parentUuid = i > 0 ? parentUuids.get(j) : null;

final JsonObjectBuilder requestBodyBuilder = Json.createObjectBuilder()
.add("name", "project-%d-%d".formatted(i, j))
.add("version", "%d.%d".formatted(i, j));
if (parentUuid != null) {
requestBodyBuilder.add("parent", Json.createObjectBuilder()
.add("uuid", parentUuid.toString()));
}

final Response response = jersey.target(V1_PROJECT)
.request()
.header(X_API_KEY, apiKey)
.put(Entity.json(requestBodyBuilder.build().toString()));
assertThat(response.getStatus()).isEqualTo(201);
final JsonObject jsonResponse = parseJsonObject(response);

projectUuidsByLevel.compute(i, (ignored, uuids) -> {
final UUID uuid = UUID.fromString(jsonResponse.getString("uuid"));
if (uuids == null) {
return new ArrayList<>(List.of(uuid));
}

uuids.add(uuid);
return uuids;
});
}
}

// Pick out the UUIDs of projects that should have a parent (i.e. level 1 or above).
final List<UUID> childUuids = projectUuidsByLevel.entrySet().stream()
.filter(entry -> entry.getKey() > 0)
.map(Map.Entry::getValue)
.flatMap(List::stream)
.toList();

// Create a [uuid -> level] mapping for better assertion failure reporting.
final Map<UUID, Integer> levelByChildUuid = projectUuidsByLevel.entrySet().stream()
.filter(entry -> entry.getKey() > 0)
.flatMap(entry -> {
final Integer level = entry.getKey();
return entry.getValue().stream().map(uuid -> Map.entry(uuid, level));
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

// Request all child projects individually.
// Ensure that the parent field is populated for all of them.
for (final UUID uuid : childUuids) {
final Response response = jersey.target(V1_PROJECT + "/" + uuid)
.request()
.header(X_API_KEY, apiKey)
.get();
assertThat(response.getStatus()).isEqualTo(200);
final JsonObject json = parseJsonObject(response);
assertThat(json.getJsonObject("parent"))
.withFailMessage("Parent missing on level: " + levelByChildUuid.get(uuid))
.isNotEmpty();
}
}

@Test // https://github.com/DependencyTrack/dependency-track/issues/3883
public void issue3883RegressionTest() {
Response response = jersey.target(V1_PROJECT)
.request()
.header(X_API_KEY, apiKey)
.put(Entity.json("""
{
"name": "acme-app-parent",
"version": "1.0.0"
}
"""));
assertThat(response.getStatus()).isEqualTo(201);
final String parentProjectUuid = parseJsonObject(response).getString("uuid");

response = jersey.target(V1_PROJECT)
.request()
.header(X_API_KEY, apiKey)
.put(Entity.json("""
{
"name": "acme-app",
"version": "1.0.0",
"parent": {
"uuid": "%s"
}
}
""".formatted(parentProjectUuid)));
assertThat(response.getStatus()).isEqualTo(201);
final String childProjectUuid = parseJsonObject(response).getString("uuid");

response = jersey.target(V1_PROJECT + "/" + parentProjectUuid)
.request()
.header(X_API_KEY, apiKey)
.get();
assertThat(response.getStatus()).isEqualTo(200);
assertThatJson(getPlainTextBody(response)).isEqualTo("""
{
"name": "acme-app-parent",
"version": "1.0.0",
"classifier": "APPLICATION",
"uuid": "${json-unit.any-string}",
"children": [
{
"name": "acme-app",
"version": "1.0.0",
"classifier": "APPLICATION",
"uuid": "${json-unit.any-string}",
"active": true
}
],
"properties": [],
"tags": [],
"active": true,
"versions": [
{
"uuid": "${json-unit.any-string}",
"version": "1.0.0",
"active": true
}
]
}
""");

response = jersey.target(V1_PROJECT + "/" + childProjectUuid)
.request()
.header(X_API_KEY, apiKey)
.get();
assertThat(response.getStatus()).isEqualTo(200);
assertThatJson(getPlainTextBody(response)).isEqualTo("""
{
"name": "acme-app",
"version": "1.0.0",
"classifier": "APPLICATION",
"uuid": "${json-unit.any-string}",
"parent": {
"name": "acme-app-parent",
"version": "1.0.0",
"uuid": "${json-unit.any-string}"
},
"children": [],
"properties": [],
"tags": [],
"active": true,
"versions": [
{
"uuid": "${json-unit.any-string}",
"version": "1.0.0",
"active": true
}
]
}
""");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,50 @@ public void informIssue2859Test() {
});
}

@Test
public void informWithLicenseResolutionByIdOrNameTest() throws Exception {
final var license = new License();
license.setLicenseId("MIT");
license.setName("MIT License");
qm.persist(license);

final var project = new Project();
project.setName("acme-license-app");
qm.persist(project);

final byte[] bomBytes = """
{
"bomFormat": "CycloneDX",
"specVersion": "1.4",
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b80",
"version": 1,
"components": [
{
"type": "library",
"name": "acme-lib-x",
"licenses": [
{
"license": {
"name": "MIT"
}
}
]
}
]
}
""".getBytes(StandardCharsets.UTF_8);

final var bomUploadEvent = new BomUploadEvent(qm.detach(Project.class, project.getId()), createTempBomFile(bomBytes));
qm.createWorkflowSteps(bomUploadEvent.getChainIdentifier());
new BomUploadProcessingTask().inform(bomUploadEvent);
assertBomProcessedNotification();

assertThat(qm.getAllComponents(project)).satisfiesExactly(component -> {
assertThat(component.getResolvedLicense()).isNotNull();
assertThat(component.getResolvedLicense().getLicenseId()).isEqualTo("MIT");
});
}

@Test // https://github.com/DependencyTrack/dependency-track/issues/1905
public void informIssue1905Test() throws Exception {
final var project = qm.createProject("Acme Example", null, "1.0", null, null, null, true, false);
Expand Down

0 comments on commit 4d1d4ae

Please sign in to comment.