Skip to content

Commit 84e28a1

Browse files
committed
test(export): add integration tests for descriptor and service file generation
- Validate proper descriptor generation for `Exporter` and `XMLExporter` implementations. - Ensure service file creation for base contracts and absence for capability contracts. - Expand coverage with stub classes for JSON and DDI exporters.
1 parent fb20e30 commit 84e28a1

3 files changed

Lines changed: 206 additions & 0 deletions

File tree

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
2+
package io.gdcc.spi.export;
3+
4+
import io.gdcc.spi.export.fixtures.StubDdiExporter;
5+
import io.gdcc.spi.export.fixtures.StubJsonExporter;
6+
import io.gdcc.spi.meta.descriptor.Descriptor;
7+
import io.gdcc.spi.meta.descriptor.DescriptorFormat;
8+
import org.junit.jupiter.api.Test;
9+
10+
import java.io.IOException;
11+
import java.io.InputStream;
12+
import java.nio.charset.StandardCharsets;
13+
14+
import static org.junit.jupiter.api.Assertions.assertEquals;
15+
import static org.junit.jupiter.api.Assertions.assertNotNull;
16+
import static org.junit.jupiter.api.Assertions.assertNull;
17+
import static org.junit.jupiter.api.Assertions.assertTrue;
18+
19+
/**
20+
* Verifies that the annotation processor generates correct descriptors and service files
21+
* when compiling real Exporter SPI implementations.
22+
*
23+
* <p>The test implementation classes in this package are compiled with the processor on the
24+
* classpath. The processor writes descriptors and service files into {@code target/test-classes/},
25+
* which this test reads at runtime to verify correctness.</p>
26+
*/
27+
class ExporterImplTest {
28+
29+
@Test
30+
void generatesDescriptorAndServiceFileForBaseExporterImplementation() throws IOException {
31+
Class<?> implClass = StubJsonExporter.class;
32+
33+
Descriptor descriptor = readDescriptor(implClass);
34+
assertNotNull(descriptor, "Descriptor should be generated for " + implClass);
35+
36+
assertEquals(DescriptorFormat.transformClassName(implClass), descriptor.klass());
37+
assertEquals(Exporter.class.getCanonicalName(), descriptor.kind());
38+
assertEquals(Exporter.API_LEVEL, descriptor.contractLevel(Exporter.class.getCanonicalName()));
39+
assertEquals(ExportDataProvider.API_LEVEL, descriptor.requiredProviderLevel(ExportDataProvider.class.getCanonicalName()));
40+
41+
String serviceFile = readServiceFile(Exporter.class);
42+
assertNotNull(serviceFile, "Service file should be generated for Exporter");
43+
assertTrue(serviceFile.contains(DescriptorFormat.transformClassName(implClass)), "Service file should contain " + implClass);
44+
}
45+
46+
@Test
47+
void generatesDescriptorWithBaseAndCapabilityForXmlExporterImplementation() throws IOException {
48+
Class<?> implClass = StubDdiExporter.class;
49+
50+
Descriptor descriptor = readDescriptor(implClass);
51+
assertNotNull(descriptor, "Descriptor should be generated for " + implClass);
52+
53+
assertEquals(DescriptorFormat.transformClassName(implClass), descriptor.klass());
54+
assertEquals(Exporter.class.getCanonicalName(), descriptor.kind());
55+
assertEquals(Exporter.API_LEVEL, descriptor.contractLevel(Exporter.class.getCanonicalName()));
56+
assertEquals(XMLExporter.API_LEVEL, descriptor.contractLevel(XMLExporter.class.getCanonicalName()));
57+
assertEquals(ExportDataProvider.API_LEVEL, descriptor.requiredProviderLevel(ExportDataProvider.class.getCanonicalName()));
58+
59+
String serviceFile = readServiceFile(Exporter.class);
60+
assertNotNull(serviceFile, "Service file should be generated for Exporter");
61+
assertTrue(serviceFile.contains(DescriptorFormat.transformClassName(implClass)), "Service file should contain " + implClass);
62+
}
63+
64+
@Test
65+
void doesNotGenerateServiceFileForXmlExporterCapability() {
66+
String serviceFile = readServiceFile(XMLExporter.class);
67+
assertNull(serviceFile, "Service file must never be generated for capability contract XMLExporter");
68+
}
69+
70+
@Test
71+
void xmlExporterDefaultMediaTypeSatisfiesBaseContract() throws IOException {
72+
// StubDdiExporter implements XMLExporter (which extends Exporter) and does NOT
73+
// override getMediaType(). Because XMLExporter extends Exporter in the Java type
74+
// hierarchy, the default on XMLExporter satisfies the abstract declaration on Exporter.
75+
// If this were not the case, compilation would have failed and no descriptor would exist.
76+
Class<?> implClass = StubDdiExporter.class;
77+
78+
Descriptor descriptor = readDescriptor(implClass);
79+
assertNotNull(descriptor, "Descriptor should exist, proving compilation succeeded without explicit getMediaType() override");
80+
}
81+
82+
// ── Helpers ─────────────────────────────────────────────────────────────────
83+
84+
private Descriptor readDescriptor(Class implClass) throws IOException {
85+
String resourcePath = DescriptorFormat.toPath(implClass);
86+
try (InputStream is = getClass().getClassLoader().getResourceAsStream(resourcePath)) {
87+
if (is == null) {
88+
return null;
89+
}
90+
return DescriptorFormat.read(new String(is.readAllBytes(), StandardCharsets.UTF_8));
91+
}
92+
}
93+
94+
private String readServiceFile(Class<?> serviceType) {
95+
String resourcePath = "META-INF/services/" + serviceType.getName();
96+
try (InputStream is = getClass().getClassLoader().getResourceAsStream(resourcePath)) {
97+
if (is == null) {
98+
return null;
99+
}
100+
return new String(is.readAllBytes(), StandardCharsets.UTF_8);
101+
} catch (IOException e) {
102+
return null;
103+
}
104+
}
105+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package io.gdcc.spi.export.fixtures;
2+
3+
import io.gdcc.spi.export.ExportDataProvider;
4+
import io.gdcc.spi.export.XMLExporter;
5+
import io.gdcc.spi.meta.annotations.DataversePlugin;
6+
7+
import java.io.OutputStream;
8+
import java.util.Locale;
9+
10+
/**
11+
* Minimal XMLExporter implementation for processor integration testing.
12+
*
13+
* <p>Does NOT override {@code getMediaType()} — the default from {@link XMLExporter}
14+
* satisfies the abstract declaration on {@link io.gdcc.spi.export.Exporter} because XMLExporter extends Exporter.</p>
15+
*/
16+
@DataversePlugin
17+
public class StubDdiExporter implements XMLExporter {
18+
@Override
19+
public void exportDataset(ExportDataProvider dataProvider, OutputStream outputStream) {
20+
/* Intentionally left blank for test class */
21+
}
22+
23+
@Override
24+
public String getFormatName() {
25+
return "stub-ddi";
26+
}
27+
28+
@Override
29+
public String getDisplayName(Locale locale) {
30+
return "Stub DDI";
31+
}
32+
33+
@Override
34+
public Boolean isHarvestable() {
35+
return true;
36+
}
37+
38+
@Override
39+
public Boolean isAvailableToUsers() {
40+
return true;
41+
}
42+
43+
@Override
44+
public String getXMLNameSpace() {
45+
return "ddi:codebook:2_5";
46+
}
47+
48+
@Override
49+
public String getXMLSchemaLocation() {
50+
return "https://ddialliance.org/Specification/DDI-Codebook/2.5/XMLSchema/codebook.xsd";
51+
}
52+
53+
@Override
54+
public String getXMLSchemaVersion() {
55+
return "2.5";
56+
}
57+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package io.gdcc.spi.export.fixtures;
2+
3+
import io.gdcc.spi.export.ExportDataProvider;
4+
import io.gdcc.spi.export.Exporter;
5+
import io.gdcc.spi.meta.annotations.DataversePlugin;
6+
7+
import java.io.OutputStream;
8+
import java.util.Locale;
9+
10+
/**
11+
* Minimal base-only Exporter implementation for processor integration testing.
12+
*/
13+
@DataversePlugin
14+
public class StubJsonExporter implements Exporter {
15+
@Override
16+
public void exportDataset(ExportDataProvider dataProvider, OutputStream outputStream) {
17+
/* Intentionally left blank for test class */
18+
}
19+
20+
@Override
21+
public String getFormatName() {
22+
return "stub-json";
23+
}
24+
25+
@Override
26+
public String getDisplayName(Locale locale) {
27+
return "Stub JSON";
28+
}
29+
30+
@Override
31+
public Boolean isHarvestable() {
32+
return false;
33+
}
34+
35+
@Override
36+
public Boolean isAvailableToUsers() {
37+
return true;
38+
}
39+
40+
@Override
41+
public String getMediaType() {
42+
return "application/json";
43+
}
44+
}

0 commit comments

Comments
 (0)