Skip to content

Commit ac1a1df

Browse files
authored
NON-318: Refactor testcontainers setup (#417)
…and test on postgres 16 now that production database has been updated
1 parent 1ce32fb commit ac1a1df

File tree

11 files changed

+135
-120
lines changed

11 files changed

+135
-120
lines changed

.circleci/config.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ parameters:
1616
default: "21.0"
1717
postgres-version:
1818
type: string
19-
default: "15"
19+
default: "16"
2020
localstack-version:
2121
type: string
2222
default: "4"

README.md

+20-8
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,42 @@
11
# HMPPS Non-associations API
2+
23
[![CircleCI](https://circleci.com/gh/ministryofjustice/hmpps-non-associations-api/tree/main.svg?style=svg)](https://circleci.com/gh/ministryofjustice/hmpps-non-associations-api)
34
[![Docker Repository on Quay](https://quay.io/repository/hmpps/hmpps-non-associations-api/status "Docker Repository on Quay")](https://quay.io/repository/hmpps/hmpps-non-associations-api)
45
[![Runbook](https://img.shields.io/badge/runbook-view-172B4D.svg?logo=confluence)](https://dsdmoj.atlassian.net/wiki/spaces/NOM/pages/1739325587/DPS+Runbook)
56
[![API docs](https://img.shields.io/badge/API_docs_-view-85EA2D.svg?logo=swagger)](https://non-associations-api-dev.hmpps.service.justice.gov.uk/swagger-ui/index.html)
67
[![Event docs](https://img.shields.io/badge/Event_docs-view-85EA2D.svg)](https://studio.asyncapi.com/?url=https://raw.githubusercontent.com/ministryofjustice/hmpps-non-associations-api/main/async-api.yml&readOnly)
78

8-
**Non-associations API to own the non-associations data for prisoners**
9+
This application is the REST api and database that owns prisoner non-association data.
910

1011
## Running locally against dev/T3 services
1112

1213
This is straight-forward as authentication is delegated down to the calling services in `dev` environment.
13-
Environment variables to be set are as follows:
1414

15-
```
16-
API_BASE_URL_OAUTH=https://sign-in-dev.hmpps.service.justice.gov.uk/auth
17-
NON_ASSOCIATIONS_API_CLIENT_ID=[choose a suitable hmpps-auth client]
18-
NON_ASSOCIATIONS_API_CLIENT_SECRET=
19-
```
15+
Use all environment variables starting with `API_BASE_URL_` from [helm chart values](./helm_deploy/values-dev.yaml).
16+
Choose a suitable hmpps-auth oauth client, for instance from kubernetes `hmpps-incentives-api` secret and add
17+
`NON_ASSOCIATIONS_API_CLIENT_ID` and `NON_ASSOCIATIONS_API_CLIENT_SECRET`.
2018

2119
Start the database and other required services via docker-compose with:
2220

2321
```shell
2422
docker compose -f docker-compose-local.yml up
2523
```
2624

27-
Then run the API.
25+
Then run the API; for example using IntelliJ.
26+
27+
## Testing and linting
28+
29+
Run unit and integration tests with:
30+
31+
```shell
32+
./gradlew test
33+
```
34+
35+
Run automatic lint fixes:
36+
37+
```shell
38+
./gradlew ktlintformat
39+
```
2840

2941
## Connecting to AWS resources from a local port
3042

docker-compose-local.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ services:
1616
- "/var/run/docker.sock:/var/run/docker.sock"
1717

1818
non-associations-db:
19-
image: postgres:15
19+
image: postgres:16
2020
networks:
2121
- hmpps
2222
container_name: non-postgres

docker-compose.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
services:
22

33
database:
4-
image: postgres:15
4+
image: postgres:16
55
networks:
66
- hmpps
77
container_name: non-db

src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsnonassociationsapi/config/LocalStackContainer.kt

-46
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package uk.gov.justice.digital.hmpps.hmppsnonassociationsapi.config
2+
3+
import org.springframework.test.context.DynamicPropertyRegistry
4+
import org.testcontainers.containers.localstack.LocalStackContainer
5+
import org.testcontainers.containers.output.Slf4jLogConsumer
6+
import org.testcontainers.utility.DockerImageName
7+
8+
object LocalStackTestcontainer : Testcontainer<LocalStackContainer>("localstack", 4566) {
9+
override fun start(): LocalStackContainer {
10+
val logConsumer = Slf4jLogConsumer(log).withPrefix("localstack")
11+
log.info("Starting localstack")
12+
return LocalStackContainer(
13+
DockerImageName.parse("localstack/localstack").withTag("4"),
14+
).apply {
15+
withServices(LocalStackContainer.Service.SQS, LocalStackContainer.Service.SNS)
16+
withEnv("DEFAULT_REGION", "eu-west-2")
17+
start()
18+
followOutput(logConsumer)
19+
}
20+
}
21+
22+
override fun setupProperties(instance: LocalStackContainer, registry: DynamicPropertyRegistry) {
23+
val localstackUrl = instance.getEndpointOverride(LocalStackContainer.Service.SNS).toString()
24+
registry.add("hmpps.sqs.localstackUrl") { localstackUrl }
25+
registry.add("hmpps.sqs.region", instance::getRegion)
26+
}
27+
}

src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsnonassociationsapi/config/PostgresContainer.kt

-39
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package uk.gov.justice.digital.hmpps.hmppsnonassociationsapi.config
2+
3+
import org.springframework.test.context.DynamicPropertyRegistry
4+
import org.testcontainers.containers.PostgreSQLContainer
5+
import org.testcontainers.containers.wait.strategy.Wait
6+
import org.testcontainers.utility.DockerImageName
7+
8+
object PostgresTestcontainer : Testcontainer<PostgreSQLContainer<Nothing>>("postgres", 5432) {
9+
override fun start(): PostgreSQLContainer<Nothing> {
10+
log.info("Creating a Postgres database")
11+
return PostgreSQLContainer<Nothing>(
12+
DockerImageName.parse("postgres").withTag("16"),
13+
).apply {
14+
withEnv("HOSTNAME_EXTERNAL", "localhost")
15+
withDatabaseName("non_associations")
16+
withUsername("non_associations")
17+
withPassword("non_associations")
18+
setWaitStrategy(Wait.forListeningPort())
19+
withReuse(true)
20+
21+
start()
22+
}
23+
}
24+
25+
override fun setupProperties(instance: PostgreSQLContainer<Nothing>, registry: DynamicPropertyRegistry) {
26+
registry.add("spring.datasource.url", instance::getJdbcUrl)
27+
registry.add("spring.datasource.username", instance::getUsername)
28+
registry.add("spring.datasource.password", instance::getPassword)
29+
registry.add("spring.flyway.url", instance::getJdbcUrl)
30+
registry.add("spring.flyway.user", instance::getUsername)
31+
registry.add("spring.flyway.password", instance::getPassword)
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package uk.gov.justice.digital.hmpps.hmppsnonassociationsapi.config
2+
3+
import org.slf4j.Logger
4+
import org.slf4j.LoggerFactory
5+
import org.springframework.test.context.DynamicPropertyRegistry
6+
import java.io.IOException
7+
import java.net.ServerSocket
8+
9+
abstract class Testcontainer<InstanceType>(
10+
val containerName: String,
11+
val exposedPort: Int,
12+
) {
13+
protected val log: Logger = LoggerFactory.getLogger(this::class.java)
14+
15+
val instance: InstanceType? by lazy { startIfNotRunning() }
16+
17+
open fun isRunning(): Boolean = try {
18+
val serverSocket = ServerSocket(exposedPort)
19+
serverSocket.localPort == 0
20+
} catch (e: IOException) {
21+
true
22+
}
23+
24+
open fun startIfNotRunning(): InstanceType? {
25+
if (isRunning()) {
26+
log.warn("Not starting $containerName container because port $exposedPort is already open")
27+
return null
28+
}
29+
return start()
30+
}
31+
32+
abstract fun start(): InstanceType
33+
34+
open fun setupProperties(instance: InstanceType, registry: DynamicPropertyRegistry) {}
35+
}

src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsnonassociationsapi/helper/TestBase.kt

+5-11
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package uk.gov.justice.digital.hmpps.hmppsnonassociationsapi.helper
33
import org.springframework.test.context.ActiveProfiles
44
import org.springframework.test.context.DynamicPropertyRegistry
55
import org.springframework.test.context.DynamicPropertySource
6-
import uk.gov.justice.digital.hmpps.hmppsnonassociationsapi.config.PostgresContainer
6+
import uk.gov.justice.digital.hmpps.hmppsnonassociationsapi.config.PostgresTestcontainer
77
import java.time.Clock
88
import java.time.Instant
99
import java.time.LocalDateTime
@@ -20,19 +20,13 @@ abstract class TestBase {
2020
)
2121
val now: LocalDateTime = LocalDateTime.now(clock)
2222

23-
private val pgContainer = PostgresContainer.instance
23+
private val postgresInstance = PostgresTestcontainer.instance
2424

25+
@Suppress("unused")
2526
@JvmStatic
2627
@DynamicPropertySource
27-
fun properties(registry: DynamicPropertyRegistry) {
28-
pgContainer?.run {
29-
registry.add("spring.datasource.url", pgContainer::getJdbcUrl)
30-
registry.add("spring.datasource.username", pgContainer::getUsername)
31-
registry.add("spring.datasource.password", pgContainer::getPassword)
32-
registry.add("spring.flyway.url", pgContainer::getJdbcUrl)
33-
registry.add("spring.flyway.user", pgContainer::getUsername)
34-
registry.add("spring.flyway.password", pgContainer::getPassword)
35-
}
28+
fun postgresProperties(registry: DynamicPropertyRegistry) {
29+
postgresInstance?.let { PostgresTestcontainer.setupProperties(postgresInstance, registry) }
3630
}
3731
}
3832

src/test/kotlin/uk/gov/justice/digital/hmpps/hmppsnonassociationsapi/integration/SqsIntegrationTestBase.kt

+12-13
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ import org.springframework.test.context.DynamicPropertyRegistry
88
import org.springframework.test.context.DynamicPropertySource
99
import org.springframework.test.context.bean.override.mockito.MockitoSpyBean
1010
import software.amazon.awssdk.services.sqs.model.PurgeQueueRequest
11-
import uk.gov.justice.digital.hmpps.hmppsnonassociationsapi.config.LocalStackContainer
12-
import uk.gov.justice.digital.hmpps.hmppsnonassociationsapi.config.LocalStackContainer.setLocalStackProperties
11+
import uk.gov.justice.digital.hmpps.hmppsnonassociationsapi.config.LocalStackTestcontainer
1312
import uk.gov.justice.hmpps.sqs.HmppsQueue
1413
import uk.gov.justice.hmpps.sqs.HmppsQueueService
1514
import uk.gov.justice.hmpps.sqs.HmppsSqsProperties
@@ -21,6 +20,17 @@ import uk.gov.justice.hmpps.sqs.countMessagesOnQueue
2120
@ActiveProfiles("test")
2221
class SqsIntegrationTestBase : IntegrationTestBase() {
2322

23+
companion object {
24+
private val localstackInstance = LocalStackTestcontainer.instance
25+
26+
@Suppress("unused")
27+
@JvmStatic
28+
@DynamicPropertySource
29+
fun localstackProperties(registry: DynamicPropertyRegistry) {
30+
localstackInstance?.also { LocalStackTestcontainer.setupProperties(localstackInstance, registry) }
31+
}
32+
}
33+
2434
@Autowired
2535
private lateinit var hmppsQueueService: HmppsQueueService
2636

@@ -49,17 +59,6 @@ class SqsIntegrationTestBase : IntegrationTestBase() {
4959
nonAssociationsQueue.sqsClient.countMessagesOnQueue(nonAssociationsQueue.queueUrl).get()
5060
}
5161

52-
companion object {
53-
private val localStackContainer = LocalStackContainer.instance
54-
55-
@Suppress("unused")
56-
@JvmStatic
57-
@DynamicPropertySource
58-
fun testcontainers(registry: DynamicPropertyRegistry) {
59-
localStackContainer?.also { setLocalStackProperties(it, registry) }
60-
}
61-
}
62-
6362
fun getNumberOfMessagesCurrentlyOnQueue(): Int? =
6463
nonAssociationsQueue.sqsClient.countMessagesOnQueue(nonAssociationsQueue.queueUrl).get()
6564
}

0 commit comments

Comments
 (0)