From 8aa657779954fc348d6774760b776224881a4b3d Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Wed, 12 Jul 2023 09:45:34 +0200 Subject: [PATCH] Add support for CosmosDB emulator Fixes #140 --- gradle/libs.versions.toml | 1 + settings.gradle | 11 +- .../test-resources-azure-cosmos/build.gradle | 19 +++ .../cosmos/CosmosTestResourcesProvider.java | 110 ++++++++++++++++++ ...t.testresources.core.TestResourcesResolver | 1 + .../azure/cosmos/CosmosStartedTest.groovy | 27 +++++ .../azure/cosmos/CosmosBook.java | 34 ++++++ .../azure/cosmos/CosmosBookRepository.java | 24 ++++ .../src/test/resources/application-test.yml | 6 + .../src/test/resources/logback.xml | 15 +++ 10 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 test-resources-azure/test-resources-azure-cosmos/build.gradle create mode 100644 test-resources-azure/test-resources-azure-cosmos/src/main/java/io/micronaut/testresources/azure/cosmos/CosmosTestResourcesProvider.java create mode 100644 test-resources-azure/test-resources-azure-cosmos/src/main/resources/META-INF/services/io.micronaut.testresources.core.TestResourcesResolver create mode 100644 test-resources-azure/test-resources-azure-cosmos/src/test/groovy/io/micronaut/testresources/azure/cosmos/CosmosStartedTest.groovy create mode 100644 test-resources-azure/test-resources-azure-cosmos/src/test/java/io/micronaut/testresources/azure/cosmos/CosmosBook.java create mode 100644 test-resources-azure/test-resources-azure-cosmos/src/test/java/io/micronaut/testresources/azure/cosmos/CosmosBookRepository.java create mode 100644 test-resources-azure/test-resources-azure-cosmos/src/test/resources/application-test.yml create mode 100644 test-resources-azure/test-resources-azure-cosmos/src/test/resources/logback.xml diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8ac3ad149..b256ef034 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -54,6 +54,7 @@ managed-testcontainers-redis = "1.6.4" boms-testcontainers = { module = "org.testcontainers:testcontainers-bom", version.ref = "managed-testcontainers" } managed-testcontainers-core = { module = "org.testcontainers:testcontainers", version.ref = "managed-testcontainers" } +managed-testcontainers-azure = { module = "org.testcontainers:azure", version.ref = "managed-testcontainers" } managed-testcontainers-elasticsearch = { module = "org.testcontainers:elasticsearch", version.ref = "managed-testcontainers" } managed-testcontainers-jdbc = { module = "org.testcontainers:jdbc", version.ref = "managed-testcontainers" } managed-testcontainers-hivemq = { module = "org.testcontainers:hivemq", version.ref = "managed-testcontainers" } diff --git a/settings.gradle b/settings.gradle index 0aaefa757..04fb36ba4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -57,6 +57,10 @@ def localstackModules = [ 'sqs' ] +def azureModules = [ + 'cosmos' +] + include 'test-resources-bom' include 'test-resources-build-tools' include 'test-resources-core' @@ -94,13 +98,18 @@ localstackModules.each { project(":test-resources-localstack-$it").projectDir = file("test-resources-localstack/$projectName") } - hibernateReactiveModules.each { String projectName = "test-resources-hibernate-reactive-$it" include projectName project(":test-resources-hibernate-reactive-$it").projectDir = file("test-resources-hibernate-reactive/$projectName") } +azureModules.each { + String projectName = "test-resources-azure-$it" + include projectName + project(":test-resources-azure-$it").projectDir = file("test-resources-azure/$projectName") +} + micronautBuild { useStandardizedProjectNames = true importMicronautCatalog() diff --git a/test-resources-azure/test-resources-azure-cosmos/build.gradle b/test-resources-azure/test-resources-azure-cosmos/build.gradle new file mode 100644 index 000000000..925a6aad1 --- /dev/null +++ b/test-resources-azure/test-resources-azure-cosmos/build.gradle @@ -0,0 +1,19 @@ +plugins { + id 'io.micronaut.build.internal.test-resources-module' +} + +description = """ +Provides support for launching an Azure Cosmos test container. +""" + +dependencies { + api(project(':micronaut-test-resources-core')) + api(project(':micronaut-test-resources-testcontainers')) + api(libs.managed.testcontainers.azure) + + testAnnotationProcessor(mn.micronaut.inject.java) + testAnnotationProcessor(mnData.micronaut.data.document.processor) + testImplementation(project(":micronaut-test-resources-embedded")) + testImplementation(testFixtures(project(":micronaut-test-resources-testcontainers"))) + testImplementation(mnData.micronaut.data.azure.cosmos) +} diff --git a/test-resources-azure/test-resources-azure-cosmos/src/main/java/io/micronaut/testresources/azure/cosmos/CosmosTestResourcesProvider.java b/test-resources-azure/test-resources-azure-cosmos/src/main/java/io/micronaut/testresources/azure/cosmos/CosmosTestResourcesProvider.java new file mode 100644 index 000000000..18936f699 --- /dev/null +++ b/test-resources-azure/test-resources-azure-cosmos/src/main/java/io/micronaut/testresources/azure/cosmos/CosmosTestResourcesProvider.java @@ -0,0 +1,110 @@ +/* + * Copyright 2003-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.testresources.azure.cosmos; + +import io.micronaut.testresources.testcontainers.AbstractTestContainersProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.CosmosDBEmulatorContainer; +import org.testcontainers.utility.DockerImageName; + +import java.io.IOException; +import java.nio.file.Files; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.time.Duration; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +public class CosmosTestResourcesProvider extends AbstractTestContainersProvider { + private static final Logger LOGGER = LoggerFactory.getLogger(CosmosTestResourcesProvider.class); + private static final Duration STARTUP_TIMEOUT = Duration.ofMinutes(5); + + public static final String SIMPLE_NAME = "cosmosdb"; + public static final String DEFAULT_IMAGE = "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest"; + + public static final String ENDPOINT = "azure.cosmos.endpoint"; + public static final String KEY = "azure.cosmos.key"; + public static final List RESOLVABLE_PROPERTIES_LIST = List.of(ENDPOINT, KEY); + public static final Set RESOLVABLE_PROPERTIES_SET = Set.of(ENDPOINT, KEY); + + @Override + protected String getSimpleName() { + return SIMPLE_NAME; + } + + @Override + protected String getDefaultImageName() { + return DEFAULT_IMAGE; + } + + @Override + public List getResolvableProperties(Map> propertyEntries, Map testResourcesConfig) { + return RESOLVABLE_PROPERTIES_LIST; + } + + @Override + protected boolean shouldAnswer(String propertyName, Map requestedProperties, Map testResourcesConfig) { + return RESOLVABLE_PROPERTIES_SET.contains(propertyName); + } + + @Override + protected CosmosDBEmulatorContainer createContainer(DockerImageName imageName, Map requestedProperties, Map testResourcesConfig) { + return new CosmosDBEmulatorWithKeystoreContainer(imageName).withStartupTimeout(STARTUP_TIMEOUT); + } + + @Override + protected Optional resolveProperty(String propertyName, CosmosDBEmulatorContainer container) { + if (RESOLVABLE_PROPERTIES_SET.contains(propertyName)) { + return Optional.ofNullable(switch (propertyName) { + case ENDPOINT -> container.getEmulatorEndpoint(); + case KEY -> container.getEmulatorKey(); + default -> null; + }); + } + return Optional.empty(); + } + + private static class CosmosDBEmulatorWithKeystoreContainer extends CosmosDBEmulatorContainer { + public CosmosDBEmulatorWithKeystoreContainer(DockerImageName imageName) { + super(imageName); + } + + @Override + public void start() { + super.start(); + configureKeyStore(this); + } + + private void configureKeyStore(CosmosDBEmulatorContainer emulator) { + try { + var keyStoreFile = Files.createTempFile("azure-cosmos-emulator", ".keystore"); + var keyStore = emulator.buildNewKeyStore(); + try (var outputStream = Files.newOutputStream(keyStoreFile)) { + keyStore.store(outputStream, emulator.getEmulatorKey().toCharArray()); + } + } catch (IOException | KeyStoreException | NoSuchAlgorithmException | + CertificateException ex) { + LOGGER.error("Cannot create keystore for CosmosDB", ex); + } + } + + } +} diff --git a/test-resources-azure/test-resources-azure-cosmos/src/main/resources/META-INF/services/io.micronaut.testresources.core.TestResourcesResolver b/test-resources-azure/test-resources-azure-cosmos/src/main/resources/META-INF/services/io.micronaut.testresources.core.TestResourcesResolver new file mode 100644 index 000000000..988115e39 --- /dev/null +++ b/test-resources-azure/test-resources-azure-cosmos/src/main/resources/META-INF/services/io.micronaut.testresources.core.TestResourcesResolver @@ -0,0 +1 @@ +io.micronaut.testresources.azure.cosmos.CosmosTestResourcesProvider diff --git a/test-resources-azure/test-resources-azure-cosmos/src/test/groovy/io/micronaut/testresources/azure/cosmos/CosmosStartedTest.groovy b/test-resources-azure/test-resources-azure-cosmos/src/test/groovy/io/micronaut/testresources/azure/cosmos/CosmosStartedTest.groovy new file mode 100644 index 000000000..a817b1884 --- /dev/null +++ b/test-resources-azure/test-resources-azure-cosmos/src/test/groovy/io/micronaut/testresources/azure/cosmos/CosmosStartedTest.groovy @@ -0,0 +1,27 @@ +package io.micronaut.testresources.azure.cosmos + +import io.micronaut.test.extensions.spock.annotation.MicronautTest +import io.micronaut.testresources.testcontainers.AbstractTestContainersSpec +import jakarta.inject.Inject + +@MicronautTest +class CosmosStartedTest extends AbstractTestContainersSpec { + @Inject + CosmosBookRepository repository + + def "starts a CosmosDB container"() { + def book = new CosmosBook(null, "Micronaut for Spring developers", 50, "1") + repository.save(book) + + when: + def books = repository.findAll() + + then: + books.size() == 1 + } + + @Override + String getImageName() { + "cosmosdb" + } +} diff --git a/test-resources-azure/test-resources-azure-cosmos/src/test/java/io/micronaut/testresources/azure/cosmos/CosmosBook.java b/test-resources-azure/test-resources-azure-cosmos/src/test/java/io/micronaut/testresources/azure/cosmos/CosmosBook.java new file mode 100644 index 000000000..17ca30659 --- /dev/null +++ b/test-resources-azure/test-resources-azure-cosmos/src/test/java/io/micronaut/testresources/azure/cosmos/CosmosBook.java @@ -0,0 +1,34 @@ +/* + * Copyright 2003-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.testresources.azure.cosmos; + +import io.micronaut.data.annotation.GeneratedValue; +import io.micronaut.data.annotation.Id; +import io.micronaut.data.annotation.MappedEntity; +import io.micronaut.data.cosmos.annotation.ETag; +import io.micronaut.data.cosmos.annotation.PartitionKey; + +@MappedEntity("cosmosbook") +public record CosmosBook( + @Id + @GeneratedValue + @PartitionKey + String id, + String title, + int totalPages, + @ETag + String version) { +} diff --git a/test-resources-azure/test-resources-azure-cosmos/src/test/java/io/micronaut/testresources/azure/cosmos/CosmosBookRepository.java b/test-resources-azure/test-resources-azure-cosmos/src/test/java/io/micronaut/testresources/azure/cosmos/CosmosBookRepository.java new file mode 100644 index 000000000..4779188b9 --- /dev/null +++ b/test-resources-azure/test-resources-azure-cosmos/src/test/java/io/micronaut/testresources/azure/cosmos/CosmosBookRepository.java @@ -0,0 +1,24 @@ +/* + * Copyright 2003-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.testresources.azure.cosmos; + +import io.micronaut.data.cosmos.annotation.CosmosRepository; +import io.micronaut.data.repository.CrudRepository; + +@CosmosRepository +public abstract class CosmosBookRepository implements CrudRepository { + +} diff --git a/test-resources-azure/test-resources-azure-cosmos/src/test/resources/application-test.yml b/test-resources-azure/test-resources-azure-cosmos/src/test/resources/application-test.yml new file mode 100644 index 000000000..1818abf73 --- /dev/null +++ b/test-resources-azure/test-resources-azure-cosmos/src/test/resources/application-test.yml @@ -0,0 +1,6 @@ +azure: + cosmos: + database: + packages: io.micronaut.testresources.azure.cosmos + default-gateway-mode: true + endpoint-discovery-enabled: false diff --git a/test-resources-azure/test-resources-azure-cosmos/src/test/resources/logback.xml b/test-resources-azure/test-resources-azure-cosmos/src/test/resources/logback.xml new file mode 100644 index 000000000..f1950efcc --- /dev/null +++ b/test-resources-azure/test-resources-azure-cosmos/src/test/resources/logback.xml @@ -0,0 +1,15 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + +