Skip to content

Commit

Permalink
add database documentation generation
Browse files Browse the repository at this point in the history
  • Loading branch information
slu-it committed Sep 3, 2024
1 parent 91d2acd commit 7d47f1e
Show file tree
Hide file tree
Showing 9 changed files with 373 additions and 79 deletions.
14 changes: 12 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,32 @@ repositories {
dependencyManagement {
imports {
mavenBom("org.jetbrains.kotlin:kotlin-bom:1.9.24")
mavenBom("org.testcontainers:testcontainers-bom:1.19.8")
mavenBom(BOM_COORDINATES)
}
dependencies {
dependency("au.com.dius.pact.provider:junit5:4.6.7")
dependency("org.wiremock:wiremock-standalone:3.4.1")
}
}

dependencies {
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.springframework.boot:spring-boot-starter-amqp")
implementation("org.springframework.boot:spring-boot-starter-jdbc")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.flywaydb:flyway-database-postgresql")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("com.fasterxml.jackson.core:jackson-annotations")
implementation("com.fasterxml.jackson.core:jackson-core")
implementation("com.fasterxml.jackson.core:jackson-databind")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
testImplementation("au.com.dius.pact.provider:junit5:4.6.7")
runtimeOnly("org.postgresql:postgresql")
testImplementation("au.com.dius.pact.provider:junit5")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.wiremock:wiremock-standalone:3.4.1")
testImplementation("org.testcontainers:junit-jupiter")
testImplementation("org.testcontainers:postgresql")
testImplementation("org.wiremock:wiremock-standalone")
}

tasks.withType<KotlinCompile> {
Expand Down
11 changes: 0 additions & 11 deletions src/main/kotlin/application/persistence/OrderRepository.kt

This file was deleted.

23 changes: 23 additions & 0 deletions src/main/resources/db/migration/V001__init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
CREATE TABLE orders
(
id UUID NOT NULL,
customer_id UUID NOT NULL,
ordered_at TIMESTAMP WITH TIME ZONE NOT NULL,
dispatched_at TIMESTAMP WITH TIME ZONE,
billing_address_id UUID NOT NULL,
shipping_address_id UUID NOT NULL,
status TEXT NOT NULL DEFAULT 'OPEN',
PRIMARY KEY (id)
);

CREATE TABLE addresses
(
id UUID NOT NULL,
customer_id UUID NOT NULL,
name TEXT,
street TEXT,
city TEXT,
zip_code TEXT,
country CHAR(2),
PRIMARY KEY (id)
)
112 changes: 111 additions & 1 deletion src/test/kotlin/application/ApplicationTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,117 @@ package application
import application.documentation.ApplicationDescription
import application.documentation.ArchitectureDocumentation.createOrAmendConsumedQueue
import application.documentation.ArchitectureDocumentation.createOrReplaceApplication
import application.documentation.ArchitectureDocumentation.createOrReplaceDatabase
import application.documentation.ArchitectureDocumentation.createOrReplaceDependent
import application.documentation.ComponentType
import application.documentation.ComponentType.BACKEND
import application.documentation.ConsumedQueue
import application.documentation.DependentDescription
import application.documentation.Distance
import application.documentation.Distance.OWNED
import application.documentation.collectDatabaseDescription
import application.documentation.loadTableDescriptions
import au.com.dius.pact.provider.IConsumerInfo
import au.com.dius.pact.provider.junit5.HttpTestTarget
import au.com.dius.pact.provider.junit5.PactVerificationContext
import au.com.dius.pact.provider.junit5.PactVerificationInvocationContextProvider
import au.com.dius.pact.provider.junitsupport.Provider
import au.com.dius.pact.provider.junitsupport.State
import au.com.dius.pact.provider.junitsupport.VerificationReports
import au.com.dius.pact.provider.junitsupport.loader.PactFolder
import org.apache.hc.core5.http.HttpRequest
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS
import org.junit.jupiter.api.TestTemplate
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.amqp.core.Binding
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT
import org.springframework.boot.test.web.server.LocalServerPort
import org.springframework.jdbc.core.simple.JdbcClient
import org.springframework.test.context.DynamicPropertyRegistry
import org.springframework.test.context.DynamicPropertySource
import org.testcontainers.containers.PostgreSQLContainer
import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers
import java.util.UUID.randomUUID

@SpringBootTest
@Testcontainers
@SpringBootTest(webEnvironment = RANDOM_PORT)
class ApplicationTests {

companion object {

@JvmStatic
@Container
val postgres = PostgreSQLContainer("postgres:16.1-alpine")
.withDatabaseName("test-${randomUUID()}")
.withUsername("postgres")
.withPassword("password")
.withExposedPorts(5432)

@JvmStatic
@DynamicPropertySource
fun registerDatasourceProperties(registry: DynamicPropertyRegistry) {
registry.add("spring.datasource.url") {
"jdbc:postgresql://${postgres.host}:${postgres.firstMappedPort}/${postgres.databaseName}"
}
registry.add("spring.datasource.username") { postgres.username }
registry.add("spring.datasource.password") { postgres.password }
registry.add("spring.datasource.hikari.schema") { "test" }
}
}

@Nested
@TestInstance(PER_CLASS)
@Provider("order-service")
@PactFolder("src/test/pacts")
@VerificationReports("console")
@ExtendWith(PactVerificationInvocationContextProvider::class)
inner class ContractTests {

@BeforeEach
fun setTarget(context: PactVerificationContext, @LocalServerPort port: Int) {
context.target = HttpTestTarget("localhost", port)
}

@TestTemplate
fun `consumer contract tests`(context: PactVerificationContext, request: HttpRequest) {
createOrReplaceDependent(dependentDescription(context.consumer))
context.verifyInteraction()
}

@State("order with any id exists and can be deleted")
fun noOpStates() {
// nothing to do
}

private fun dependentDescription(consumer: IConsumerInfo): DependentDescription =
when (consumer.name) {
"frontend" -> DependentDescription(
id = "frontend",
type = ComponentType.FRONTEND,
distanceFromUs = Distance.OWNED
)

"external-service-x" -> DependentDescription(
id = "external-service-x",
type = ComponentType.BACKEND,
distanceFromUs = Distance.EXTERNAL
)

else -> error("Unmapped dependent [${consumer.name}]! Please add a mapping here.")
}
}

@Nested
inner class Documentation(
@Autowired private val jdbcClient: JdbcClient,
@Autowired private val queueBindings: List<Binding>,
@Value("\${spring.application.name}") private val applicationName: String,
) {
Expand All @@ -41,5 +137,19 @@ class ApplicationTests {
}
.forEach(::createOrAmendConsumedQueue)
}

@Test
fun `generate database description`() {
createOrReplaceDatabase(
collectDatabaseDescription(
id = "order-service-db",
name = "Order Service Database",
description = "The database of the Order Service.",
tables = loadTableDescriptions("/documentation/order-service-db_table-descriptions.json"),
jdbcClient = jdbcClient,
schemaName = "test"
)
)
}
}
}
65 changes: 0 additions & 65 deletions src/test/kotlin/application/ContractTests.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,15 @@ object ArchitectureDocumentation {
}
}

fun createOrReplaceDatabase(description: DatabaseDescription) {
val folder = File(rootFolder, "databases")
val file = File(folder, description.id + ".json")

createOrReplaceFile(file) {
write(toJsonString(description))
}
}

private fun toJsonString(value: Any): String =
objectMapper.writeValueAsString(value)

Expand Down
Loading

0 comments on commit 7d47f1e

Please sign in to comment.