Skip to content

Commit 8bbea61

Browse files
authored
Merge pull request #107 from ptomanRH/add-codecov-coverage
feat: add Codecov integration with JaCoCo coverage
2 parents 70b1db3 + 3bcb214 commit 8bbea61

8 files changed

Lines changed: 233 additions & 6 deletions

File tree

.github/workflows/ci.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ jobs:
1818
tests:
1919
runs-on: ubuntu-latest
2020
name: Tests
21+
permissions:
22+
id-token: write
2123
strategy:
2224
matrix:
2325
java: [17, 21]
@@ -31,6 +33,15 @@ jobs:
3133
distribution: 'temurin'
3234
- name: Build with Maven
3335
run: mvn -B verify --file pom.xml
36+
- name: Upload coverage to Codecov
37+
if: matrix.java == 21
38+
uses: codecov/codecov-action@v5
39+
with:
40+
use_oidc: true
41+
flags: unit-tests
42+
files: target/site/jacoco/jacoco.xml
43+
slug: guacsec/trustify-da-api-spec
44+
fail_ci_if_error: false
3445

3546
openapi-lint-checks:
3647
runs-on: ubuntu-latest

codecov.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
coverage:
2+
status:
3+
project:
4+
default:
5+
target: auto
6+
threshold: 1%
7+
informational: true
8+
patch:
9+
default:
10+
target: auto
11+
threshold: 1%
12+
informational: true
13+
14+
# JaCoCo (pom.xml) already omits generated v5 models from jacoco.xml; this backs up
15+
# Codecov path resolution when reports reference generated source paths.
16+
ignore:
17+
- "target/generated-sources/**"
18+
19+
flags:
20+
unit-tests:
21+
carryforward: true
22+
23+
comment:
24+
layout: "reach,diff,flags,files"
25+
behavior: default

pom.xml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
<maven-resources-plugin.version>3.3.1</maven-resources-plugin.version>
3838
<maven-source-plugin.version>3.2.1</maven-source-plugin.version>
3939
<maven-surefire-plugin.version>3.0.0</maven-surefire-plugin.version>
40+
<jacoco-maven-plugin.version>0.8.14</jacoco-maven-plugin.version>
4041
<build-helper-maven-plugin.version>3.4.0</build-helper-maven-plugin.version>
4142
<extra-enforcer-rules.version>1.6.2</extra-enforcer-rules.version>
4243
<flatten-maven-plugin.version>1.4.1</flatten-maven-plugin.version>
@@ -246,6 +247,11 @@
246247
<artifactId>maven-surefire-plugin</artifactId>
247248
<version>${maven-surefire-plugin.version}</version>
248249
</plugin>
250+
<plugin>
251+
<groupId>org.jacoco</groupId>
252+
<artifactId>jacoco-maven-plugin</artifactId>
253+
<version>${jacoco-maven-plugin.version}</version>
254+
</plugin>
249255
<!-- Third-Party Plugins -->
250256
<plugin>
251257
<groupId>com.mycila</groupId>
@@ -593,6 +599,33 @@ limitations under the License.]]>
593599
</execution>
594600
</executions>
595601
</plugin>
602+
<plugin>
603+
<groupId>org.jacoco</groupId>
604+
<artifactId>jacoco-maven-plugin</artifactId>
605+
<configuration>
606+
<!-- OpenAPI models under api.v5 are generated; only hand-written sources are measured. -->
607+
<includes>
608+
<include>io/github/guacsec/trustifyda/api/PackageRef*</include>
609+
<include>io/github/guacsec/trustifyda/api/serialization/**</include>
610+
<include>io/github/guacsec/trustifyda/api/v5/SeverityUtils*</include>
611+
</includes>
612+
</configuration>
613+
<executions>
614+
<execution>
615+
<id>prepare-agent</id>
616+
<goals>
617+
<goal>prepare-agent</goal>
618+
</goals>
619+
</execution>
620+
<execution>
621+
<id>report</id>
622+
<phase>verify</phase>
623+
<goals>
624+
<goal>report</goal>
625+
</goals>
626+
</execution>
627+
</executions>
628+
</plugin>
596629
</plugins>
597630
</build>
598631
<profiles>

src/main/java/io/github/guacsec/trustifyda/api/PackageRef.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,9 @@ public String version() {
8181
}
8282

8383
public boolean isCoordinatesEquals(PackageRef other) {
84-
if(other == null) {
84+
if (other == null) {
8585
return false;
8686
}
87-
if(other.purl == null) {
88-
return this.purl == null;
89-
}
9087
return this.purl.isCoordinatesEquals(other.purl);
9188
}
9289

src/test/java/io/github/guacsec/trustifyda/api/PackageRefTest.java

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@
1818
package io.github.guacsec.trustifyda.api;
1919

2020
import static org.junit.jupiter.api.Assertions.assertEquals;
21+
import static org.junit.jupiter.api.Assertions.assertFalse;
22+
import static org.junit.jupiter.api.Assertions.assertNotEquals;
2123
import static org.junit.jupiter.api.Assertions.assertNull;
24+
import static org.junit.jupiter.api.Assertions.assertThrows;
2225
import static org.junit.jupiter.api.Assertions.assertTrue;
2326
import org.junit.jupiter.api.Test;
2427

25-
import io.github.guacsec.trustifyda.api.PackageRef;
26-
2728
public class PackageRefTest {
2829

2930
@Test
@@ -65,5 +66,91 @@ public void testCoordinatesEquals() {
6566
assertEquals(originalPurl, ref.toString());
6667

6768
assertTrue(ref.isCoordinatesEquals(new PackageRef(coordinates)));
69+
assertFalse(ref.isCoordinatesEquals(null));
70+
}
71+
72+
@Test
73+
void builderBuildsMavenPurlFromCoordinates() {
74+
var ref = PackageRef.builder()
75+
.pkgManager("maven")
76+
.namespace("org.example")
77+
.name("artifact")
78+
.version("1.0.0")
79+
.build();
80+
assertEquals("pkg:maven/org.example/artifact@1.0.0", ref.toString());
81+
assertEquals("org.example:artifact", ref.name());
82+
assertEquals("1.0.0", ref.version());
83+
}
84+
85+
@Test
86+
void builderAcceptsRawPurl() {
87+
var purl = "pkg:npm/foo@1.2.3";
88+
var ref = PackageRef.builder().purl(purl).build();
89+
assertEquals(purl, ref.ref());
90+
}
91+
92+
@Test
93+
void builderRejectsInvalidPurl() {
94+
assertThrows(IllegalArgumentException.class,
95+
() -> PackageRef.builder().purl("not-a-purl").build());
96+
}
97+
98+
@Test
99+
void builderRequiresCoordinatesWhenPurlIsAbsent() {
100+
assertThrows(NullPointerException.class,
101+
() -> PackageRef.builder().pkgManager("maven").name("x").build());
102+
}
103+
104+
@Test
105+
void stringConstructorRejectsInvalidPurl() {
106+
assertThrows(IllegalArgumentException.class, () -> new PackageRef("not-a-purl"));
107+
}
108+
109+
@Test
110+
void parseBuildsRefFromFourPartGav() {
111+
var ref = PackageRef.parse("com.example:my-artifact:jar:1.2.3", "maven");
112+
assertEquals("pkg:maven/com.example/my-artifact@1.2.3", ref.toString());
113+
assertEquals("com.example:my-artifact", ref.name());
114+
}
115+
116+
@Test
117+
void parseBuildsRefFromSixPartGav() {
118+
var ref = PackageRef.parse("com.example:my-artifact:jar:extra:2.0.0:notes", "maven");
119+
assertEquals("pkg:maven/com.example/my-artifact@2.0.0", ref.toString());
120+
assertEquals("2.0.0", ref.version());
121+
}
122+
123+
@Test
124+
void parseRejectsUnexpectedGavFormat() {
125+
assertThrows(IllegalArgumentException.class,
126+
() -> PackageRef.parse("it:has:more:than:six:parts:too:many", "maven"));
127+
assertThrows(IllegalArgumentException.class,
128+
() -> PackageRef.parse("only:three:parts", "maven"));
129+
}
130+
131+
@Test
132+
void toGavAndUrlQueryString() {
133+
var ref = new PackageRef("pkg:maven/org.example/artifact@1.0.0");
134+
assertEquals("org.example:artifact:1.0.0", ref.toGav());
135+
assertEquals("purl=pkg:maven/org.example/artifact@1.0.0", ref.toUrlQueryString("purl"));
136+
assertEquals("=pkg:maven/org.example/artifact@1.0.0", ref.toUrlQueryString(null));
137+
}
138+
139+
@Test
140+
void equalsAndHashCodeUseFullPurl() {
141+
var a = new PackageRef("pkg:maven/org.example/artifact@1.0.0");
142+
var b = new PackageRef("pkg:maven/org.example/artifact@1.0.0");
143+
var c = new PackageRef("pkg:maven/org.example/artifact@2.0.0");
144+
assertEquals(a, b);
145+
assertEquals(a.hashCode(), b.hashCode());
146+
assertNotEquals(a, c);
147+
assertFalse(a.equals(null));
148+
assertFalse(a.equals(new Object()));
149+
}
150+
151+
@Test
152+
void nonMavenNameJoinsNamespaceWithSlash() {
153+
var ref = new PackageRef("pkg:golang/example.com/lib@v1.0.0");
154+
assertEquals("example.com/lib", ref.name());
68155
}
69156
}

src/test/java/io/github/guacsec/trustifyda/api/serialization/PackageURLDeserializerTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,10 @@ void deserializeInvalidStringThrows() {
5252
assertThrows(JsonProcessingException.class,
5353
() -> mapper.readValue("\"not-a-valid-purl\"", PackageRef.class));
5454
}
55+
56+
@Test
57+
void deserializeRejectsNonStringJson() {
58+
assertThrows(JsonProcessingException.class,
59+
() -> mapper.readValue("{\"type\":\"maven\"}", PackageRef.class));
60+
}
5561
}

src/test/java/io/github/guacsec/trustifyda/api/serialization/PackageURLSerializerTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@
1818
package io.github.guacsec.trustifyda.api.serialization;
1919

2020
import static org.junit.jupiter.api.Assertions.assertEquals;
21+
2122
import org.junit.jupiter.api.Test;
2223

2324
import com.fasterxml.jackson.core.JsonProcessingException;
2425
import com.fasterxml.jackson.databind.ObjectMapper;
26+
import com.fasterxml.jackson.databind.module.SimpleModule;
27+
import com.github.packageurl.PackageURL;
2528

2629
import io.github.guacsec.trustifyda.api.PackageRef;
2730

@@ -43,4 +46,14 @@ void testSerialize() throws JsonProcessingException {
4346
assertEquals("i386", pkgRef.purl().getQualifiers().get("arch"));
4447
assertEquals("\"" + rpmPkg + "\"", mapper.writeValueAsString(pkgRef));
4548
}
49+
50+
@Test
51+
void serializesPackageUrlDirectly() throws Exception {
52+
var mapperWithSerializer = new ObjectMapper()
53+
.registerModule(new SimpleModule().addSerializer(
54+
PackageURL.class, new PackageURLSerializer()));
55+
var purl = new PackageURL("pkg:maven/org.example/artifact@1.0.0");
56+
assertEquals("\"pkg:maven/org.example/artifact@1.0.0\"",
57+
mapperWithSerializer.writeValueAsString(purl));
58+
}
4659
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2023-2025 Trustify Dependency Analytics Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
*
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package io.github.guacsec.trustifyda.api.v5;
19+
20+
import static org.junit.jupiter.api.Assertions.assertEquals;
21+
import static org.junit.jupiter.api.Assertions.assertNull;
22+
import static org.junit.jupiter.api.Assertions.assertThrows;
23+
24+
import org.junit.jupiter.api.Test;
25+
26+
class SeverityUtilsTest {
27+
28+
@Test
29+
void fromValueReturnsNullForNullInput() {
30+
assertNull(SeverityUtils.fromValue(null));
31+
}
32+
33+
@Test
34+
void fromValueIsCaseInsensitive() {
35+
assertEquals(Severity.HIGH, SeverityUtils.fromValue("high"));
36+
assertEquals(Severity.CRITICAL, SeverityUtils.fromValue("CRITICAL"));
37+
}
38+
39+
@Test
40+
void fromValueRejectsUnknownSeverity() {
41+
assertThrows(IllegalArgumentException.class, () -> SeverityUtils.fromValue("unknown"));
42+
}
43+
44+
@Test
45+
void fromScoreMapsCvssRanges() {
46+
assertEquals(Severity.LOW, SeverityUtils.fromScore(0));
47+
assertEquals(Severity.LOW, SeverityUtils.fromScore(3.9f));
48+
assertEquals(Severity.MEDIUM, SeverityUtils.fromScore(4));
49+
assertEquals(Severity.MEDIUM, SeverityUtils.fromScore(6.9f));
50+
assertEquals(Severity.HIGH, SeverityUtils.fromScore(7));
51+
assertEquals(Severity.HIGH, SeverityUtils.fromScore(8.9f));
52+
assertEquals(Severity.CRITICAL, SeverityUtils.fromScore(9));
53+
assertEquals(Severity.CRITICAL, SeverityUtils.fromScore(10));
54+
}
55+
}

0 commit comments

Comments
 (0)