Skip to content

Commit 6263110

Browse files
authored
Standardize Arch Unit tests (#2359)
1 parent d4345a5 commit 6263110

2 files changed

Lines changed: 15 additions & 219 deletions

File tree

schemacrawler-verify/src/test/java/schemacrawler/test/ArchitectureTest.java

Lines changed: 4 additions & 219 deletions
Original file line numberDiff line numberDiff line change
@@ -8,225 +8,10 @@
88

99
package schemacrawler.test;
1010

11-
import static com.tngtech.archunit.base.DescribedPredicate.not;
12-
import static com.tngtech.archunit.core.domain.JavaClass.Predicates.resideInAPackage;
13-
import static com.tngtech.archunit.core.domain.JavaClass.Predicates.simpleName;
14-
import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith;
15-
import static com.tngtech.archunit.core.importer.ImportOption.Predefined.DO_NOT_INCLUDE_TESTS;
16-
import static com.tngtech.archunit.lang.conditions.ArchPredicates.are;
17-
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
18-
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.methods;
19-
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
20-
import static com.tngtech.archunit.library.GeneralCodingRules.ACCESS_STANDARD_STREAMS;
21-
import static com.tngtech.archunit.library.GeneralCodingRules.THROW_GENERIC_EXCEPTIONS;
22-
import static com.tngtech.archunit.library.dependencies.SlicesRuleDefinition.slices;
23-
import static org.hamcrest.CoreMatchers.is;
24-
import static org.hamcrest.MatcherAssert.assertThat;
25-
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
11+
public class ArchitectureTest extends BaseArchitectureTest {
2612

27-
import com.tngtech.archunit.core.domain.JavaClasses;
28-
import com.tngtech.archunit.core.domain.JavaMethod;
29-
import com.tngtech.archunit.core.domain.JavaModifier;
30-
import com.tngtech.archunit.core.importer.ClassFileImporter;
31-
import java.lang.reflect.AccessibleObject;
32-
import java.lang.reflect.Constructor;
33-
import java.util.Optional;
34-
import java.util.regex.Pattern;
35-
import org.junit.jupiter.api.BeforeAll;
36-
import org.junit.jupiter.api.Test;
37-
import org.junit.jupiter.api.TestInstance;
38-
import schemacrawler.schemacrawler.ModelImplementation;
39-
import schemacrawler.schemacrawler.Retriever;
40-
41-
@TestInstance(PER_CLASS)
42-
public class ArchitectureTest {
43-
44-
private JavaClasses classes;
45-
46-
@BeforeAll
47-
public void _classes() {
48-
final String description = "SchemaCrawler production classes";
49-
classes =
50-
new ClassFileImporter()
51-
.withImportOption(DO_NOT_INCLUDE_TESTS)
52-
.withImportOption(location -> !location.matches(Pattern.compile(".*[Tt]est.*")))
53-
.importPackages("schemacrawler..")
54-
.as(description);
55-
assertThat(description + " classes not found", classes.isEmpty(), is(false));
56-
}
57-
58-
@Test
59-
public void lookupMethods() {
60-
final Optional<JavaMethod> anyMatchingMethod =
61-
classes.stream()
62-
.flatMap(c -> c.getMethods().stream())
63-
.filter(m -> m.getName().matches("lookup.*"))
64-
.filter(m -> m.getModifiers().contains(JavaModifier.PUBLIC))
65-
.findAny();
66-
assertThat(anyMatchingMethod.isPresent(), is(true));
67-
68-
methods()
69-
.that()
70-
.haveNameMatching("lookup.*")
71-
.and()
72-
.arePublic()
73-
.should()
74-
.haveRawReturnType(Optional.class)
75-
.because("lookups may not return a value")
76-
.check(classes);
77-
}
78-
79-
// The schemacrawler.crawl package is intentionally kept flat (not split into subpackages).
80-
//
81-
// All Mutable* model implementations and *Retriever JDBC extractors are package-private.
82-
// This prevents both module-path and classpath users from constructing or referencing
83-
// these internal classes.
84-
//
85-
// Java package-private visibility is strictly per-package. Splitting into subpackages would
86-
// require making these classes at least public — immediately exposing them.
87-
// Although schemacrawler.crawl is exported from the JPMS module (for MetadataResultSet and
88-
// ResultsCrawler access), the package-private Mutable* and @Retriever classes remain
89-
// inaccessible. These tests enforce the architectural boundary.
90-
@Test
91-
public void model() {
92-
93-
noClasses()
94-
.that()
95-
.resideOutsideOfPackage("schemacrawler.crawl")
96-
.should()
97-
.dependOnClassesThat(
98-
resideInAPackage("schemacrawler.crawl").and(annotatedWith(ModelImplementation.class)))
99-
.because(
100-
"""
101-
@ModelImplementation classes in schemacrawler.crawl are package-private internal
102-
schema model implementations; they must only be used within schemacrawler.crawl
103-
to prevent classpath clients from constructing schema model objects directly
104-
""")
105-
.check(classes);
106-
107-
noClasses()
108-
.that()
109-
.resideOutsideOfPackage("schemacrawler.ermodel.implementation")
110-
.should()
111-
.dependOnClassesThat(
112-
resideInAPackage("schemacrawler.ermodel.implementation")
113-
.and(annotatedWith(ModelImplementation.class)))
114-
.because(
115-
"""
116-
@ModelImplementation classes in schemacrawler.ermodel.implementation are internal
117-
ER model implementations; they must only be used within that package
118-
""")
119-
.check(classes);
120-
121-
noClasses()
122-
.that()
123-
.resideOutsideOfPackages(
124-
"schemacrawler.loader.catalog.model", "schemacrawler.loader.ermodel.attributes")
125-
.should()
126-
.dependOnClassesThat(
127-
resideInAPackage("schemacrawler.loader.catalog.model")
128-
.and(annotatedWith(ModelImplementation.class)))
129-
.because(
130-
"""
131-
@ModelImplementation classes in schemacrawler.loader.catalog.model are internal YAML
132-
deserialization DTOs; only AttributesLoader in ermodel.attributes (the designated
133-
processor) is permitted to reference them outside the model package
134-
""")
135-
.check(classes);
136-
137-
noClasses()
138-
.that()
139-
.resideOutsideOfPackage("schemacrawler.crawl")
140-
.should()
141-
.dependOnClassesThat(annotatedWith(Retriever.class))
142-
.because(
143-
"""
144-
@Retriever classes in schemacrawler.crawl are package-private JDBC metadata
145-
extractors; they must only be used within schemacrawler.crawl
146-
""")
147-
.check(classes);
148-
149-
classes()
150-
.that()
151-
.areAnnotatedWith(ModelImplementation.class)
152-
.should()
153-
.notBePublic()
154-
.because(
155-
"""
156-
@ModelImplementation classes are package-private internal implementations;
157-
declaring them public exposes them to classpath clients
158-
""")
159-
.check(classes);
160-
161-
classes()
162-
.that()
163-
.areAnnotatedWith(Retriever.class)
164-
.should()
165-
.notBePublic()
166-
.because(
167-
"""
168-
@Retriever classes are package-private JDBC metadata extractors;
169-
declaring them public exposes them to classpath clients
170-
""")
171-
.check(classes);
172-
}
173-
174-
@Test
175-
public void notAccessStandardStreams() {
176-
noClasses()
177-
.should(ACCESS_STANDARD_STREAMS)
178-
.because("production code should not write to standard streams")
179-
.check(classes);
180-
}
181-
182-
@Test
183-
public void notThrowGenericExceptions() {
184-
noClasses()
185-
.should(THROW_GENERIC_EXCEPTIONS)
186-
.because(
187-
"SchemaCrawler defines it own exceptions, and wraps SQL exceptions with additional"
188-
+ " information")
189-
.check(classes);
190-
}
191-
192-
@Test
193-
public void packageCycles() {
194-
slices()
195-
.matching("schemacrawler.(**)..")
196-
.as("SchemaCrawler production classes")
197-
.should()
198-
.beFreeOfCycles()
199-
.because("packages should have a clear, acyclic dependency structure")
200-
.check(classes);
201-
}
202-
203-
@Test
204-
public void reflectiveAccessOverride() {
205-
noClasses()
206-
.should()
207-
.callMethod(AccessibleObject.class, "setAccessible", boolean.class)
208-
.orShould()
209-
.callMethod(
210-
AccessibleObject.class, "setAccessible", AccessibleObject[].class, boolean.class)
211-
.because("avoid reflective access override")
212-
.check(classes);
213-
}
214-
215-
@Test
216-
public void reflectiveClassLoading() {
217-
noClasses()
218-
.that(are(not(simpleName("BasePluginCommandRegistry"))))
219-
.should()
220-
.callMethod(Class.class, "forName", String.class)
221-
.orShould()
222-
.callMethod(Class.class, "getDeclaredConstructors")
223-
.orShould()
224-
.callMethod(Class.class, "getDeclaredConstructor", Class[].class)
225-
.orShould()
226-
.callMethod(Class.class, "getConstructor", Class[].class)
227-
.orShould()
228-
.callMethod(Constructor.class, "newInstance")
229-
.because("avoid reflective class loading")
230-
.check(classes);
13+
@Override
14+
protected String classesSpecification() {
15+
return "schemacrawler..";
23116
}
23217
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
* SchemaCrawler
3+
* http://www.schemacrawler.com
4+
* Copyright (c) 2000-2026, Sualeh Fatehi <sualeh@hotmail.com>.
5+
* All rights reserved.
6+
* SPDX-License-Identifier: EPL-2.0
7+
*/
8+
9+
package schemacrawler.test;
10+
11+
public class NoReflectionTest extends BaseNoReflectionTest {}

0 commit comments

Comments
 (0)