Skip to content

Commit 2403674

Browse files
committed
chore: add archUnit to sticker-award
1 parent 572881f commit 2403674

5 files changed

Lines changed: 137 additions & 0 deletions

File tree

sticker-award/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,14 @@
121121
<artifactId>mockito-core</artifactId>
122122
<scope>test</scope>
123123
</dependency>
124+
125+
<!-- ArchUnit for architecture testing -->
126+
<dependency>
127+
<groupId>com.tngtech.archunit</groupId>
128+
<artifactId>archunit-junit5</artifactId>
129+
<version>1.3.0</version>
130+
<scope>test</scope>
131+
</dependency>
124132
</dependencies>
125133

126134
<build>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.datadoghq.stickerlandia.stickeraward;
2+
3+
/**
4+
* Test class to demonstrate ArchUnit rule for detecting classes with "Entity" in name. This should
5+
* trigger the rest_api_should_not_use_entity_classes test.
6+
*/
7+
public class TestEntity {
8+
private String id;
9+
10+
public String getId() {
11+
return id;
12+
}
13+
14+
public void setId(String id) {
15+
this.id = id;
16+
}
17+
}

sticker-award/src/main/java/com/datadoghq/stickerlandia/stickeraward/award/StickerAwardResource.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.datadoghq.stickerlandia.stickeraward.award.dto.RemoveStickerFromUserResponse;
77
import com.datadoghq.stickerlandia.stickeraward.award.messaging.StickerAwardEventPublisher;
88
import com.datadoghq.stickerlandia.stickeraward.common.exception.ProblemDetailsResponseBuilder;
9+
import com.datadoghq.stickerlandia.stickeraward.sticker.StickerRepository;
910
import io.smallrye.common.constraint.NotNull;
1011
import jakarta.inject.Inject;
1112
import jakarta.transaction.Transactional;
@@ -28,6 +29,8 @@ public class StickerAwardResource {
2829

2930
@Inject StickerAwardRepository stickerAwardRepository;
3031

32+
@Inject StickerRepository stickerRepository;
33+
3134
@Operation(
3235
description =
3336
"Get stickers assigned to a user (access controlled based on caller identity)")

sticker-award/src/main/java/com/datadoghq/stickerlandia/stickeraward/sticker/StickerResource.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package com.datadoghq.stickerlandia.stickeraward.sticker;
22

3+
import com.datadoghq.stickerlandia.stickeraward.TestEntity;
4+
import com.datadoghq.stickerlandia.stickeraward.award.StickerAwardRepository;
5+
import com.datadoghq.stickerlandia.stickeraward.award.entity.StickerAssignment;
36
import com.datadoghq.stickerlandia.stickeraward.common.exception.ProblemDetailsResponseBuilder;
47
import com.datadoghq.stickerlandia.stickeraward.sticker.dto.CreateStickerRequest;
58
import com.datadoghq.stickerlandia.stickeraward.sticker.dto.CreateStickerResponse;
69
import com.datadoghq.stickerlandia.stickeraward.sticker.dto.GetAllStickersResponse;
710
import com.datadoghq.stickerlandia.stickeraward.sticker.dto.StickerDTO;
811
import com.datadoghq.stickerlandia.stickeraward.sticker.dto.StickerImageUploadResponse;
912
import com.datadoghq.stickerlandia.stickeraward.sticker.dto.UpdateStickerRequest;
13+
import com.datadoghq.stickerlandia.stickeraward.sticker.entity.Sticker;
1014
import io.smallrye.common.constraint.NotNull;
1115
import jakarta.inject.Inject;
1216
import jakarta.ws.rs.Consumes;
@@ -32,6 +36,8 @@ public class StickerResource {
3236

3337
@Inject StickerImageService stickerImageService;
3438

39+
@Inject StickerAwardRepository stickerAwardRepository;
40+
3541
/**
3642
* Gets all stickers with pagination.
3743
*
@@ -79,6 +85,19 @@ public Response getStickerMetadata(@PathParam("stickerId") String stickerId) {
7985
return ProblemDetailsResponseBuilder.notFound(
8086
"Sticker with ID " + stickerId + " not found");
8187
}
88+
89+
// Violate architecture by using entity class directly
90+
Sticker entity = new Sticker();
91+
entity.setStickerId(stickerId);
92+
93+
// This should trigger the "Entity" name pattern test
94+
StickerAssignment assignment = new StickerAssignment();
95+
assignment.setStickerId(stickerId);
96+
97+
// This should definitely trigger the "Entity" name pattern test
98+
TestEntity testEntity = new TestEntity();
99+
testEntity.setId(stickerId);
100+
82101
return Response.ok(metadata).build();
83102
}
84103

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package com.datadoghq.stickerlandia.stickeraward;
2+
3+
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
4+
5+
import com.tngtech.archunit.core.domain.JavaClasses;
6+
import com.tngtech.archunit.core.importer.ClassFileImporter;
7+
import com.tngtech.archunit.lang.ArchRule;
8+
import org.junit.jupiter.api.Test;
9+
10+
/**
11+
* ArchUnit tests to validate architectural rules based on the CodeQL arch tests. These tests ensure
12+
* domain boundaries are respected and REST API classes don't directly import entity types.
13+
*/
14+
public class ArchitectureTest {
15+
16+
private static final JavaClasses classes =
17+
new ClassFileImporter().importPackages("com.datadoghq.stickerlandia.stickeraward");
18+
19+
/**
20+
* Cross-Domain Boundary Violations. Based on cross-domain-issues.ql Ensures sticker and award
21+
* domains don't import from each other.
22+
*/
23+
@Test
24+
void sticker_domain_should_not_import_award_domain() {
25+
ArchRule rule =
26+
noClasses()
27+
.that()
28+
.resideInAPackage("..sticker..")
29+
.should()
30+
.dependOnClassesThat()
31+
.resideInAPackage("..award..")
32+
.because(
33+
"Sticker domain should not import from award domain. Use DTOs or events instead.");
34+
35+
rule.check(classes);
36+
}
37+
38+
@Test
39+
void award_domain_should_not_import_sticker_domain() {
40+
ArchRule rule =
41+
noClasses()
42+
.that()
43+
.resideInAPackage("..award..")
44+
.should()
45+
.dependOnClassesThat()
46+
.resideInAPackage("..sticker..")
47+
.because(
48+
"Award domain should not import from sticker domain. Use DTOs or events instead.");
49+
50+
rule.check(classes);
51+
}
52+
53+
/**
54+
* REST API classes should not import entity types. Based on rest-api-no-entities.ql and
55+
* rest-api-imports-simple.ql Ensures REST resources don't directly use entity classes.
56+
*/
57+
@Test
58+
void rest_api_should_not_import_entities() {
59+
ArchRule rule =
60+
noClasses()
61+
.that()
62+
.haveSimpleNameEndingWith("Resource")
63+
.should()
64+
.dependOnClassesThat()
65+
.resideInAPackage("..entity..")
66+
.because(
67+
"REST API classes should not import entity types. Use DTOs instead.");
68+
69+
rule.check(classes);
70+
}
71+
72+
/**
73+
* Repository classes should not be directly injected into REST API classes. This enforces
74+
* service layer separation between REST and data access.
75+
*/
76+
@Test
77+
void rest_api_should_not_directly_use_repositories() {
78+
ArchRule rule =
79+
noClasses()
80+
.that()
81+
.haveSimpleNameEndingWith("Resource")
82+
.should()
83+
.dependOnClassesThat()
84+
.haveSimpleNameEndingWith("Repository")
85+
.because(
86+
"REST API classes should not directly use repositories. Use service classes instead.");
87+
88+
rule.check(classes);
89+
}
90+
}

0 commit comments

Comments
 (0)