Skip to content

Commit d570ffb

Browse files
authored
tests(Java): run test against actual schema files (#592)
This change modifies the Java Unit tests to use the schemas in this repository to validate the examples, instead of those bundled in the `cyclonedx-java-core` artifact. Closes #256
2 parents aadc597 + dc4a2ab commit d570ffb

File tree

4 files changed

+249
-103
lines changed

4 files changed

+249
-103
lines changed

tools/pom.xml

+34-9
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@
5454
<lib.commons.io.version>2.17.0</lib.commons.io.version>
5555
<lib.commons.lang3.version>3.17.0</lib.commons.lang3.version>
5656
<lib.commons.text.version>1.12.0</lib.commons.text.version>
57+
<lib.json.schema.validator>1.5.5</lib.json.schema.validator>
5758
<lib.unirest.version>1.4.9</lib.unirest.version>
58-
<lib.cyclonedx.core.java.version>10.0.0</lib.cyclonedx.core.java.version>
59+
<lib.slf4j.api>2.0.16</lib.slf4j.api>
5960
</properties>
6061

6162
<scm>
@@ -98,6 +99,24 @@
9899
</repository>
99100
</repositories>
100101

102+
<dependencyManagement>
103+
<dependencies>
104+
105+
<dependency>
106+
<groupId>org.slf4j</groupId>
107+
<artifactId>slf4j-api</artifactId>
108+
<version>${lib.slf4j.api}</version>
109+
</dependency>
110+
111+
<dependency>
112+
<groupId>org.slf4j</groupId>
113+
<artifactId>slf4j-simple</artifactId>
114+
<version>${lib.slf4j.api}</version>
115+
</dependency>
116+
117+
</dependencies>
118+
</dependencyManagement>
119+
101120
<dependencies>
102121
<!-- Apache Commons -->
103122
<dependency>
@@ -123,16 +142,22 @@
123142
<scope>compile</scope>
124143
</dependency>
125144
<!-- Unit tests -->
145+
<dependency>
146+
<groupId>com.networknt</groupId>
147+
<artifactId>json-schema-validator</artifactId>
148+
<version>${lib.json.schema.validator}</version>
149+
<scope>test</scope>
150+
</dependency>
126151
<dependency>
127152
<groupId>org.junit.jupiter</groupId>
128-
<artifactId>junit-jupiter-engine</artifactId>
129-
<version>5.7.0</version>
153+
<artifactId>junit-jupiter-api</artifactId>
154+
<version>5.11.4</version>
130155
<scope>test</scope>
131156
</dependency>
157+
<!-- Runtime-only test dependency -->
132158
<dependency>
133-
<groupId>org.cyclonedx</groupId>
134-
<artifactId>cyclonedx-core-java</artifactId>
135-
<version>${lib.cyclonedx.core.java.version}</version>
159+
<groupId>org.slf4j</groupId>
160+
<artifactId>slf4j-simple</artifactId>
136161
<scope>test</scope>
137162
</dependency>
138163
</dependencies>
@@ -142,15 +167,15 @@
142167
<plugin>
143168
<groupId>org.apache.maven.plugins</groupId>
144169
<artifactId>maven-surefire-plugin</artifactId>
145-
<version>3.5.1</version>
170+
<version>3.5.2</version>
146171
</plugin>
147172
</plugins>
148173
<testResources>
149174
<testResource>
150-
<directory>${basedir}/../schema</directory>
175+
<directory>${project.basedir}/../schema</directory>
151176
</testResource>
152177
<testResource>
153-
<directory>src/test/resources/</directory>
178+
<directory>src/test/resources</directory>
154179
</testResource>
155180
</testResources>
156181
</build>

tools/src/test/java/org/cyclonedx/schema/BaseSchemaVerificationTest.java

+6-9
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,14 @@ List<String> getAllResources() throws Exception {
3333
return files;
3434
}
3535

36-
List<String> getResources(final String resourceDirectory) throws Exception {
37-
final List<String> files = new ArrayList<>();
38-
String dir = resourceDirectory;
39-
if (!resourceDirectory.endsWith("/")) {
40-
dir += "/";
41-
}
42-
try (InputStream in = this.getClass().getClassLoader().getResourceAsStream(dir)) {
36+
private List<String> getResources(final String resourceDirectory) throws Exception {
37+
final List<String> resources = new ArrayList<>();
38+
try (InputStream in = this.getClass().getClassLoader().getResourceAsStream(resourceDirectory)) {
4339
if (in != null) {
44-
files.addAll(IOUtils.readLines(in, StandardCharsets.UTF_8));
40+
IOUtils.readLines(in, StandardCharsets.UTF_8)
41+
.forEach(resource -> resources.add(resourceDirectory + resource));
4542
}
4643
}
47-
return files;
44+
return resources;
4845
}
4946
}

tools/src/test/java/org/cyclonedx/schema/JsonSchemaVerificationTest.java

+103-44
Original file line numberDiff line numberDiff line change
@@ -13,68 +13,127 @@
1313
*/
1414
package org.cyclonedx.schema;
1515

16-
import java.io.File;
16+
import static org.junit.jupiter.api.Assertions.assertFalse;
17+
import static org.junit.jupiter.api.Assertions.assertTrue;
18+
19+
import com.fasterxml.jackson.core.JsonParser;
20+
import com.fasterxml.jackson.databind.JsonNode;
21+
import com.fasterxml.jackson.databind.ObjectMapper;
22+
import com.fasterxml.jackson.databind.json.JsonMapper;
23+
import com.networknt.schema.DefaultJsonMetaSchemaFactory;
24+
import com.networknt.schema.DisallowUnknownKeywordFactory;
25+
import com.networknt.schema.JsonMetaSchema;
26+
import com.networknt.schema.JsonMetaSchemaFactory;
27+
import com.networknt.schema.JsonSchema;
28+
import com.networknt.schema.JsonSchemaFactory;
29+
import com.networknt.schema.NonValidationKeyword;
30+
import com.networknt.schema.SchemaId;
31+
import com.networknt.schema.SchemaLocation;
32+
import com.networknt.schema.SchemaValidatorsConfig;
33+
import com.networknt.schema.resource.ClasspathSchemaLoader;
34+
import com.networknt.schema.resource.DisallowSchemaLoader;
35+
import java.io.IOException;
36+
import java.io.InputStream;
1737
import java.util.ArrayList;
1838
import java.util.Collection;
1939
import java.util.List;
20-
21-
import org.cyclonedx.parsers.JsonParser;
22-
import org.cyclonedx.Version;
40+
import org.apache.commons.lang3.StringUtils;
2341
import org.junit.jupiter.api.DynamicTest;
2442
import org.junit.jupiter.api.TestFactory;
2543

26-
import static org.junit.jupiter.api.Assertions.assertTrue;
27-
import static org.junit.jupiter.api.Assertions.assertFalse;
44+
class JsonSchemaVerificationTest extends BaseSchemaVerificationTest {
45+
46+
private static final ObjectMapper MAPPER = new JsonMapper();
47+
48+
private static final String JSF_NAMESPACE = "http://cyclonedx.org/schema/jsf-0.82.schema.json";
49+
private static final String SPDX_NAMESPACE = "http://cyclonedx.org/schema/spdx.schema.json";
50+
51+
private static final JsonSchema VERSION_12;
52+
private static final JsonSchema VERSION_13;
53+
private static final JsonSchema VERSION_14;
54+
private static final JsonSchema VERSION_15;
55+
private static final JsonSchema VERSION_16;
56+
57+
static {
58+
JsonMetaSchemaFactory metaSchemaFactory = new DefaultJsonMetaSchemaFactory() {
59+
@Override
60+
public JsonMetaSchema getMetaSchema(
61+
String iri, JsonSchemaFactory schemaFactory, SchemaValidatorsConfig config) {
62+
return addCustomKeywords(super.getMetaSchema(iri, schemaFactory, config));
63+
}
64+
};
65+
JsonSchemaFactory factory = JsonSchemaFactory.builder()
66+
.defaultMetaSchemaIri(SchemaId.V7)
67+
.metaSchema(addCustomKeywords(JsonMetaSchema.getV7()))
68+
.metaSchemaFactory(metaSchemaFactory)
69+
.schemaLoaders(b -> b.add(new ClasspathSchemaLoader()).add(DisallowSchemaLoader.getInstance()))
70+
.schemaMappers(b -> b.mapPrefix(SPDX_NAMESPACE, "classpath:spdx.schema.json")
71+
.mapPrefix(JSF_NAMESPACE, "classpath:jsf-0.82.schema.json"))
72+
.build();
73+
VERSION_12 = factory.getSchema(SchemaLocation.of("classpath:bom-1.2-strict.schema.json"));
74+
VERSION_13 = factory.getSchema(SchemaLocation.of("classpath:bom-1.3-strict.schema.json"));
75+
VERSION_14 = factory.getSchema(SchemaLocation.of("classpath:bom-1.4.schema.json"));
76+
VERSION_15 = factory.getSchema(SchemaLocation.of("classpath:bom-1.5.schema.json"));
77+
VERSION_16 = factory.getSchema(SchemaLocation.of("classpath:bom-1.6.schema.json"));
78+
}
2879

29-
public class JsonSchemaVerificationTest extends BaseSchemaVerificationTest {
80+
private static JsonMetaSchema addCustomKeywords(JsonMetaSchema metaSchema) {
81+
return JsonMetaSchema.builder(metaSchema)
82+
// Non-standard keywords in the CycloneDX schema files.
83+
.keyword(new NonValidationKeyword("deprecated"))
84+
.keyword(new NonValidationKeyword("meta:enum"))
85+
.unknownKeywordFactory(new DisallowUnknownKeywordFactory())
86+
.build();
87+
}
3088

3189
@TestFactory
3290
Collection<DynamicTest> dynamicTestsWithCollection() throws Exception {
33-
final List<String> files = getAllResources();
91+
final List<String> resources = getAllResources();
3492
final List<DynamicTest> dynamicTests = new ArrayList<>();
35-
for (final String file: files) {
36-
if (file.endsWith(".json")) {
37-
final Version schemaVersion;
38-
if (file.endsWith("-1.2.json")) {
39-
schemaVersion = Version.VERSION_12;
40-
} else if (file.endsWith("-1.3.json")) {
41-
schemaVersion = Version.VERSION_13;
42-
} else if (file.endsWith("-1.4.json")) {
43-
schemaVersion = Version.VERSION_14;
44-
} else if (file.endsWith("-1.5.json")) {
45-
schemaVersion = Version.VERSION_15;
46-
} else if (file.endsWith("-1.6.json")) {
47-
schemaVersion = Version.VERSION_16;
48-
} else {
49-
schemaVersion = null;
50-
}
51-
if (file.startsWith("valid") && schemaVersion != null) {
52-
dynamicTests.add(DynamicTest.dynamicTest(file, () -> assertTrue(
53-
isValidJson(schemaVersion, "/" + schemaVersion.getVersionString() + "/" + file), file)));
54-
} else if (file.startsWith("invalid") && schemaVersion != null) {
55-
dynamicTests.add(DynamicTest.dynamicTest(file, () -> assertFalse(
56-
isValidJson(schemaVersion, "/" + schemaVersion.getVersionString() + "/" + file), file)));
93+
for (final String resource : resources) {
94+
String resourceName = StringUtils.substringAfterLast(resource, "/");
95+
if (resourceName.endsWith(".json")) {
96+
JsonSchema schema = getSchema(resourceName);
97+
if (schema != null) {
98+
if (resourceName.startsWith("valid")) {
99+
dynamicTests.add(DynamicTest.dynamicTest(
100+
resource, () -> assertTrue(isValid(schema, resource), resource)));
101+
} else if (resourceName.startsWith("invalid")) {
102+
dynamicTests.add(DynamicTest.dynamicTest(
103+
resource, () -> assertFalse(isValid(schema, resource), resource)));
104+
}
57105
}
58106
}
59107
}
60108
return dynamicTests;
61109
}
62110

63-
private boolean isValidJson(Version version, String resource) throws Exception {
64-
final File file = new File(this.getClass().getResource(resource).getFile());
65-
final JsonParser parser = new JsonParser();
66-
return parser.isValid(file, version);
67-
68-
// Uncomment to provide more detailed validation errors
69-
/*
70-
try {
71-
final String jsonString = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
72-
parser.getJsonSchema(version, true).validate(new JSONObject(jsonString));
73-
return true;
74-
} catch (ValidationException e) {
75-
e.printStackTrace();
111+
private boolean isValid(JsonSchema schema, String resource) {
112+
try (InputStream input = getClass().getClassLoader().getResourceAsStream(resource);
113+
JsonParser parser = MAPPER.createParser(input)) {
114+
JsonNode node = parser.readValueAsTree();
115+
return schema.validate(node).isEmpty();
116+
} catch (IOException e) {
76117
return false;
77118
}
78-
*/
119+
}
120+
121+
private JsonSchema getSchema(String resourceName) {
122+
if (resourceName.endsWith("-1.2.json")) {
123+
return VERSION_12;
124+
}
125+
if (resourceName.endsWith("-1.3.json")) {
126+
return VERSION_13;
127+
}
128+
if (resourceName.endsWith("-1.4.json")) {
129+
return VERSION_14;
130+
}
131+
if (resourceName.endsWith("-1.5.json")) {
132+
return VERSION_15;
133+
}
134+
if (resourceName.endsWith("-1.6.json")) {
135+
return VERSION_16;
136+
}
137+
return null;
79138
}
80139
}

0 commit comments

Comments
 (0)