diff --git a/ingest-v2/src/test/java/com/microsoft/azure/kusto/ingest/v2/CachingTokenCredential.java b/ingest-v2/src/test/java/com/microsoft/azure/kusto/ingest/v2/CachingTokenCredential.java new file mode 100644 index 00000000..c64e0d8a --- /dev/null +++ b/ingest-v2/src/test/java/com/microsoft/azure/kusto/ingest/v2/CachingTokenCredential.java @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +package com.microsoft.azure.kusto.ingest.v2; + +import com.azure.core.credential.AccessToken; +import com.azure.core.credential.TokenCredential; +import com.azure.core.credential.TokenRequestContext; +import com.azure.identity.AzureCliCredentialBuilder; +import reactor.core.publisher.Mono; + +/** + * Test-only credential: calls {@code az} once at class load, reuses the token forever. + */ +public class CachingTokenCredential implements TokenCredential { + + private static final AccessToken TOKEN; + + static { + // Runs once on main thread before any reactor threads — .block() is safe here. + TOKEN = new AzureCliCredentialBuilder().build() + .getToken(new TokenRequestContext().addScopes("https://kusto.kusto.windows.net/.default")) + .block(); + } + + public static final TokenCredential INSTANCE = new CachingTokenCredential(); + + @Override + public Mono getToken(TokenRequestContext request) { + return Mono.just(TOKEN); + } +} \ No newline at end of file diff --git a/ingest-v2/src/test/java/com/microsoft/azure/kusto/ingest/v2/IngestV2JavaTestBase.java b/ingest-v2/src/test/java/com/microsoft/azure/kusto/ingest/v2/IngestV2JavaTestBase.java index ad536b6e..8227f4b7 100644 --- a/ingest-v2/src/test/java/com/microsoft/azure/kusto/ingest/v2/IngestV2JavaTestBase.java +++ b/ingest-v2/src/test/java/com/microsoft/azure/kusto/ingest/v2/IngestV2JavaTestBase.java @@ -4,7 +4,6 @@ package com.microsoft.azure.kusto.ingest.v2; import com.azure.core.credential.TokenCredential; -import com.azure.identity.AzureCliCredentialBuilder; import com.microsoft.azure.kusto.data.Client; import com.microsoft.azure.kusto.data.ClientFactory; import com.microsoft.azure.kusto.data.KustoResultSetTable; @@ -40,7 +39,8 @@ public abstract class IngestV2JavaTestBase { public IngestV2JavaTestBase(Class testClass) { this.logger = LoggerFactory.getLogger(testClass); - this.tokenProvider = new AzureCliCredentialBuilder().build(); + // Reuse the shared CachingTokenCredential singleton to prevent multiple concurrent requests to fetch token from azcli + this.tokenProvider = CachingTokenCredential.INSTANCE; // Get configuration from environment variables this.database = System.getenv("TEST_DATABASE") != null @@ -116,9 +116,10 @@ public void createTables() throws Exception { } mappingBuilder.append("\n]```"); - // Create admin client + // Create admin client using the shared CachingTokenCredential to prevent + // concurrent az subprocess invocations from parallel test threads adminClusterClient = ClientFactory.createClient( - ConnectionStringBuilder.createWithAzureCli(engineEndpoint) + ConnectionStringBuilder.createWithTokenCredential(engineEndpoint, tokenProvider) ); // Execute table creation and mapping diff --git a/ingest-v2/src/test/kotlin/com/microsoft/azure/kusto/ingest/v2/IngestV2TestBase.kt b/ingest-v2/src/test/kotlin/com/microsoft/azure/kusto/ingest/v2/IngestV2TestBase.kt index a0e64ae9..d2ffcd78 100644 --- a/ingest-v2/src/test/kotlin/com/microsoft/azure/kusto/ingest/v2/IngestV2TestBase.kt +++ b/ingest-v2/src/test/kotlin/com/microsoft/azure/kusto/ingest/v2/IngestV2TestBase.kt @@ -3,7 +3,6 @@ package com.microsoft.azure.kusto.ingest.v2 import com.azure.core.credential.TokenCredential -import com.azure.identity.AzureCliCredentialBuilder import com.microsoft.azure.kusto.data.Client import com.microsoft.azure.kusto.data.ClientFactory import com.microsoft.azure.kusto.data.auth.ConnectionStringBuilder @@ -21,8 +20,11 @@ import kotlin.test.assertTrue abstract class IngestV2TestBase(testClass: Class<*>) { protected val logger: Logger = LoggerFactory.getLogger(testClass) - protected val tokenProvider: TokenCredential = - AzureCliCredentialBuilder().build() + + // Shared across all test class instances via CachingTokenCredential singleton. + // Ensures az account get-access-token is invoked only once per scope. + protected val tokenProvider: TokenCredential = CachingTokenCredential.INSTANCE + protected val database = System.getenv("TEST_DATABASE") ?: "e2e" protected val dmEndpoint: String = System.getenv("DM_CONNECTION_STRING") @@ -75,8 +77,9 @@ abstract class IngestV2TestBase(testClass: Class<*>) { .trimIndent() adminClusterClient = ClientFactory.createClient( - ConnectionStringBuilder.createWithAzureCli( + ConnectionStringBuilder.createWithTokenCredential( engineEndpoint, + tokenProvider, ), ) adminClusterClient.executeMgmt(database, createTableScript) @@ -157,4 +160,4 @@ abstract class IngestV2TestBase(testClass: Class<*>) { ) } } -} +} \ No newline at end of file