diff --git a/README.md b/README.md index 570f32bcb0..c5dcd48a60 100644 --- a/README.md +++ b/README.md @@ -243,8 +243,41 @@ The service will respond with the inserted geo features: # Testing locally -To run tests locally run Gradle `cleanAndTest` task: +Naksha tests can currently run in two modes: + +1. Against a Postgres database that you start yourself (for example the containerized Postgres described above). +2. Against a Postgres container started automatically by the build via [Testcontainers](https://github.com/testcontainers). + +Before running Gradle, set the environment variables for the mode you want. + +### Option 1: Run tests against a local standalone Postgres + +```bash +export NAKSHA_APP_SERVICE_TEST_CONTEXT=LOCAL_STANDALONE +export NAKSHA_TEST_STORAGE_ID=local_psql_test_storage +export HUB_ADMIN_STORAGE_ID=local_psql_test_storage + +# For `here-naksha-lib-psql` tests. +export NAKSHA_TEST_PSQL_DB_URL="jdbc:postgresql://localhost:5432/postgres?user=postgres&password=password" + +# For `here-naksha-app-service` admin storage tests. +export NAKSHA_TEST_ADMIN_DB_URL="jdbc:postgresql://localhost:5432/postgres?user=postgres&password=password" + +# For `here-naksha-app-service` data storage tests. +# Kept separate from the admin DB URL to allow deployment-like test setups, +# where administrative entities are stored in a different database/storage than data entities. +export NAKSHA_TEST_DATA_DB_URL="jdbc:postgresql://localhost:5432/postgres?user=postgres&password=password" + +./gradlew cleanAndTest +``` + +### Option 2: Run tests with Testcontainers-managed Postgres + ```bash +export NAKSHA_APP_SERVICE_TEST_CONTEXT=TEST_CONTAINERS +export HUB_ADMIN_STORAGE_ID=local_psql_test_storage +export NAKSHA_TEST_STORAGE_ID=local_psql_test_storage + ./gradlew cleanAndTest ``` diff --git a/build.gradle.kts b/build.gradle.kts index 4c19d2edca..356d15722c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -78,6 +78,7 @@ val allModules = mapOf( Pair("naksha", Pair(CleanAndTest.OFF, PublishModule.CONFIG_ONLY)), Pair("here-naksha-app-service", Pair(CleanAndTest.KOTLIN, PublishModule.YES)), Pair("here-naksha-common-http", Pair(CleanAndTest.KOTLIN, PublishModule.YES)), + Pair("here-naksha-common-test", Pair(CleanAndTest.OFF, PublishModule.YES)), Pair("here-naksha-handler-activitylog", Pair(CleanAndTest.KOTLIN, PublishModule.YES)), Pair("here-naksha-lib-auth", Pair(CleanAndTest.KOTLIN, PublishModule.YES)), Pair("here-naksha-lib-base", Pair(CleanAndTest.KOTLIN, PublishModule.YES)), diff --git a/buildSrc/src/main/kotlin/Descriptions.kt b/buildSrc/src/main/kotlin/Descriptions.kt index db05b904ee..7be490f56c 100644 --- a/buildSrc/src/main/kotlin/Descriptions.kt +++ b/buildSrc/src/main/kotlin/Descriptions.kt @@ -6,6 +6,7 @@ import org.gradle.api.Project private val Descriptions = mapOf( "here-naksha-app-service" to "TBD", "here-naksha-common-http" to "TBD", + "here-naksha-common-test" to "TBD", "here-naksha-handler-activitylog" to "Naksha handler, adds downward compatibility to XYZ-Hub activity-log.", //"here-naksha-handler-http" to "TBD", "here-naksha-lib-auth" to "Naksha library, provides helper classes to perform authorization against Wikvaya UPM (User Permission Management) authorization matrix.", diff --git a/here-naksha-app-service/src/jvmTest/java/com/here/naksha/app/init/TestStorageConfig.java b/here-naksha-app-service/src/jvmTest/java/com/here/naksha/app/init/TestStorageConfig.java index a119d18645..0290b05e6f 100644 --- a/here-naksha-app-service/src/jvmTest/java/com/here/naksha/app/init/TestStorageConfig.java +++ b/here-naksha-app-service/src/jvmTest/java/com/here/naksha/app/init/TestStorageConfig.java @@ -1,9 +1,8 @@ package com.here.naksha.app.init; -import static com.here.naksha.lib.hub.NakshaHubAdminStorageIdentifiers.DEFAULT_HUB_ADMIN_STORAGE_ID; - import com.here.naksha.lib.core.util.IoHelp; import com.here.naksha.lib.core.util.IoHelp.LoadedBytes; +import com.here.naksha.lib.hub.NakshaHubAdminStorageIdentifiers; import java.nio.charset.StandardCharsets; import naksha.model.NakshaVersion; import naksha.psql.PgConfig; @@ -34,19 +33,19 @@ public TestStorageConfig(String mapId, PgConfig pgConfig) { final byte[] bytes = loadedBytes.getBytes(); String url = new String(bytes, StandardCharsets.UTF_8); if (url.startsWith("jdbc:postgresql://")) { - PgConfig pgConfig = new PgConfig(DEFAULT_HUB_ADMIN_STORAGE_ID).withMasterUri(url); + PgConfig pgConfig = new PgConfig(NakshaHubAdminStorageIdentifiers.getHubAdminStorageId()).withMasterUri(url); return new TestStorageConfig(mapId, pgConfig); } } catch (Exception ignore) { } String url = System.getenv(envName); if (url != null && url.startsWith("jdbc:postgresql://")) { - PgConfig pgConfig = new PgConfig(DEFAULT_HUB_ADMIN_STORAGE_ID).withMasterUri(url); + PgConfig pgConfig = new PgConfig(NakshaHubAdminStorageIdentifiers.getHubAdminStorageId()).withMasterUri(url); return new TestStorageConfig(mapId, pgConfig); } url = System.getenv("TEST_NAKSHA_PSQL_URL"); if (url != null && url.startsWith("jdbc:postgresql://")) { - PgConfig pgConfig = new PgConfig(DEFAULT_HUB_ADMIN_STORAGE_ID).withMasterUri(url); + PgConfig pgConfig = new PgConfig(NakshaHubAdminStorageIdentifiers.getHubAdminStorageId()).withMasterUri(url); return new TestStorageConfig(mapId, pgConfig); } @@ -57,7 +56,7 @@ public TestStorageConfig(String mapId, PgConfig pgConfig) { url = "jdbc:postgresql://localhost:5432/postgres?user=postgres&password=" + password + "&schema=" + mapId + "&app=" + "Naksha/v" + NakshaVersion.current; - PgConfig pgConfig = new PgConfig(DEFAULT_HUB_ADMIN_STORAGE_ID).withMasterUri(url); + PgConfig pgConfig = new PgConfig(NakshaHubAdminStorageIdentifiers.getHubAdminStorageId()).withMasterUri(url); return new TestStorageConfig(mapId, pgConfig); } } diff --git a/here-naksha-cli/build.gradle.kts b/here-naksha-cli/build.gradle.kts index 74ba80be55..b1f4bda049 100644 --- a/here-naksha-cli/build.gradle.kts +++ b/here-naksha-cli/build.gradle.kts @@ -50,6 +50,7 @@ kotlin { } jvmTest { dependencies { + implementation(project(":here-naksha-common-test")) implementation(libs.bundles.testing) implementation(libs.test.containers) runtimeOnly(libs.junit.platform.launcher) // https://github.com/gradle/gradle/issues/34512 diff --git a/here-naksha-cli/src/jvmTest/java/com/here/naksha/cli/copy/service/psql/PsqlCopyTest.java b/here-naksha-cli/src/jvmTest/java/com/here/naksha/cli/copy/service/psql/PsqlCopyTest.java index 6933943184..c510e0833d 100644 --- a/here-naksha-cli/src/jvmTest/java/com/here/naksha/cli/copy/service/psql/PsqlCopyTest.java +++ b/here-naksha-cli/src/jvmTest/java/com/here/naksha/cli/copy/service/psql/PsqlCopyTest.java @@ -1,6 +1,18 @@ package com.here.naksha.cli.copy.service.psql; -import com.here.naksha.cli.copy.service.*; +import static naksha.model.RandomFeatures.randomFeatures; +import static naksha.model.util.RequestHelper.createFeaturesRequest; +import static naksha.model.util.RequestHelper.createWriteCollectionsRequest; +import static naksha.model.util.ResultHelper.extractResponseItems; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertIterableEquals; + +import com.here.naksha.cli.copy.service.CopyElement; +import com.here.naksha.cli.copy.service.CopyService; +import com.here.naksha.cli.copy.service.CopyServiceException; +import com.here.naksha.cli.copy.service.CopyServiceSuccessResultPayload; +import com.here.naksha.cli.copy.service.StorageProvider; import com.here.naksha.cli.copy.service.executors.OneShotFeaturesWriteExecutor; import com.here.naksha.cli.copy.service.executors.ParallelFeaturesWriteExecutor; import com.here.naksha.cli.copy.service.executors.model.FeaturesWriteExecutor; @@ -8,9 +20,12 @@ import com.here.naksha.cli.results.CommandSuccess; import com.here.naksha.cli.storages.GeneratingStorage; import com.here.naksha.cli.storages.GeneratingStorageConfig; -import com.here.naksha.cli.testcontainers.TestContainersPsqlStorage; import com.here.naksha.lib.core.models.geojson.WebMercatorTile; +import java.util.List; +import java.util.UUID; +import java.util.stream.Stream; import naksha.base.StringList; +import naksha.common.test.CommonTestConstants; import naksha.model.IStorage; import naksha.model.Naksha; import naksha.model.NakshaContext; @@ -18,7 +33,14 @@ import naksha.model.objects.NakshaCollection; import naksha.model.objects.NakshaFeature; import naksha.model.objects.NakshaMap; -import naksha.model.request.*; +import naksha.model.objects.NakshaStorage; +import naksha.model.request.ReadFeatures; +import naksha.model.request.Request; +import naksha.model.request.RequestQuery; +import naksha.model.request.Response; +import naksha.model.request.SuccessResponse; +import naksha.model.request.Write; +import naksha.model.request.WriteRequest; import naksha.model.request.query.SpIntersects; import naksha.model.request.query.SpOr; import org.junit.jupiter.api.BeforeEach; @@ -26,25 +48,23 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import java.util.List; -import java.util.UUID; -import java.util.stream.Stream; - -import static naksha.model.RandomFeatures.randomFeatures; -import static naksha.model.util.RequestHelper.createFeaturesRequest; -import static naksha.model.util.RequestHelper.createWriteCollectionsRequest; -import static naksha.model.util.ResultHelper.extractResponseItems; -import static org.junit.jupiter.api.Assertions.*; - class PsqlCopyTest { private final String srcCollectionId = "srccolid"; private final String targetCollectionId = "targetcolid"; private SessionOptions sessionOptions; + private static final NakshaStorage storageConfig = NakshaStorage.fromJSON(""" + { + "id": "%s", + "className": "naksha.psql.PsqlTestStorage" + } + """.formatted(CommonTestConstants.getTestStorageId())); + private IStorage psqlStorage; @BeforeEach void beforeEach() { NakshaContext.currentContext().withAppId("testapp"); sessionOptions = SessionOptions.from(NakshaContext.currentContext()); + psqlStorage = Naksha.useStorage(storageConfig); } @ParameterizedTest @@ -58,7 +78,7 @@ void shouldCopyFeaturesBetweenGeneratingStorageAndPostgres(FeaturesWriteExecutor CopyElement source = copyElementForGeneratingStorage(sourceStorage); // And: prepared target - IStorage targetStorage = TestContainersPsqlStorage.getInstance().getStorage(); + IStorage targetStorage = psqlStorage; CopyElement target = createMapWithEmptyCollection(targetStorage, targetCollectionId); // And: copy service @@ -79,7 +99,7 @@ void shouldCopyFeaturesBetweenGeneratingStorageAndPostgres(FeaturesWriteExecutor @MethodSource("featuresWriteExecutors") void shouldCopyFeaturesBetweenMapsOnTheSameStorage(FeaturesWriteExecutor featuresWriteExecutor) { // Given: the same storage for source and target - IStorage storage = TestContainersPsqlStorage.getInstance().getStorage(); + IStorage storage = psqlStorage; // Given: prepared source CopyElement source = createMapWithEmptyCollection(storage, srcCollectionId); @@ -110,7 +130,7 @@ void shouldCopyFeaturesBetweenMapsOnTheSameStorage(FeaturesWriteExecutor feature @MethodSource("featuresWriteExecutors") void shouldCreateMapAndCollectionThenCopy(FeaturesWriteExecutor featuresWriteExecutor) { // Given: the same storage for source and target - IStorage storage = TestContainersPsqlStorage.getInstance().getStorage(); + IStorage storage = psqlStorage; // Given: prepared source CopyElement source = createMapWithEmptyCollection(storage, srcCollectionId); diff --git a/here-naksha-cli/src/jvmTest/java/com/here/naksha/cli/testcontainers/TestContainersPsqlStorage.java b/here-naksha-cli/src/jvmTest/java/com/here/naksha/cli/testcontainers/TestContainersPsqlStorage.java deleted file mode 100644 index 54bebed56c..0000000000 --- a/here-naksha-cli/src/jvmTest/java/com/here/naksha/cli/testcontainers/TestContainersPsqlStorage.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.here.naksha.cli.testcontainers; - -import naksha.model.IStorage; -import naksha.model.Naksha; -import naksha.model.NakshaContext; -import naksha.model.objects.NakshaStorage; -import naksha.psql.PgInstanceConfig; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.wait.strategy.Wait; -import org.testcontainers.containers.wait.strategy.WaitAllStrategy; - -import java.time.Duration; -import java.time.temporal.ChronoUnit; - -import static org.testcontainers.containers.wait.strategy.WaitAllStrategy.Mode.WITH_MAXIMUM_OUTER_TIMEOUT; - -public final class TestContainersPsqlStorage { - private final int exposedPort = 5432; - private final String postgresImageUri = "ghcr.io/naksha-oss/naksha-postgres:v16.2-r5"; - private final GenericContainer postgres = new GenericContainer<>(postgresImageUri); - private IStorage storage; - - public IStorage getStorage() { - return storage; - } - - public static TestContainersPsqlStorage getInstance() { - return Holder.INSTANCE; - } - - /** - * Should be called once before any operation. - */ - private void start() { - setUpPostgres(); - postgres.start(); - NakshaContext.currentContext().withAppId("testcontainer"); - storage = Naksha.useStorage(getNakshaStorage()); - } - - /** - * Should be called once after all operations. - */ - private void stop() { - postgres.stop(); - } - - private static final class Holder { - private static final TestContainersPsqlStorage INSTANCE = new TestContainersPsqlStorage(); - - static { - INSTANCE.start(); - Runtime.getRuntime().addShutdownHook( - new Thread(INSTANCE::stop) - ); - } - } - - private TestContainersPsqlStorage() { - } - - private NakshaStorage getNakshaStorage() { - return NakshaStorage.fromJSON( - """ - { - "id": "storage", - "type": "Storage", - "create": true, - "upgrade": true, - "className": "naksha.psql.PsqlStorage", - "master": { - "host": "%s", - "database": "%s", - "port": %s, - "user": "%s", - "password": "%s", - "readOnly": false - } - } - """.formatted( - postgres.getHost(), - PgInstanceConfig.DEFAULT_DB, - postgres.getMappedPort(exposedPort), - PgInstanceConfig.DEFAULT_USER, - PgInstanceConfig.DEFAULT_PASSWORD - ) - ); - } - - private void setUpPostgres() { - postgres.addExposedPort(exposedPort); - postgres.addEnv("PGPASSWORD", PgInstanceConfig.DEFAULT_PASSWORD); - postgres.setWaitStrategy( - new WaitAllStrategy(WITH_MAXIMUM_OUTER_TIMEOUT) - .withStartupTimeout(Duration.of(120, ChronoUnit.SECONDS)) - .withStrategy(Wait.forLogMessage(".*Future log output will appear in directory.*", 2)) - .withStrategy(Wait.forListeningPort()) - ); - } -} diff --git a/here-naksha-cli/src/jvmTest/java/com/here/naksha/cli/utils/JsonParserTest.java b/here-naksha-cli/src/jvmTest/java/com/here/naksha/cli/utils/JsonParserTest.java index 414edf53e4..e974852a02 100644 --- a/here-naksha-cli/src/jvmTest/java/com/here/naksha/cli/utils/JsonParserTest.java +++ b/here-naksha-cli/src/jvmTest/java/com/here/naksha/cli/utils/JsonParserTest.java @@ -10,6 +10,7 @@ import java.nio.file.Path; import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assumptions.assumeFalse; import static org.junit.jupiter.api.Assumptions.assumeTrue; class JsonParserTest { @@ -33,6 +34,7 @@ void shouldFailWithNoReadable(@TempDir Path dir) throws IOException { Files.writeString(pathToFile, "{}"); File file = pathToFile.toFile(); assumeTrue(file.setReadable(false), "Can not set file as unreadable!"); + assumeFalse(Files.isReadable(pathToFile), "File is still readable by the process, skipping test"); JsonParser jsonParser = new JsonParser(); // When & Then diff --git a/here-naksha-common-test/build.gradle.kts b/here-naksha-common-test/build.gradle.kts new file mode 100644 index 0000000000..b59be1e318 --- /dev/null +++ b/here-naksha-common-test/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + alias(libs.plugins.kotlin.multiplatform) +} + +description = gatherDescription() + +kotlin { + jvm { + compilerOptions { + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11) + } + } + js(IR) { + nodejs() + } +} diff --git a/here-naksha-common-test/src/commonMain/kotlin/naksha/common/test/CommonTestConstants.kt b/here-naksha-common-test/src/commonMain/kotlin/naksha/common/test/CommonTestConstants.kt new file mode 100644 index 0000000000..2a12cfcfbe --- /dev/null +++ b/here-naksha-common-test/src/commonMain/kotlin/naksha/common/test/CommonTestConstants.kt @@ -0,0 +1,14 @@ +package naksha.common.test + +import kotlin.jvm.JvmStatic + +object CommonTestConstants { + private const val DEFAULT_TEST_STORAGE_ID: String = "local_psql_test_storage" + private const val TEST_STORAGE_ID_ENV: String = "NAKSHA_TEST_STORAGE_ID" + + @JvmStatic + fun getTestStorageId(): String = + currentEnvironment()[TEST_STORAGE_ID_ENV]?.takeUnless { it.isBlank() } ?: DEFAULT_TEST_STORAGE_ID +} + +internal expect fun currentEnvironment(): Map diff --git a/here-naksha-common-test/src/jsMain/kotlin/naksha/common/test/CommonTestEnvironment.js.kt b/here-naksha-common-test/src/jsMain/kotlin/naksha/common/test/CommonTestEnvironment.js.kt new file mode 100644 index 0000000000..c77cae0be4 --- /dev/null +++ b/here-naksha-common-test/src/jsMain/kotlin/naksha/common/test/CommonTestEnvironment.js.kt @@ -0,0 +1,3 @@ +package naksha.common.test + +internal actual fun currentEnvironment(): Map = emptyMap() diff --git a/here-naksha-common-test/src/jvmMain/kotlin/naksha/common/test/CommonTestEnvironment.jvm.kt b/here-naksha-common-test/src/jvmMain/kotlin/naksha/common/test/CommonTestEnvironment.jvm.kt new file mode 100644 index 0000000000..91fd4e2454 --- /dev/null +++ b/here-naksha-common-test/src/jvmMain/kotlin/naksha/common/test/CommonTestEnvironment.jvm.kt @@ -0,0 +1,3 @@ +package naksha.common.test + +internal actual fun currentEnvironment(): Map = System.getenv() diff --git a/here-naksha-lib-hub/src/jvmMain/java/com/here/naksha/lib/hub/NakshaHub.java b/here-naksha-lib-hub/src/jvmMain/java/com/here/naksha/lib/hub/NakshaHub.java index 1e5e943ba4..2a9c2a708b 100644 --- a/here-naksha-lib-hub/src/jvmMain/java/com/here/naksha/lib/hub/NakshaHub.java +++ b/here-naksha-lib-hub/src/jvmMain/java/com/here/naksha/lib/hub/NakshaHub.java @@ -18,6 +18,19 @@ */ package com.here.naksha.lib.hub; +import static com.here.naksha.lib.core.HubInternalIdentifiers.ALL_HUB_INTERNAL_COLLECTIONS; +import static com.here.naksha.lib.core.HubInternalIdentifiers.CONFIGS; +import static com.here.naksha.lib.core.HubInternalIdentifiers.EVENT_HANDLERS; +import static com.here.naksha.lib.core.HubInternalIdentifiers.STORAGES; +import static com.here.naksha.lib.core.exceptions.UncheckedException.unchecked; +import static naksha.model.Action.CREATED; +import static naksha.model.NakshaContext.currentContext; +import static naksha.model.util.RequestHelper.createFeatureRequest; +import static naksha.model.util.RequestHelper.readFeaturesByIdRequest; +import static naksha.model.util.RequestHelper.readFeaturesByIdsRequest; +import static naksha.model.util.ResultHelper.extractResponseItems; +import static naksha.model.util.ResultHelper.readFeatureFromResponse; + import com.here.naksha.lib.core.AbstractTask; import com.here.naksha.lib.core.DefaultRequestLimitManager; import com.here.naksha.lib.core.INaksha; @@ -33,6 +46,12 @@ import com.here.naksha.lib.extmanager.helpers.FileClientFactory; import com.here.naksha.lib.hub.storages.NHAdminStorage; import com.here.naksha.lib.hub.storages.NHSpaceStorage; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; import naksha.base.FromJsonOptions; import naksha.base.JvmBoxingUtil; import naksha.base.JvmJsonUtil; @@ -59,7 +78,6 @@ import naksha.model.request.query.AnyOp; import naksha.model.request.query.IPropertyQuery; import naksha.model.request.query.PAnd; -import naksha.model.request.query.POr; import naksha.model.request.query.PQuery; import naksha.model.request.query.Property; import naksha.model.util.ResultHelper; @@ -70,28 +88,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; - -import static com.here.naksha.lib.core.HubInternalIdentifiers.ALL_HUB_INTERNAL_COLLECTIONS; -import static com.here.naksha.lib.core.HubInternalIdentifiers.CONFIGS; -import static com.here.naksha.lib.core.HubInternalIdentifiers.EVENT_HANDLERS; -import static com.here.naksha.lib.core.HubInternalIdentifiers.STORAGES; -import static com.here.naksha.lib.core.exceptions.UncheckedException.unchecked; -import static com.here.naksha.lib.hub.NakshaHubAdminStorageIdentifiers.DEFAULT_HUB_ADMIN_MAP_ID; -import static com.here.naksha.lib.hub.NakshaHubAdminStorageIdentifiers.DEFAULT_HUB_ADMIN_STORAGE_ID; -import static naksha.model.Action.CREATED; -import static naksha.model.NakshaContext.currentContext; -import static naksha.model.util.RequestHelper.createFeatureRequest; -import static naksha.model.util.RequestHelper.readFeaturesByIdRequest; -import static naksha.model.util.RequestHelper.readFeaturesByIdsRequest; -import static naksha.model.util.ResultHelper.extractResponseItems; -import static naksha.model.util.ResultHelper.readFeatureFromResponse; - public class NakshaHub implements INaksha { /** @@ -138,8 +134,8 @@ public NakshaHub( final @Nullable NakshaHubConfig customCfg, final @Nullable String configId) { this( - DEFAULT_HUB_ADMIN_MAP_ID, - DEFAULT_HUB_ADMIN_STORAGE_ID, + NakshaHubAdminStorageIdentifiers.getHubAdminMapId(), + NakshaHubAdminStorageIdentifiers.getHubAdminStorageId(), adminPgMasterUrl, customCfg, configId @@ -176,7 +172,7 @@ public NakshaHub( this.spaceStorageInstance = new NHSpaceStorage(this, new NakshaEventPipelineFactory(this)); // setup backend storage DB and Hub config NakshaContext nakshaContext = setupMapAndContext(adminMapId); - final NakshaHubConfig finalCfg = this.storageSetup(customCfg, configId, nakshaContext); + final NakshaHubConfig finalCfg = this.storageSetup(customCfg, configId, nakshaContext, adminMapId); if (finalCfg == null) { throw new RuntimeException("Server configuration not found! Neither in Admin storage nor a default file."); } @@ -222,19 +218,19 @@ private NakshaContext setupMapAndContext(String mapId) { private @Nullable NakshaHubConfig storageSetup( final @Nullable NakshaHubConfig customCfg, final @Nullable String configId, - final @NotNull NakshaContext nakshaContext - ) { + final @NotNull NakshaContext nakshaContext, + final @NotNull String adminMapId) { // 1. Create all Admin collections in Admin DB - createAdminCollections(nakshaContext); + createAdminCollections(nakshaContext, adminMapId); // 2. fetch / add latest config - return configSetup(nakshaContext, customCfg, configId); + return configSetup(nakshaContext, customCfg, configId, adminMapId); } - private void createAdminCollections(NakshaContext nakshaContext) { + private void createAdminCollections(NakshaContext nakshaContext, String adminMapId) { getAdminStorage().runInWriteSession(SessionOptions.from(nakshaContext, true), admin -> { logger.info("WriteCollections Request for {}, against Admin storage.", ALL_HUB_INTERNAL_COLLECTIONS); - final Response createAdminCollectionsResponse = admin.execute(upsertAdminCollectionsRequest()); + final Response createAdminCollectionsResponse = admin.execute(upsertAdminCollectionsRequest(adminMapId)); if (createAdminCollectionsResponse instanceof SuccessResponse successResponse) { NakshaFeatureList createdCollections = successResponse.getFeatures(); for (NakshaFeature createdCollection : createdCollections) { @@ -257,10 +253,10 @@ private void createAdminCollections(NakshaContext nakshaContext) { }); } - private static WriteRequest upsertAdminCollectionsRequest() { + private static WriteRequest upsertAdminCollectionsRequest(@NotNull String adminMapId) { final WriteRequest writeRequest = new WriteRequest(); for (String adminCollectionId : ALL_HUB_INTERNAL_COLLECTIONS) { - writeRequest.add(new Write().upsertCollection(new NakshaCollection(adminCollectionId, DEFAULT_HUB_ADMIN_MAP_ID))); + writeRequest.add(new Write().upsertCollection(new NakshaCollection(adminCollectionId, adminMapId))); } return writeRequest; } @@ -268,7 +264,8 @@ private static WriteRequest upsertAdminCollectionsRequest() { private @Nullable NakshaHubConfig configSetup( final @NotNull NakshaContext nakshaContext, final @Nullable NakshaHubConfig customCfg, - final @Nullable String configId) { + final @Nullable String configId, + final @NotNull String adminMapId) { /* * Config preference, for a given configId (e.g. "custom-config"): * 1. Custom config - If provided, persist the same in DB, and use the same for NakshaHub @@ -280,7 +277,7 @@ private static WriteRequest upsertAdminCollectionsRequest() { return getAdminStorage().useWriteSession(SessionOptions.from(nakshaContext, true), admin -> { if (customCfg != null) { WriteRequest writeCustomCfg = new WriteRequest() - .add(new Write().upsertFeature(DEFAULT_HUB_ADMIN_MAP_ID, CONFIGS, customCfg)); + .add(new Write().upsertFeature(adminMapId, CONFIGS, customCfg)); Response writeCustomCfgResponse = admin.execute(writeCustomCfg); if (writeCustomCfgResponse instanceof SuccessResponse) { admin.commit(); diff --git a/here-naksha-lib-hub/src/jvmMain/java/com/here/naksha/lib/hub/NakshaHubAdminStorageIdentifiers.java b/here-naksha-lib-hub/src/jvmMain/java/com/here/naksha/lib/hub/NakshaHubAdminStorageIdentifiers.java index 6fddf59d1b..e743059fca 100644 --- a/here-naksha-lib-hub/src/jvmMain/java/com/here/naksha/lib/hub/NakshaHubAdminStorageIdentifiers.java +++ b/here-naksha-lib-hub/src/jvmMain/java/com/here/naksha/lib/hub/NakshaHubAdminStorageIdentifiers.java @@ -1,8 +1,57 @@ package com.here.naksha.lib.hub; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class NakshaHubAdminStorageIdentifiers { - private NakshaHubAdminStorageIdentifiers() {} - public static String DEFAULT_HUB_ADMIN_STORAGE_ID = "naksha-hub-admin-storage"; - public static String DEFAULT_HUB_ADMIN_MAP_ID = "naksha-hub-admin"; + private static final Logger logger = LoggerFactory.getLogger(NakshaHubAdminStorageIdentifiers.class); + + private NakshaHubAdminStorageIdentifiers() { + } + + private static final String HUB_ADMIN_STORAGE_ENV_NAME = "HUB_ADMIN_STORAGE_ID"; + private static final String HUB_ADMIN_MAP_ENV_NAME = "HUB_ADMIN_MAP_ID"; + private static final String DEFAULT_HUB_ADMIN_STORAGE_ID = "naksha-hub-admin-storage"; + private static final String DEFAULT_HUB_ADMIN_MAP_ID = "naksha-hub-admin"; + + /** + * Returns the Hub Admin Storage ID, based on the environment variable {@code HUB_ADMIN_STORAGE_ID}. If the environment variable is not + * set, the default value {@code naksha-hub-admin-storage} is returned. + */ + public static String getHubAdminStorageId() { + final String envValue = System.getenv(HUB_ADMIN_STORAGE_ENV_NAME); + if (envValue == null) { + logger.info( + "Environment variable {} is not set. Using default value for Hub Admin StorageID: {}", + HUB_ADMIN_STORAGE_ENV_NAME, DEFAULT_HUB_ADMIN_STORAGE_ID + ); + return DEFAULT_HUB_ADMIN_STORAGE_ID; + } else { + logger.info( + "Environment variable {} is set. Using value for Hub Admin StorageID: {}", + HUB_ADMIN_STORAGE_ENV_NAME, envValue); + return envValue; + } + } + + /** + * Returns the Hub Admin Map ID, based on the environment variable {@code HUB_ADMIN_MAP_ID}. If the environment variable is not set, the + * default value {@code naksha-hub-admin} is returned. + */ + public static String getHubAdminMapId() { + final String envValue = System.getenv(HUB_ADMIN_MAP_ENV_NAME); + if (envValue == null) { + logger.info( + "Environment variable {} is not set. Using default value for Hub Admin MapID: {}", + HUB_ADMIN_MAP_ENV_NAME, DEFAULT_HUB_ADMIN_MAP_ID + ); + return DEFAULT_HUB_ADMIN_MAP_ID; + } else { + logger.info( + "Environment variable {} is set. Using value for Hub Admin MapID: {}", + HUB_ADMIN_MAP_ENV_NAME, envValue); + return envValue; + } + } } diff --git a/here-naksha-lib-hub/src/jvmTest/java/com/here/naksha/lib/hub/mock/NHAdminMock.java b/here-naksha-lib-hub/src/jvmTest/java/com/here/naksha/lib/hub/mock/NHAdminMock.java index fdb11de14b..d38483c58e 100644 --- a/here-naksha-lib-hub/src/jvmTest/java/com/here/naksha/lib/hub/mock/NHAdminMock.java +++ b/here-naksha-lib-hub/src/jvmTest/java/com/here/naksha/lib/hub/mock/NHAdminMock.java @@ -21,7 +21,6 @@ import static com.here.naksha.lib.core.HubInternalIdentifiers.ALL_HUB_INTERNAL_COLLECTIONS; import static com.here.naksha.lib.core.HubInternalIdentifiers.CONFIGS; import static com.here.naksha.lib.core.exceptions.UncheckedException.unchecked; -import static com.here.naksha.lib.hub.NakshaHubAdminStorageIdentifiers.DEFAULT_HUB_ADMIN_MAP_ID; import static naksha.model.util.RequestHelper.createFeatureRequest; import com.here.naksha.lib.hub.NakshaHubAdminStorageIdentifiers; @@ -104,7 +103,8 @@ private void setupConfig() { final NakshaContext ctx = NakshaContext.newInstance("naksha_mock"); ctx.attachToCurrentThread(); runInWriteSession(SessionOptions.from(ctx, true), admin -> { - final Response response = admin.execute(createFeatureRequest(DEFAULT_HUB_ADMIN_MAP_ID, CONFIGS, nakshaHubConfig)); + final Response response = admin.execute( + createFeatureRequest(NakshaHubAdminStorageIdentifiers.getHubAdminMapId(), CONFIGS, nakshaHubConfig)); if (response instanceof ErrorResponse errorResponse) { admin.rollback(); throw unchecked( @@ -134,7 +134,7 @@ private void setupCollections() { runInWriteSession(SessionOptions.from(ctx, true), admin -> { WriteRequest writeAdminCollections = new WriteRequest(); for (final String name : ALL_HUB_INTERNAL_COLLECTIONS) { - Write write = new Write().createCollection(new NakshaCollection(name, DEFAULT_HUB_ADMIN_MAP_ID)); + Write write = new Write().createCollection(new NakshaCollection(name, NakshaHubAdminStorageIdentifiers.getHubAdminMapId())); writeAdminCollections.add(write); } final Response response = admin.execute(writeAdminCollections); diff --git a/here-naksha-lib-psql/build.gradle.kts b/here-naksha-lib-psql/build.gradle.kts index 58352df531..883f7c5430 100644 --- a/here-naksha-lib-psql/build.gradle.kts +++ b/here-naksha-lib-psql/build.gradle.kts @@ -57,6 +57,7 @@ kotlin { } commonTest { dependencies { + implementation(project(":here-naksha-common-test")) implementation(kotlin("test")) implementation(kotlin("test-common")) implementation(kotlin("test-annotations-common")) diff --git a/here-naksha-lib-psql/src/commonTest/kotlin/naksha/psql/PgTestBase.kt b/here-naksha-lib-psql/src/commonTest/kotlin/naksha/psql/PgTestBase.kt index dc9259ca3a..a9512e27fd 100644 --- a/here-naksha-lib-psql/src/commonTest/kotlin/naksha/psql/PgTestBase.kt +++ b/here-naksha-lib-psql/src/commonTest/kotlin/naksha/psql/PgTestBase.kt @@ -7,6 +7,7 @@ import naksha.base.Platform import naksha.base.Platform.PlatformCompanion.logger import naksha.base.PlatformUtil import naksha.base.proxy +import naksha.common.test.CommonTestConstants import naksha.model.* import naksha.model.objects.NakshaCollection import naksha.model.objects.NakshaFeature @@ -337,7 +338,7 @@ abstract class PgTestBase( @JvmStatic @JsStatic val storageConfig = NakshaStorage.fromJSON("""{ - "id": "local_psql_test_storage", + "id": "${CommonTestConstants.getTestStorageId()}", "className": "naksha.psql.PsqlTestStorage" }""").proxy(PgConfig::class) diff --git a/here-naksha-lib-psql/src/commonTest/kotlin/naksha/psql/UpgradeStorageTest.kt b/here-naksha-lib-psql/src/commonTest/kotlin/naksha/psql/UpgradeStorageTest.kt index d130489894..c9b85be27c 100644 --- a/here-naksha-lib-psql/src/commonTest/kotlin/naksha/psql/UpgradeStorageTest.kt +++ b/here-naksha-lib-psql/src/commonTest/kotlin/naksha/psql/UpgradeStorageTest.kt @@ -1,6 +1,7 @@ package naksha.psql import naksha.base.Int64 +import naksha.common.test.CommonTestConstants import naksha.model.Naksha import naksha.model.NakshaVersion import naksha.model.objects.NakshaStorage @@ -26,7 +27,7 @@ class UpgradeStorageTest : PgTestBase() { // Downgrade storage, we need `override` instruction for this. val downgradeVersion = NakshaVersion.of("1.0.0") val downgradeConfig = NakshaStorage.fromJSON("""{ - "id": "local_psql_test_storage", + "id": "${CommonTestConstants.getTestStorageId()}", "className": "naksha.psql.PsqlTestStorage", "version": "$downgradeVersion", "override": true diff --git a/here-naksha-lib-view/build.gradle.kts b/here-naksha-lib-view/build.gradle.kts index 1d95f6958d..a3209bac16 100644 --- a/here-naksha-lib-view/build.gradle.kts +++ b/here-naksha-lib-view/build.gradle.kts @@ -26,6 +26,7 @@ kotlin { } jvmTest { dependencies { + implementation(project(":here-naksha-common-test")) implementation(libs.bundles.testing) implementation(project(":here-naksha-lib-model")) implementation(project(":here-naksha-lib-psql")) diff --git a/here-naksha-lib-view/src/jvmTest/java/com/here/naksha/lib/view/PsqlTests.java b/here-naksha-lib-view/src/jvmTest/java/com/here/naksha/lib/view/PsqlTests.java index bc25702f4c..83e84c2c5b 100644 --- a/here-naksha-lib-view/src/jvmTest/java/com/here/naksha/lib/view/PsqlTests.java +++ b/here-naksha-lib-view/src/jvmTest/java/com/here/naksha/lib/view/PsqlTests.java @@ -18,6 +18,7 @@ */ package com.here.naksha.lib.view; +import naksha.common.test.CommonTestConstants; import naksha.base.Platform; import naksha.model.IStorage; import naksha.model.Naksha; @@ -95,8 +96,10 @@ static void beforeTest() { nakshaContext = NakshaContext.currentContext().withAppId(TEST_APP_ID).withAuthor(TEST_AUTHOR).withSu(true); storage = Naksha.useStorage( NakshaStorage.fromJSON( - "{\"id\":\"local_psql_test_storage\",\"className\":\"naksha.psql.PsqlTestStorage\"}" - ) + String.format( + "{\"id\":\"%s\",\"className\":\"naksha.psql.PsqlTestStorage\"}", + CommonTestConstants.getTestStorageId()) + ) ); assertNotNull(storage); diff --git a/settings.gradle.kts b/settings.gradle.kts index 3aeb2e484a..f09bb4ac5d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -23,6 +23,7 @@ include(":here-naksha-lib-handlers") include(":here-naksha-lib-hub") include(":here-naksha-lib-view") include(":here-naksha-common-http") +include(":here-naksha-common-test") include(":here-naksha-storage-http") include(":here-naksha-app-service") include(":here-naksha-lib-ext-manager")