From 9f00da073b3cdc44d0fe683388e3e699c72e9597 Mon Sep 17 00:00:00 2001 From: Jose Alvarez Date: Thu, 8 Jan 2026 14:57:31 +0100 Subject: [PATCH 1/6] Fixed exception mapping for agents --- .../ai/agents/implementation/http/HttpClientHelper.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sdk/ai/azure-ai-agents/src/main/java/com/azure/ai/agents/implementation/http/HttpClientHelper.java b/sdk/ai/azure-ai-agents/src/main/java/com/azure/ai/agents/implementation/http/HttpClientHelper.java index 8052a17e5902..ee3f3e350ce3 100644 --- a/sdk/ai/azure-ai-agents/src/main/java/com/azure/ai/agents/implementation/http/HttpClientHelper.java +++ b/sdk/ai/azure-ai-agents/src/main/java/com/azure/ai/agents/implementation/http/HttpClientHelper.java @@ -3,6 +3,7 @@ package com.azure.ai.agents.implementation.http; +import com.azure.core.exception.AzureException; import com.azure.core.exception.HttpResponseException; import com.azure.core.http.HttpHeaderName; import com.azure.core.http.HttpHeaders; @@ -169,7 +170,11 @@ private static Throwable mapAzureExceptionToOpenAI(Throwable throwable) { } else if (throwable instanceof TimeoutException) { return throwable; } else { - return new OpenAIException(throwable.getMessage(), throwable); + if (throwable instanceof AzureException) { + return new OpenAIException(throwable.getMessage(), throwable.getCause()); + } else { + return new OpenAIException(throwable.getMessage(), throwable); + } } } From b22b9af8e1c6f4a72ca5196983788d1098d0c68b Mon Sep 17 00:00:00 2001 From: Jose Alvarez Date: Thu, 8 Jan 2026 15:41:08 +0100 Subject: [PATCH 2/6] Ported classes over for http pipeline adapter --- .../ai/projects/AIProjectClientBuilder.java | 30 +-- .../http/AzureHttpResponseAdapter.java | 63 +++++ .../implementation/http/HttpClientHelper.java | 252 ++++++++++++++++++ 3 files changed, 327 insertions(+), 18 deletions(-) create mode 100644 sdk/ai/azure-ai-projects/src/main/java/com/azure/ai/projects/implementation/http/AzureHttpResponseAdapter.java create mode 100644 sdk/ai/azure-ai-projects/src/main/java/com/azure/ai/projects/implementation/http/HttpClientHelper.java diff --git a/sdk/ai/azure-ai-projects/src/main/java/com/azure/ai/projects/AIProjectClientBuilder.java b/sdk/ai/azure-ai-projects/src/main/java/com/azure/ai/projects/AIProjectClientBuilder.java index 1759eeab6983..1bcf18e1065c 100644 --- a/sdk/ai/azure-ai-projects/src/main/java/com/azure/ai/projects/AIProjectClientBuilder.java +++ b/sdk/ai/azure-ai-projects/src/main/java/com/azure/ai/projects/AIProjectClientBuilder.java @@ -5,6 +5,7 @@ import com.azure.ai.projects.implementation.AIProjectClientImpl; import com.azure.ai.projects.implementation.TokenUtils; +import com.azure.ai.projects.implementation.http.HttpClientHelper; import com.azure.core.annotation.Generated; import com.azure.core.annotation.ServiceClientBuilder; import com.azure.core.client.traits.ConfigurationTrait; @@ -32,7 +33,6 @@ import com.azure.core.util.ClientOptions; import com.azure.core.util.Configuration; import com.azure.core.util.CoreUtils; -import com.azure.core.util.UserAgentUtil; import com.azure.core.util.builder.ClientBuilderUtil; import com.azure.core.util.logging.ClientLogger; import com.azure.core.util.serializer.JacksonAdapter; @@ -41,7 +41,7 @@ import com.openai.client.okhttp.OpenAIOkHttpClient; import com.openai.client.okhttp.OpenAIOkHttpClientAsync; import com.openai.credential.BearerTokenCredential; -import java.time.Duration; + import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -544,8 +544,9 @@ public SchedulesClient buildSchedulesClient() { * @return an instance of EvaluationsClient */ public EvaluationsClient buildEvaluationsClient() { - OpenAIOkHttpClient.Builder builder = getOpenAIClientBuilder(); - return new EvaluationsClient(builder.build()); + return new EvaluationsClient(getOpenAIClientBuilder().build() + .withOptions(optionBuilder -> optionBuilder + .httpClient(HttpClientHelper.mapToOpenAIHttpClient(createHttpPipeline())))); } /** @@ -554,7 +555,9 @@ public EvaluationsClient buildEvaluationsClient() { * @return an instance of EvaluationsClientAsync */ public EvaluationsAsyncClient buildEvaluationsAsyncClient() { - return new EvaluationsAsyncClient(getOpenAIAsyncClientBuilder().build()); + return new EvaluationsAsyncClient(getOpenAIAsyncClientBuilder().build() + .withOptions(optionBuilder -> optionBuilder + .httpClient(HttpClientHelper.mapToOpenAIHttpClient(createHttpPipeline())))); } private OpenAIOkHttpClient.Builder getOpenAIClientBuilder() { @@ -562,12 +565,12 @@ private OpenAIOkHttpClient.Builder getOpenAIClientBuilder() { .credential( BearerTokenCredential.create(TokenUtils.getBearerTokenSupplier(this.tokenCredential, DEFAULT_SCOPES))); builder.baseUrl(this.endpoint + (this.endpoint.endsWith("/") ? "openai" : "/openai")); - builder.replaceHeaders("User-Agent", getUserAgent()); if (this.serviceVersion != null) { builder.azureServiceVersion(AzureOpenAIServiceVersion.fromString(this.serviceVersion.getVersion())); builder.azureUrlPathMode(AzureUrlPathMode.UNIFIED); } - builder.timeout(Duration.ofSeconds(30)); + // We set the builder retries to 0 to avoid conflicts with the retry policy added through the HttpPipeline. + builder.maxRetries(0); return builder; } @@ -576,23 +579,14 @@ private OpenAIOkHttpClientAsync.Builder getOpenAIAsyncClientBuilder() { .credential( BearerTokenCredential.create(TokenUtils.getBearerTokenSupplier(this.tokenCredential, DEFAULT_SCOPES))); builder.baseUrl(this.endpoint + (this.endpoint.endsWith("/") ? "openai" : "/openai")); - builder.replaceHeaders("User-Agent", getUserAgent()); if (this.serviceVersion != null) { builder.azureServiceVersion(AzureOpenAIServiceVersion.fromString(this.serviceVersion.getVersion())); builder.azureUrlPath(AzureUrlPathMode.UNIFIED); } - builder.timeout(Duration.ofSeconds(30)); + // We set the builder retries to 0 to avoid conflicts with the retry policy added through the HttpPipeline. + builder.maxRetries(0); return builder; } - private String getUserAgent() { - HttpLogOptions localHttpLogOptions = this.httpLogOptions == null ? new HttpLogOptions() : this.httpLogOptions; - ClientOptions localClientOptions = this.clientOptions == null ? new ClientOptions() : this.clientOptions; - String sdkName = PROPERTIES.getOrDefault(SDK_NAME, "UnknownName"); - String sdkVersion = PROPERTIES.getOrDefault(SDK_VERSION, "UnknownVersion"); - String applicationId = CoreUtils.getApplicationId(localClientOptions, localHttpLogOptions); - return UserAgentUtil.toUserAgentString(applicationId, sdkName, sdkVersion, configuration); - } - private static final ClientLogger LOGGER = new ClientLogger(AIProjectClientBuilder.class); } diff --git a/sdk/ai/azure-ai-projects/src/main/java/com/azure/ai/projects/implementation/http/AzureHttpResponseAdapter.java b/sdk/ai/azure-ai-projects/src/main/java/com/azure/ai/projects/implementation/http/AzureHttpResponseAdapter.java new file mode 100644 index 000000000000..84db64020502 --- /dev/null +++ b/sdk/ai/azure-ai-projects/src/main/java/com/azure/ai/projects/implementation/http/AzureHttpResponseAdapter.java @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.ai.projects.implementation.http; + +import com.azure.core.http.HttpHeader; +import com.azure.core.http.HttpHeaders; +import com.azure.core.util.logging.ClientLogger; +import com.openai.core.http.Headers; +import com.openai.core.http.HttpResponse; + +import java.io.InputStream; + +/** + * Adapter that exposes an Azure {@link com.azure.core.http.HttpResponse} as an OpenAI {@link HttpResponse}. This keeps + * the translation logic encapsulated so response handling elsewhere can remain framework agnostic. + */ +final class AzureHttpResponseAdapter implements HttpResponse { + + private static final ClientLogger LOGGER = new ClientLogger(AzureHttpResponseAdapter.class); + + private final com.azure.core.http.HttpResponse azureResponse; + + /** + * Creates a new adapter instance for the provided Azure response. + * + * @param azureResponse Response returned by the Azure pipeline. + */ + AzureHttpResponseAdapter(com.azure.core.http.HttpResponse azureResponse) { + this.azureResponse = azureResponse; + } + + @Override + public int statusCode() { + return azureResponse.getStatusCode(); + } + + @Override + public Headers headers() { + return toOpenAiHeaders(azureResponse.getHeaders()); + } + + @Override + public InputStream body() { + return azureResponse.getBodyAsBinaryData().toStream(); + } + + @Override + public void close() { + azureResponse.close(); + } + + /** + * Copies headers from the Azure response into the immutable OpenAI {@link Headers} collection. + */ + private static Headers toOpenAiHeaders(HttpHeaders httpHeaders) { + Headers.Builder builder = Headers.builder(); + for (HttpHeader header : httpHeaders) { + builder.put(header.getName(), header.getValuesList()); + } + return builder.build(); + } +} diff --git a/sdk/ai/azure-ai-projects/src/main/java/com/azure/ai/projects/implementation/http/HttpClientHelper.java b/sdk/ai/azure-ai-projects/src/main/java/com/azure/ai/projects/implementation/http/HttpClientHelper.java new file mode 100644 index 000000000000..8aae7cf6724f --- /dev/null +++ b/sdk/ai/azure-ai-projects/src/main/java/com/azure/ai/projects/implementation/http/HttpClientHelper.java @@ -0,0 +1,252 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.ai.projects.implementation.http; + +import com.azure.core.exception.AzureException; +import com.azure.core.exception.HttpResponseException; +import com.azure.core.http.HttpHeaderName; +import com.azure.core.http.HttpHeaders; +import com.azure.core.http.HttpMethod; +import com.azure.core.http.HttpPipeline; +import com.azure.core.util.BinaryData; +import com.azure.core.util.Context; +import com.azure.core.util.CoreUtils; +import com.azure.core.util.logging.ClientLogger; +import com.openai.core.RequestOptions; +import com.openai.core.Timeout; +import com.openai.core.http.Headers; +import com.openai.core.http.HttpClient; +import com.openai.core.http.HttpRequest; +import com.openai.core.http.HttpRequestBody; +import com.openai.core.http.HttpResponse; +import com.openai.errors.BadRequestException; +import com.openai.errors.InternalServerException; +import com.openai.errors.NotFoundException; +import com.openai.errors.OpenAIException; +import com.openai.errors.PermissionDeniedException; +import com.openai.errors.RateLimitException; +import com.openai.errors.UnauthorizedException; +import com.openai.errors.UnexpectedStatusCodeException; +import com.openai.errors.UnprocessableEntityException; +import reactor.core.publisher.Mono; + +import java.io.ByteArrayOutputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeoutException; + +/** + * Utility entry point that adapts an Azure {@link com.azure.core.http.HttpClient} so it can be consumed by + * the OpenAI SDK generated clients. The helper performs request/response translation so that existing Azure + * pipelines, diagnostics, and retry policies can be reused without exposing the Azure HTTP primitives to + * callers that only understand the OpenAI surface area. + */ +public final class HttpClientHelper { + + private static final ClientLogger LOGGER = new ClientLogger(HttpClientHelper.class); + + private HttpClientHelper() { + } + + /** + * Implements the OpenAI {@link HttpClient} interface that sends the HTTP request through the Azure HTTP pipeline. + * All requests and responses are converted on the fly. + * + * @param httpPipeline The Azure HTTP pipeline that will execute HTTP requests. + * @return A bridge client that honors the OpenAI interface but delegates execution to the Azure pipeline. + */ + public static HttpClient mapToOpenAIHttpClient(HttpPipeline httpPipeline) { + return new HttpClientWrapper(httpPipeline); + } + + private static final class HttpClientWrapper implements HttpClient { + + private final HttpPipeline httpPipeline; + + private HttpClientWrapper(HttpPipeline httpPipeline) { + this.httpPipeline = Objects.requireNonNull(httpPipeline, "'httpPipeline' cannot be null."); + } + + @Override + public void close() { + // no-op + } + + @Override + public HttpResponse execute(HttpRequest request) { + return execute(request, RequestOptions.none()); + } + + @Override + public HttpResponse execute(HttpRequest request, RequestOptions requestOptions) { + Objects.requireNonNull(request, "request"); + Objects.requireNonNull(requestOptions, "requestOptions"); + + try { + com.azure.core.http.HttpRequest azureRequest = buildAzureRequest(request); + return new AzureHttpResponseAdapter( + this.httpPipeline.sendSync(azureRequest, buildRequestContext(requestOptions))); + } catch (MalformedURLException exception) { + throw new OpenAIException("Invalid URL in request: " + exception.getMessage(), + LOGGER.logThrowableAsError(exception)); + } + } + + @Override + public CompletableFuture executeAsync(HttpRequest request) { + return executeAsync(request, RequestOptions.none()); + } + + @Override + public CompletableFuture executeAsync(HttpRequest request, RequestOptions requestOptions) { + Objects.requireNonNull(request, "request"); + Objects.requireNonNull(requestOptions, "requestOptions"); + + return Mono.fromCallable(() -> buildAzureRequest(request)) + .flatMap(azureRequest -> this.httpPipeline.send(azureRequest, buildRequestContext(requestOptions))) + .map(response -> (HttpResponse) new AzureHttpResponseAdapter(response)) + .onErrorMap(HttpClientWrapper::mapAzureExceptionToOpenAI) + .toFuture(); + } + + /** + * Maps Azure exceptions to their corresponding OpenAI exception types. + * + * @param throwable The Azure exception to map. + * @return The corresponding OpenAI exception. + */ + private static Throwable mapAzureExceptionToOpenAI(Throwable throwable) { + if (throwable instanceof HttpResponseException) { + HttpResponseException httpResponseException = (HttpResponseException) throwable; + int statusCode = httpResponseException.getResponse().getStatusCode(); + Headers headers = toOpenAIHeaders(httpResponseException.getResponse().getHeaders()); + + switch (statusCode) { + case 400: + return BadRequestException.builder().headers(headers).cause(httpResponseException).build(); + + case 401: + return UnauthorizedException.builder().headers(headers).cause(httpResponseException).build(); + + case 403: + return PermissionDeniedException.builder() + .headers(headers) + .cause(httpResponseException) + .build(); + + case 404: + return NotFoundException.builder().headers(headers).cause(httpResponseException).build(); + + case 422: + return UnprocessableEntityException.builder() + .headers(headers) + .cause(httpResponseException) + .build(); + + case 429: + return RateLimitException.builder().headers(headers).cause(httpResponseException).build(); + + case 500: + case 502: + case 503: + case 504: + return InternalServerException.builder() + .statusCode(statusCode) + .headers(headers) + .cause(httpResponseException) + .build(); + + default: + return UnexpectedStatusCodeException.builder() + .statusCode(statusCode) + .headers(headers) + .cause(httpResponseException) + .build(); + } + } else if (throwable instanceof TimeoutException) { + return throwable; + } else { + if (throwable instanceof AzureException) { + return new OpenAIException(throwable.getMessage(), throwable.getCause()); + } else { + return new OpenAIException(throwable.getMessage(), throwable); + } + } + } + + /** + * Converts Azure {@link HttpHeaders} to OpenAI {@link Headers}. + */ + private static Headers toOpenAIHeaders(HttpHeaders azureHeaders) { + Headers.Builder builder = Headers.builder(); + azureHeaders.forEach(header -> builder.put(header.getName(), header.getValue())); + return builder.build(); + } + + /** + * Converts the OpenAI request metadata and body into an Azure {@link com.azure.core.http.HttpRequest}. + */ + private static com.azure.core.http.HttpRequest buildAzureRequest(HttpRequest request) + throws MalformedURLException { + HttpRequestBody requestBody = request.body(); + String contentType = requestBody != null ? requestBody.contentType() : null; + BinaryData bodyData = null; + + if (requestBody != null && requestBody.contentLength() > 0) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + requestBody.writeTo(outputStream); + bodyData = BinaryData.fromBytes(outputStream.toByteArray()); + } + + HttpHeaders headers = toAzureHeaders(request.headers()); + if (!CoreUtils.isNullOrEmpty(contentType) && headers.getValue(HttpHeaderName.CONTENT_TYPE) == null) { + headers.set(HttpHeaderName.CONTENT_TYPE, contentType); + } + + com.azure.core.http.HttpRequest azureRequest = new com.azure.core.http.HttpRequest( + HttpMethod.valueOf(request.method().name()), URI.create(request.url()).toURL(), headers); + + if (bodyData != null) { + azureRequest.setBody(bodyData); + } + + return azureRequest; + } + + /** + * Copies OpenAI headers into an {@link HttpHeaders} instance so the Azure pipeline can process them. + */ + private static HttpHeaders toAzureHeaders(Headers sourceHeaders) { + HttpHeaders target = new HttpHeaders(); + sourceHeaders.names().forEach(name -> { + List values = sourceHeaders.values(name); + HttpHeaderName headerName = HttpHeaderName.fromString(name); + if (values.isEmpty()) { + target.set(headerName, ""); + } else { + target.set(headerName, values); + } + }); + return target; + } + + /** + * Builds the request context from the given request options. + * @param requestOptions OpenAI SDK request options + * @return Azure request {@link Context} + */ + private static Context buildRequestContext(RequestOptions requestOptions) { + Context context = new Context("azure-eagerly-read-response", true); + Timeout timeout = requestOptions.getTimeout(); + // we use "read" as it's the closes thing to the "response timeout" + if (timeout != null && !timeout.read().isZero() && !timeout.read().isNegative()) { + context = context.addData("azure-response-timeout", timeout.read()); + } + return context; + } + } +} From 34a86856b651660c5cd8ed04a40317ea3b9b34ec Mon Sep 17 00:00:00 2001 From: Jose Alvarez Date: Fri, 9 Jan 2026 10:43:23 +0100 Subject: [PATCH 3/6] PR feedback and style checks --- .../ai/agents/implementation/http/HttpClientHelper.java | 2 +- .../com/azure/ai/projects/AIProjectClientBuilder.java | 8 ++++---- .../ai/projects/implementation/http/HttpClientHelper.java | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sdk/ai/azure-ai-agents/src/main/java/com/azure/ai/agents/implementation/http/HttpClientHelper.java b/sdk/ai/azure-ai-agents/src/main/java/com/azure/ai/agents/implementation/http/HttpClientHelper.java index ee3f3e350ce3..e86d423fbfdf 100644 --- a/sdk/ai/azure-ai-agents/src/main/java/com/azure/ai/agents/implementation/http/HttpClientHelper.java +++ b/sdk/ai/azure-ai-agents/src/main/java/com/azure/ai/agents/implementation/http/HttpClientHelper.java @@ -242,7 +242,7 @@ private static HttpHeaders toAzureHeaders(Headers sourceHeaders) { private static Context buildRequestContext(RequestOptions requestOptions) { Context context = new Context("azure-eagerly-read-response", true); Timeout timeout = requestOptions.getTimeout(); - // we use "read" as it's the closes thing to the "response timeout" + // we use "read" as it's the closest thing to the "response timeout" if (timeout != null && !timeout.read().isZero() && !timeout.read().isNegative()) { context = context.addData("azure-response-timeout", timeout.read()); } diff --git a/sdk/ai/azure-ai-projects/src/main/java/com/azure/ai/projects/AIProjectClientBuilder.java b/sdk/ai/azure-ai-projects/src/main/java/com/azure/ai/projects/AIProjectClientBuilder.java index 1bcf18e1065c..f0a1d943d5c4 100644 --- a/sdk/ai/azure-ai-projects/src/main/java/com/azure/ai/projects/AIProjectClientBuilder.java +++ b/sdk/ai/azure-ai-projects/src/main/java/com/azure/ai/projects/AIProjectClientBuilder.java @@ -545,8 +545,8 @@ public SchedulesClient buildSchedulesClient() { */ public EvaluationsClient buildEvaluationsClient() { return new EvaluationsClient(getOpenAIClientBuilder().build() - .withOptions(optionBuilder -> optionBuilder - .httpClient(HttpClientHelper.mapToOpenAIHttpClient(createHttpPipeline())))); + .withOptions(optionBuilder -> optionBuilder + .httpClient(HttpClientHelper.mapToOpenAIHttpClient(createHttpPipeline())))); } /** @@ -556,8 +556,8 @@ public EvaluationsClient buildEvaluationsClient() { */ public EvaluationsAsyncClient buildEvaluationsAsyncClient() { return new EvaluationsAsyncClient(getOpenAIAsyncClientBuilder().build() - .withOptions(optionBuilder -> optionBuilder - .httpClient(HttpClientHelper.mapToOpenAIHttpClient(createHttpPipeline())))); + .withOptions(optionBuilder -> optionBuilder + .httpClient(HttpClientHelper.mapToOpenAIHttpClient(createHttpPipeline())))); } private OpenAIOkHttpClient.Builder getOpenAIClientBuilder() { diff --git a/sdk/ai/azure-ai-projects/src/main/java/com/azure/ai/projects/implementation/http/HttpClientHelper.java b/sdk/ai/azure-ai-projects/src/main/java/com/azure/ai/projects/implementation/http/HttpClientHelper.java index 8aae7cf6724f..013e7d770eba 100644 --- a/sdk/ai/azure-ai-projects/src/main/java/com/azure/ai/projects/implementation/http/HttpClientHelper.java +++ b/sdk/ai/azure-ai-projects/src/main/java/com/azure/ai/projects/implementation/http/HttpClientHelper.java @@ -242,7 +242,7 @@ private static HttpHeaders toAzureHeaders(Headers sourceHeaders) { private static Context buildRequestContext(RequestOptions requestOptions) { Context context = new Context("azure-eagerly-read-response", true); Timeout timeout = requestOptions.getTimeout(); - // we use "read" as it's the closes thing to the "response timeout" + // we use "read" as it's the closest thing to the "response timeout" if (timeout != null && !timeout.read().isZero() && !timeout.read().isNegative()) { context = context.addData("azure-response-timeout", timeout.read()); } From f2426a93b816def2a6a57a351a868e804b46b30f Mon Sep 17 00:00:00 2001 From: Jose Alvarez Date: Wed, 14 Jan 2026 12:46:38 +0100 Subject: [PATCH 4/6] Added timeout tests, header exclusions, and clean up to match other projects structure --- .../com/azure/ai/projects/ClientTestBase.java | 174 +++++++- .../projects/ConnectionsAsyncClientTest.java | 54 +-- .../ai/projects/ConnectionsClientTest.java | 53 +-- .../ai/projects/DatasetsAsyncClientTest.java | 75 +--- .../azure/ai/projects/DatasetsClientTest.java | 75 +--- .../projects/DeploymentsAsyncClientTest.java | 48 +-- .../ai/projects/DeploymentsClientTest.java | 45 +- .../projects/EvaluationsAsyncClientTest.java | 362 ++++++++-------- .../ai/projects/EvaluationsClientTest.java | 400 +++++++++--------- .../ai/projects/IndexesAsyncClientTest.java | 49 +-- .../azure/ai/projects/IndexesClientTest.java | 49 +-- .../java/com/azure/ai/projects/TestUtils.java | 5 +- 12 files changed, 653 insertions(+), 736 deletions(-) diff --git a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/ClientTestBase.java b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/ClientTestBase.java index a52d0eda05ca..d94e4091dcf6 100644 --- a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/ClientTestBase.java +++ b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/ClientTestBase.java @@ -2,6 +2,13 @@ // Licensed under the MIT License. package com.azure.ai.projects; +import com.azure.ai.projects.models.Connection; +import com.azure.ai.projects.models.ConnectionType; +import com.azure.ai.projects.models.DatasetVersion; +import com.azure.ai.projects.models.Deployment; +import com.azure.ai.projects.models.DeploymentType; +import com.azure.ai.projects.models.FileDatasetVersion; +import com.azure.ai.projects.models.Index; import com.azure.core.http.HttpClient; import com.azure.core.test.TestMode; import com.azure.core.test.TestProxyTestBase; @@ -11,6 +18,8 @@ import com.azure.core.test.utils.MockTokenCredential; import com.azure.core.util.Configuration; import com.azure.identity.DefaultAzureCredentialBuilder; +import org.junit.jupiter.api.Assertions; + import java.io.File; import java.io.FileNotFoundException; import java.net.URISyntaxException; @@ -23,7 +32,8 @@ public class ClientTestBase extends TestProxyTestBase { private boolean sanitizersRemoved = false; - protected AIProjectClientBuilder getClientBuilder(HttpClient httpClient) { + protected AIProjectClientBuilder getClientBuilder(HttpClient httpClient, + AIProjectsServiceVersion aiProjectsServiceVersion) { AIProjectClientBuilder builder = new AIProjectClientBuilder() .httpClient(interceptorManager.isPlaybackMode() ? interceptorManager.getPlaybackClient() : httpClient); @@ -49,9 +59,10 @@ protected AIProjectClientBuilder getClientBuilder(HttpClient httpClient) { } String version = Configuration.getGlobalConfiguration().get("SERVICE_VERSION"); - AIProjectsServiceVersion serviceVersion = version != null - ? AIProjectsServiceVersion.valueOf(version) - : AIProjectsServiceVersion.V2025_05_15_PREVIEW; + + AIProjectsServiceVersion serviceVersion + = version == null ? aiProjectsServiceVersion : AIProjectsServiceVersion.valueOf(version); + builder.serviceVersion(serviceVersion); return builder; } @@ -71,7 +82,9 @@ private void addTestRecordCustomSanitizers() { } private void addCustomMatchers() { - interceptorManager.addMatchers(new CustomMatcher().setExcludedHeaders(Arrays.asList("Cookie", "Set-Cookie"))); + interceptorManager.addMatchers(new CustomMatcher().setExcludedHeaders(Arrays.asList("Cookie", "Set-Cookie", + "X-Stainless-Arch", "X-Stainless-Lang", "X-Stainless-OS", "X-Stainless-OS-Version", + "X-Stainless-Package-Version", "X-Stainless-Runtime", "X-Stainless-Runtime-Version"))); } protected Path getPath(String fileName) throws FileNotFoundException, URISyntaxException { @@ -83,4 +96,155 @@ protected Path getPath(String fileName) throws FileNotFoundException, URISyntaxE File file = new File(resource.toURI()); return file.toPath(); } + + protected ConnectionsClient getConnectionsClient(HttpClient httpClient, + AIProjectsServiceVersion aiProjectsServiceVersion) { + return getClientBuilder(httpClient, aiProjectsServiceVersion).buildConnectionsClient(); + } + + protected ConnectionsAsyncClient getConnectionsAsyncClient(HttpClient httpClient, + AIProjectsServiceVersion aiProjectsServiceVersion) { + return getClientBuilder(httpClient, aiProjectsServiceVersion).buildConnectionsAsyncClient(); + } + + protected DatasetsClient getDatasetsClient(HttpClient httpClient, + AIProjectsServiceVersion aiProjectsServiceVersion) { + return getClientBuilder(httpClient, aiProjectsServiceVersion).buildDatasetsClient(); + } + + protected DatasetsAsyncClient getDatasetsAsyncClient(HttpClient httpClient, + AIProjectsServiceVersion aiProjectsServiceVersion) { + return getClientBuilder(httpClient, aiProjectsServiceVersion).buildDatasetsAsyncClient(); + } + + protected DeploymentsClient getDeploymentsClient(HttpClient httpClient, + AIProjectsServiceVersion aiProjectsServiceVersion) { + return getClientBuilder(httpClient, aiProjectsServiceVersion).buildDeploymentsClient(); + } + + protected DeploymentsAsyncClient getDeploymentsAsyncClient(HttpClient httpClient, + AIProjectsServiceVersion aiProjectsServiceVersion) { + return getClientBuilder(httpClient, aiProjectsServiceVersion).buildDeploymentsAsyncClient(); + } + + protected EvaluationsClient getEvaluationsClient(HttpClient httpClient, + AIProjectsServiceVersion aiProjectsServiceVersion) { + return getClientBuilder(httpClient, aiProjectsServiceVersion).buildEvaluationsClient(); + } + + protected EvaluationsAsyncClient getEvaluationsAsyncClient(HttpClient httpClient, + AIProjectsServiceVersion aiProjectsServiceVersion) { + return getClientBuilder(httpClient, aiProjectsServiceVersion).buildEvaluationsAsyncClient(); + } + + protected IndexesClient getIndexesClient(HttpClient httpClient, AIProjectsServiceVersion aiProjectsServiceVersion) { + return getClientBuilder(httpClient, aiProjectsServiceVersion).buildIndexesClient(); + } + + protected IndexesAsyncClient getIndexesAsyncClient(HttpClient httpClient, + AIProjectsServiceVersion aiProjectsServiceVersion) { + return getClientBuilder(httpClient, aiProjectsServiceVersion).buildIndexesAsyncClient(); + } + + /** + * Helper method to verify a Connection has valid properties. + * @param connection The connection to validate + * @param expectedName The expected name of the connection, or null if no specific name is expected + * @param expectedType The expected connection type, or null if no specific type is expected + * @param shouldBeDefault Whether the connection should be a default connection, or null if not checking this property + */ + protected void assertValidConnection(Connection connection, String expectedName, ConnectionType expectedType, + Boolean shouldBeDefault) { + Assertions.assertNotNull(connection); + Assertions.assertNotNull(connection.getName()); + Assertions.assertNotNull(connection.getId()); + Assertions.assertNotNull(connection.getType()); + Assertions.assertNotNull(connection.getTarget()); + Assertions.assertNotNull(connection.getCredentials()); + + if (expectedName != null) { + Assertions.assertEquals(expectedName, connection.getName()); + } + + if (expectedType != null) { + Assertions.assertEquals(expectedType, connection.getType()); + } + + if (shouldBeDefault != null) { + Assertions.assertEquals(shouldBeDefault, connection.isDefault()); + } + } + + /** + * Helper method to validate common properties of a DatasetVersion + * + * @param datasetVersion The dataset version to validate + * @param expectedName The expected name of the dataset + * @param expectedVersion The expected version string + */ + protected void assertDatasetVersion(DatasetVersion datasetVersion, String expectedName, String expectedVersion) { + Assertions.assertNotNull(datasetVersion, "Dataset version should not be null"); + Assertions.assertEquals(expectedName, datasetVersion.getName(), "Dataset name should match expected value"); + Assertions.assertEquals(expectedVersion, datasetVersion.getVersion(), + "Dataset version should match expected value"); + Assertions.assertNotNull(datasetVersion.getType(), "Dataset type should not be null"); + } + + /** + * Helper method to validate common properties of a FileDatasetVersion + * + * @param fileDatasetVersion The file dataset version to validate + * @param expectedName The expected name of the dataset + * @param expectedVersion The expected version string + * @param expectedDataUri The expected data URI (optional) + */ + protected void assertFileDatasetVersion(FileDatasetVersion fileDatasetVersion, String expectedName, + String expectedVersion, String expectedDataUri) { + assertDatasetVersion(fileDatasetVersion, expectedName, expectedVersion); + if (expectedDataUri != null) { + Assertions.assertEquals(expectedDataUri, fileDatasetVersion.getDataUri(), + "Dataset dataUri should match expected value"); + } + } + + /** + * Helper method to verify a Deployment has valid properties. + * @param deployment The deployment to validate + * @param expectedName The expected name of the deployment, or null if no specific name is expected + * @param expectedType The expected deployment type, or null if no specific type is expected + */ + protected void assertValidDeployment(Deployment deployment, String expectedName, DeploymentType expectedType) { + Assertions.assertNotNull(deployment); + Assertions.assertNotNull(deployment.getName()); + Assertions.assertNotNull(deployment.getType()); + + if (expectedName != null) { + Assertions.assertEquals(expectedName, deployment.getName()); + } + + if (expectedType != null) { + Assertions.assertEquals(expectedType, deployment.getType()); + } + } + + /** + * Helper method to verify an Index has valid properties. + * @param index The index to validate + * @param expectedName The expected name of the index, or null if no specific name is expected + * @param expectedVersion The expected version of the index, or null if no specific version is expected + */ + protected void assertValidIndex(Index index, String expectedName, String expectedVersion) { + Assertions.assertNotNull(index); + Assertions.assertNotNull(index.getName()); + Assertions.assertNotNull(index.getVersion()); + Assertions.assertNotNull(index.getType()); + + if (expectedName != null) { + Assertions.assertEquals(expectedName, index.getName()); + } + + if (expectedVersion != null) { + Assertions.assertEquals(expectedVersion, index.getVersion()); + } + } } diff --git a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/ConnectionsAsyncClientTest.java b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/ConnectionsAsyncClientTest.java index cbb2af539ac8..15ed293d766b 100644 --- a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/ConnectionsAsyncClientTest.java +++ b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/ConnectionsAsyncClientTest.java @@ -22,47 +22,10 @@ @Disabled("Disabled for lack of recordings. Needs to be enabled on the Public Preview release.") public class ConnectionsAsyncClientTest extends ClientTestBase { - private AIProjectClientBuilder clientBuilder; - private ConnectionsAsyncClient connectionsAsyncClient; - - private void setup(HttpClient httpClient) { - clientBuilder = getClientBuilder(httpClient); - connectionsAsyncClient = clientBuilder.buildConnectionsAsyncClient(); - } - - /** - * Helper method to verify a Connection has valid properties. - * @param connection The connection to validate - * @param expectedName The expected name of the connection, or null if no specific name is expected - * @param expectedType The expected connection type, or null if no specific type is expected - * @param shouldBeDefault Whether the connection should be a default connection, or null if not checking this property - */ - private void assertValidConnection(Connection connection, String expectedName, ConnectionType expectedType, - Boolean shouldBeDefault) { - Assertions.assertNotNull(connection); - Assertions.assertNotNull(connection.getName()); - Assertions.assertNotNull(connection.getId()); - Assertions.assertNotNull(connection.getType()); - Assertions.assertNotNull(connection.getTarget()); - Assertions.assertNotNull(connection.getCredentials()); - - if (expectedName != null) { - Assertions.assertEquals(expectedName, connection.getName()); - } - - if (expectedType != null) { - Assertions.assertEquals(expectedType, connection.getType()); - } - - if (shouldBeDefault != null) { - Assertions.assertEquals(shouldBeDefault, connection.isDefault()); - } - } - @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testListConnectionsAsync(HttpClient httpClient) { - setup(httpClient); + public void testListConnectionsAsync(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + ConnectionsAsyncClient connectionsAsyncClient = getConnectionsAsyncClient(httpClient, serviceVersion); // Verify that listing connections returns results PagedFlux connectionsFlux = connectionsAsyncClient.listConnections(); @@ -83,8 +46,8 @@ public void testListConnectionsAsync(HttpClient httpClient) { @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testListConnectionsWithFiltersAsync(HttpClient httpClient) { - setup(httpClient); + public void testListConnectionsWithFiltersAsync(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + ConnectionsAsyncClient connectionsAsyncClient = getConnectionsAsyncClient(httpClient, serviceVersion); // Test listing connections with type filter PagedFlux azureOpenAIConnections @@ -110,8 +73,9 @@ public void testListConnectionsWithFiltersAsync(HttpClient httpClient) { @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testGetConnectionWithoutCredentialsAsync(HttpClient httpClient) { - setup(httpClient); + public void testGetConnectionWithoutCredentialsAsync(HttpClient httpClient, + AIProjectsServiceVersion serviceVersion) { + ConnectionsAsyncClient connectionsAsyncClient = getConnectionsAsyncClient(httpClient, serviceVersion); String connectionName = Configuration.getGlobalConfiguration().get("TEST_CONNECTION_NAME", "agentaisearch2aqa"); @@ -134,8 +98,8 @@ public void testGetConnectionWithoutCredentialsAsync(HttpClient httpClient) { @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testGetConnectionWithCredentialsAsync(HttpClient httpClient) { - setup(httpClient); + public void testGetConnectionWithCredentialsAsync(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + ConnectionsAsyncClient connectionsAsyncClient = getConnectionsAsyncClient(httpClient, serviceVersion); String connectionName = Configuration.getGlobalConfiguration().get("TEST_CONNECTION_NAME", "agentaisearch2aqa"); diff --git a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/ConnectionsClientTest.java b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/ConnectionsClientTest.java index d037b1408a14..37227467101b 100644 --- a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/ConnectionsClientTest.java +++ b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/ConnectionsClientTest.java @@ -16,47 +16,10 @@ @Disabled("Disabled for lack of recordings. Needs to be enabled on the Public Preview release.") public class ConnectionsClientTest extends ClientTestBase { - private AIProjectClientBuilder clientBuilder; - private ConnectionsClient connectionsClient; - - private void setup(HttpClient httpClient) { - clientBuilder = getClientBuilder(httpClient); - connectionsClient = clientBuilder.buildConnectionsClient(); - } - - /** - * Helper method to verify a Connection has valid properties. - * @param connection The connection to validate - * @param expectedName The expected name of the connection, or null if no specific name is expected - * @param expectedType The expected connection type, or null if no specific type is expected - * @param shouldBeDefault Whether the connection should be a default connection, or null if not checking this property - */ - private void assertValidConnection(Connection connection, String expectedName, ConnectionType expectedType, - Boolean shouldBeDefault) { - Assertions.assertNotNull(connection); - Assertions.assertNotNull(connection.getName()); - Assertions.assertNotNull(connection.getId()); - Assertions.assertNotNull(connection.getType()); - Assertions.assertNotNull(connection.getTarget()); - Assertions.assertNotNull(connection.getCredentials()); - - if (expectedName != null) { - Assertions.assertEquals(expectedName, connection.getName()); - } - - if (expectedType != null) { - Assertions.assertEquals(expectedType, connection.getType()); - } - - if (shouldBeDefault != null) { - Assertions.assertEquals(shouldBeDefault, connection.isDefault()); - } - } - @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testListConnections(HttpClient httpClient) { - setup(httpClient); + public void testListConnections(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + ConnectionsClient connectionsClient = getConnectionsClient(httpClient, serviceVersion); // Verify that listing connections returns results Iterable connections = connectionsClient.listConnections(); @@ -78,8 +41,8 @@ public void testListConnections(HttpClient httpClient) { @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testListConnectionsWithFilters(HttpClient httpClient) { - setup(httpClient); + public void testListConnectionsWithFilters(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + ConnectionsClient connectionsClient = getConnectionsClient(httpClient, serviceVersion); // Test listing connections with type filter Iterable azureOpenAIConnections @@ -103,8 +66,8 @@ public void testListConnectionsWithFilters(HttpClient httpClient) { @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testGetConnectionWithoutCredentials(HttpClient httpClient) { - setup(httpClient); + public void testGetConnectionWithoutCredentials(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + ConnectionsClient connectionsClient = getConnectionsClient(httpClient, serviceVersion); String connectionName = Configuration.getGlobalConfiguration().get("TEST_CONNECTION_NAME", "agentaisearch2aqa"); @@ -126,8 +89,8 @@ public void testGetConnectionWithoutCredentials(HttpClient httpClient) { @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testGetConnectionWithCredentials(HttpClient httpClient) { - setup(httpClient); + public void testGetConnectionWithCredentials(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + ConnectionsClient connectionsClient = getConnectionsClient(httpClient, serviceVersion); String connectionName = Configuration.getGlobalConfiguration().get("TEST_CONNECTION_NAME", "agentaisearch2aqa"); diff --git a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/DatasetsAsyncClientTest.java b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/DatasetsAsyncClientTest.java index 1640c373357a..56b07600d05a 100644 --- a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/DatasetsAsyncClientTest.java +++ b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/DatasetsAsyncClientTest.java @@ -26,51 +26,12 @@ @Disabled("Disabled for lack of recordings. Needs to be enabled on the Public Preview release.") public class DatasetsAsyncClientTest extends ClientTestBase { - private AIProjectClientBuilder clientBuilder; - private DatasetsAsyncClient datasetsAsyncClient; - - private void setup(HttpClient httpClient) { - clientBuilder = getClientBuilder(httpClient); - datasetsAsyncClient = clientBuilder.buildDatasetsAsyncClient(); - } - - /** - * Helper method to validate common properties of a DatasetVersion - * - * @param datasetVersion The dataset version to validate - * @param expectedName The expected name of the dataset - * @param expectedVersion The expected version string - */ - private void assertDatasetVersion(DatasetVersion datasetVersion, String expectedName, String expectedVersion) { - Assertions.assertNotNull(datasetVersion, "Dataset version should not be null"); - Assertions.assertEquals(expectedName, datasetVersion.getName(), "Dataset name should match expected value"); - Assertions.assertEquals(expectedVersion, datasetVersion.getVersion(), - "Dataset version should match expected value"); - Assertions.assertNotNull(datasetVersion.getType(), "Dataset type should not be null"); - } - - /** - * Helper method to validate common properties of a FileDatasetVersion - * - * @param fileDatasetVersion The file dataset version to validate - * @param expectedName The expected name of the dataset - * @param expectedVersion The expected version string - * @param expectedDataUri The expected data URI (optional) - */ - private void assertFileDatasetVersion(FileDatasetVersion fileDatasetVersion, String expectedName, - String expectedVersion, String expectedDataUri) { - assertDatasetVersion(fileDatasetVersion, expectedName, expectedVersion); - if (expectedDataUri != null) { - Assertions.assertEquals(expectedDataUri, fileDatasetVersion.getDataUri(), - "Dataset dataUri should match expected value"); - } - } - @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testCreateDatasetWithFile(HttpClient httpClient) throws FileNotFoundException, URISyntaxException { - setup(httpClient); + public void testCreateDatasetWithFile(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) + throws FileNotFoundException, URISyntaxException { + DatasetsAsyncClient datasetsAsyncClient = getDatasetsAsyncClient(httpClient, serviceVersion); String datasetName = Configuration.getGlobalConfiguration().get("DATASET_NAME", "my-dataset"); String datasetVersionString = Configuration.getGlobalConfiguration().get("DATASET_VERSION", "1.0"); @@ -86,8 +47,9 @@ public void testCreateDatasetWithFile(HttpClient httpClient) throws FileNotFound @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testCreateDatasetWithFolder(HttpClient httpClient) throws IOException, URISyntaxException { - setup(httpClient); + public void testCreateDatasetWithFolder(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) + throws IOException, URISyntaxException { + DatasetsAsyncClient datasetsAsyncClient = getDatasetsAsyncClient(httpClient, serviceVersion); String datasetName = Configuration.getGlobalConfiguration().get("DATASET_NAME", "folder-dataset") + UUID.randomUUID().toString().substring(0, 8); @@ -117,8 +79,8 @@ public void testCreateDatasetWithFolder(HttpClient httpClient) throws IOExceptio @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testListDatasets(HttpClient httpClient) { - setup(httpClient); + public void testListDatasets(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + DatasetsAsyncClient datasetsAsyncClient = getDatasetsAsyncClient(httpClient, serviceVersion); // Collect datasets into a list List datasetsList = new ArrayList<>(); @@ -137,8 +99,8 @@ public void testListDatasets(HttpClient httpClient) { @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testListDatasetVersions(HttpClient httpClient) { - setup(httpClient); + public void testListDatasetVersions(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + DatasetsAsyncClient datasetsAsyncClient = getDatasetsAsyncClient(httpClient, serviceVersion); String datasetName = Configuration.getGlobalConfiguration().get("DATASET_NAME", "my-dataset"); @@ -160,8 +122,8 @@ public void testListDatasetVersions(HttpClient httpClient) { @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testGetDataset(HttpClient httpClient) { - setup(httpClient); + public void testGetDataset(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + DatasetsAsyncClient datasetsAsyncClient = getDatasetsAsyncClient(httpClient, serviceVersion); String datasetName = Configuration.getGlobalConfiguration().get("DATASET_NAME", "my-dataset"); String datasetVersion = Configuration.getGlobalConfiguration().get("DATASET_VERSION", "1.0"); @@ -174,8 +136,8 @@ public void testGetDataset(HttpClient httpClient) { @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testCreateOrUpdateDataset(HttpClient httpClient) { - setup(httpClient); + public void testCreateOrUpdateDataset(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + DatasetsAsyncClient datasetsAsyncClient = getDatasetsAsyncClient(httpClient, serviceVersion); String datasetName = Configuration.getGlobalConfiguration().get("DATASET_NAME", "updated-dataset") + UUID.randomUUID().toString().substring(0, 8); @@ -198,8 +160,8 @@ public void testCreateOrUpdateDataset(HttpClient httpClient) { @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testPendingUpload(HttpClient httpClient) { - setup(httpClient); + public void testPendingUpload(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + DatasetsAsyncClient datasetsAsyncClient = getDatasetsAsyncClient(httpClient, serviceVersion); String datasetName = Configuration.getGlobalConfiguration().get("DATASET_NAME", "pending-upload-dataset") + UUID.randomUUID().toString().substring(0, 8); @@ -222,8 +184,9 @@ public void testPendingUpload(HttpClient httpClient) { @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testDeleteDataset(HttpClient httpClient) throws FileNotFoundException, URISyntaxException { - setup(httpClient); + public void testDeleteDataset(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) + throws FileNotFoundException, URISyntaxException { + DatasetsAsyncClient datasetsAsyncClient = getDatasetsAsyncClient(httpClient, serviceVersion); // First create a dataset that we can then delete String datasetName = Configuration.getGlobalConfiguration().get("DATASET_NAME", "delete-test-dataset") diff --git a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/DatasetsClientTest.java b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/DatasetsClientTest.java index 28fa7a406c2e..df8e4cd12d07 100644 --- a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/DatasetsClientTest.java +++ b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/DatasetsClientTest.java @@ -25,51 +25,12 @@ @Disabled("Disabled for lack of recordings. Needs to be enabled on the Public Preview release.") public class DatasetsClientTest extends ClientTestBase { - private AIProjectClientBuilder clientBuilder; - private DatasetsClient datasetsClient; - - private void setup(HttpClient httpClient) { - clientBuilder = getClientBuilder(httpClient); - datasetsClient = clientBuilder.buildDatasetsClient(); - } - - /** - * Helper method to validate common properties of a DatasetVersion - * - * @param datasetVersion The dataset version to validate - * @param expectedName The expected name of the dataset - * @param expectedVersion The expected version string - */ - private void assertDatasetVersion(DatasetVersion datasetVersion, String expectedName, String expectedVersion) { - Assertions.assertNotNull(datasetVersion, "Dataset version should not be null"); - Assertions.assertEquals(expectedName, datasetVersion.getName(), "Dataset name should match expected value"); - Assertions.assertEquals(expectedVersion, datasetVersion.getVersion(), - "Dataset version should match expected value"); - Assertions.assertNotNull(datasetVersion.getType(), "Dataset type should not be null"); - } - - /** - * Helper method to validate common properties of a FileDatasetVersion - * - * @param fileDatasetVersion The file dataset version to validate - * @param expectedName The expected name of the dataset - * @param expectedVersion The expected version string - * @param expectedDataUri The expected data URI (optional) - */ - private void assertFileDatasetVersion(FileDatasetVersion fileDatasetVersion, String expectedName, - String expectedVersion, String expectedDataUri) { - assertDatasetVersion(fileDatasetVersion, expectedName, expectedVersion); - if (expectedDataUri != null) { - Assertions.assertEquals(expectedDataUri, fileDatasetVersion.getDataUri(), - "Dataset dataUri should match expected value"); - } - } - @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testCreateDatasetWithFile(HttpClient httpClient) throws FileNotFoundException, URISyntaxException { - setup(httpClient); + public void testCreateDatasetWithFile(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) + throws FileNotFoundException, URISyntaxException { + DatasetsClient datasetsClient = getDatasetsClient(httpClient, serviceVersion); String datasetName = Configuration.getGlobalConfiguration().get("DATASET_NAME", "my-dataset"); String datasetVersionString = Configuration.getGlobalConfiguration().get("DATASET_VERSION", "1.0"); @@ -85,8 +46,9 @@ public void testCreateDatasetWithFile(HttpClient httpClient) throws FileNotFound @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testCreateDatasetWithFolder(HttpClient httpClient) throws IOException, URISyntaxException { - setup(httpClient); + public void testCreateDatasetWithFolder(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) + throws IOException, URISyntaxException { + DatasetsClient datasetsClient = getDatasetsClient(httpClient, serviceVersion); String datasetName = Configuration.getGlobalConfiguration().get("DATASET_NAME", "folder-dataset") + UUID.randomUUID().toString().substring(0, 8); @@ -115,8 +77,8 @@ public void testCreateDatasetWithFolder(HttpClient httpClient) throws IOExceptio @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testListDatasets(HttpClient httpClient) { - setup(httpClient); + public void testListDatasets(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + DatasetsClient datasetsClient = getDatasetsClient(httpClient, serviceVersion); // Verify that listing datasets returns results Iterable datasets = datasetsClient.listLatest(); @@ -132,8 +94,8 @@ public void testListDatasets(HttpClient httpClient) { @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testListDatasetVersions(HttpClient httpClient) { - setup(httpClient); + public void testListDatasetVersions(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + DatasetsClient datasetsClient = getDatasetsClient(httpClient, serviceVersion); String datasetName = Configuration.getGlobalConfiguration().get("DATASET_NAME", "my-dataset"); @@ -151,8 +113,8 @@ public void testListDatasetVersions(HttpClient httpClient) { @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testGetDataset(HttpClient httpClient) { - setup(httpClient); + public void testGetDataset(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + DatasetsClient datasetsClient = getDatasetsClient(httpClient, serviceVersion); String datasetName = Configuration.getGlobalConfiguration().get("DATASET_NAME", "my-dataset"); String datasetVersion = Configuration.getGlobalConfiguration().get("DATASET_VERSION", "1.0"); @@ -167,8 +129,8 @@ public void testGetDataset(HttpClient httpClient) { @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testCreateOrUpdateDataset(HttpClient httpClient) { - setup(httpClient); + public void testCreateOrUpdateDataset(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + DatasetsClient datasetsClient = getDatasetsClient(httpClient, serviceVersion); String datasetName = Configuration.getGlobalConfiguration().get("DATASET_NAME", "updated-dataset") + UUID.randomUUID().toString().substring(0, 8); @@ -191,8 +153,8 @@ public void testCreateOrUpdateDataset(HttpClient httpClient) { @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testPendingUpload(HttpClient httpClient) { - setup(httpClient); + public void testPendingUpload(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + DatasetsClient datasetsClient = getDatasetsClient(httpClient, serviceVersion); String datasetName = Configuration.getGlobalConfiguration().get("DATASET_NAME", "pending-upload-dataset") + UUID.randomUUID().toString().substring(0, 8); @@ -215,8 +177,9 @@ public void testPendingUpload(HttpClient httpClient) { @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testDeleteDataset(HttpClient httpClient) throws FileNotFoundException, URISyntaxException { - setup(httpClient); + public void testDeleteDataset(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) + throws FileNotFoundException, URISyntaxException { + DatasetsClient datasetsClient = getDatasetsClient(httpClient, serviceVersion); // First create a dataset that we can then delete String datasetName = Configuration.getGlobalConfiguration().get("DATASET_NAME", "delete-test-dataset") diff --git a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/DeploymentsAsyncClientTest.java b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/DeploymentsAsyncClientTest.java index 112406fd725a..186df7488b2c 100644 --- a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/DeploymentsAsyncClientTest.java +++ b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/DeploymentsAsyncClientTest.java @@ -21,38 +21,10 @@ @Disabled("Disabled for lack of recordings. Needs to be enabled on the Public Preview release.") public class DeploymentsAsyncClientTest extends ClientTestBase { - private AIProjectClientBuilder clientBuilder; - private DeploymentsAsyncClient deploymentsAsyncClient; - - private void setup(HttpClient httpClient) { - clientBuilder = getClientBuilder(httpClient); - deploymentsAsyncClient = clientBuilder.buildDeploymentsAsyncClient(); - } - - /** - * Helper method to verify a Deployment has valid properties. - * @param deployment The deployment to validate - * @param expectedName The expected name of the deployment, or null if no specific name is expected - * @param expectedType The expected deployment type, or null if no specific type is expected - */ - private void assertValidDeployment(Deployment deployment, String expectedName, DeploymentType expectedType) { - Assertions.assertNotNull(deployment); - Assertions.assertNotNull(deployment.getName()); - Assertions.assertNotNull(deployment.getType()); - - if (expectedName != null) { - Assertions.assertEquals(expectedName, deployment.getName()); - } - - if (expectedType != null) { - Assertions.assertEquals(expectedType, deployment.getType()); - } - } - @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testListDeployments(HttpClient httpClient) { - setup(httpClient); + public void testListDeployments(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + DeploymentsAsyncClient deploymentsAsyncClient = getDeploymentsAsyncClient(httpClient, serviceVersion); // Verify that listing deployments returns results PagedFlux deploymentsFlux = deploymentsAsyncClient.list(); @@ -73,8 +45,8 @@ public void testListDeployments(HttpClient httpClient) { @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testListDeploymentsWithFilters(HttpClient httpClient) { - setup(httpClient); + public void testListDeploymentsWithFilters(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + DeploymentsAsyncClient deploymentsAsyncClient = getDeploymentsAsyncClient(httpClient, serviceVersion); // Test listing deployments with model publisher filter String testPublisher = "openai"; @@ -115,8 +87,8 @@ public void testListDeploymentsWithFilters(HttpClient httpClient) { @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testGetDeployment(HttpClient httpClient) { - setup(httpClient); + public void testGetDeployment(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + DeploymentsAsyncClient deploymentsAsyncClient = getDeploymentsAsyncClient(httpClient, serviceVersion); String deploymentName = Configuration.getGlobalConfiguration().get("TEST_DEPLOYMENT_NAME", "gpt-4o-mini"); @@ -128,8 +100,8 @@ public void testGetDeployment(HttpClient httpClient) { @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testGetDeploymentAndVerifyType(HttpClient httpClient) { - setup(httpClient); + public void testGetDeploymentAndVerifyType(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + DeploymentsAsyncClient deploymentsAsyncClient = getDeploymentsAsyncClient(httpClient, serviceVersion); String deploymentName = Configuration.getGlobalConfiguration().get("TEST_DEPLOYMENT_NAME", "gpt-4o-mini"); @@ -141,8 +113,8 @@ public void testGetDeploymentAndVerifyType(HttpClient httpClient) { @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testGetDeploymentNotFound(HttpClient httpClient) { - setup(httpClient); + public void testGetDeploymentNotFound(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + DeploymentsAsyncClient deploymentsAsyncClient = getDeploymentsAsyncClient(httpClient, serviceVersion); String nonExistentDeploymentName = "non-existent-deployment-name"; diff --git a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/DeploymentsClientTest.java b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/DeploymentsClientTest.java index c37e686304c0..d999a36a1bac 100644 --- a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/DeploymentsClientTest.java +++ b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/DeploymentsClientTest.java @@ -16,39 +16,10 @@ @Disabled("Disabled for lack of recordings. Needs to be enabled on the Public Preview release.") public class DeploymentsClientTest extends ClientTestBase { - private AIProjectClientBuilder clientBuilder; - private DeploymentsClient deploymentsClient; - - private void setup(HttpClient httpClient) { - clientBuilder = getClientBuilder(httpClient); - deploymentsClient = clientBuilder.buildDeploymentsClient(); - } - - /** - * Helper method to verify a Deployment has valid properties. - * @param deployment The deployment to validate - * @param expectedName The expected name of the deployment, or null if no specific name is expected - * @param expectedType The expected deployment type, or null if no specific type is expected - */ - private void assertValidDeployment(Deployment deployment, String expectedName, DeploymentType expectedType) { - Assertions.assertNotNull(deployment); - Assertions.assertNotNull(deployment.getName()); - Assertions.assertNotNull(deployment.getType()); - - if (expectedName != null) { - Assertions.assertEquals(expectedName, deployment.getName()); - } - - if (expectedType != null) { - Assertions.assertEquals(expectedType, deployment.getType()); - } - } - @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testListDeployments(HttpClient httpClient) { - setup(httpClient); - + public void testListDeployments(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + DeploymentsClient deploymentsClient = getDeploymentsClient(httpClient, serviceVersion); // Verify that listing deployments returns results Iterable deployments = deploymentsClient.list(); Assertions.assertNotNull(deployments); @@ -69,8 +40,8 @@ public void testListDeployments(HttpClient httpClient) { @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testListDeploymentsWithFilters(HttpClient httpClient) { - setup(httpClient); + public void testListDeploymentsWithFilters(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + DeploymentsClient deploymentsClient = getDeploymentsClient(httpClient, serviceVersion); // Test listing deployments with model publisher filter String testPublisher = "openai"; @@ -95,8 +66,8 @@ public void testListDeploymentsWithFilters(HttpClient httpClient) { @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testGetDeployment(HttpClient httpClient) { - setup(httpClient); + public void testGetDeployment(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + DeploymentsClient deploymentsClient = getDeploymentsClient(httpClient, serviceVersion); String deploymentName = Configuration.getGlobalConfiguration().get("TEST_DEPLOYMENT_NAME", "gpt-4o-mini"); @@ -118,8 +89,8 @@ public void testGetDeployment(HttpClient httpClient) { @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testGetDeploymentAndVerifyType(HttpClient httpClient) { - setup(httpClient); + public void testGetDeploymentAndVerifyType(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + DeploymentsClient deploymentsClient = getDeploymentsClient(httpClient, serviceVersion); String deploymentName = Configuration.getGlobalConfiguration().get("TEST_DEPLOYMENT_NAME", "gpt-4o-mini"); diff --git a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/EvaluationsAsyncClientTest.java b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/EvaluationsAsyncClientTest.java index 2e86712c06b3..0ab3c45cfe05 100644 --- a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/EvaluationsAsyncClientTest.java +++ b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/EvaluationsAsyncClientTest.java @@ -1,180 +1,186 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -//package com.azure.ai.projects; -// -//import com.azure.ai.projects.models.DatasetVersion; -//import com.azure.ai.projects.models.Evaluation; -//import com.azure.ai.projects.models.EvaluatorConfiguration; -//import com.azure.ai.projects.models.EvaluatorId; -//import com.azure.ai.projects.models.InputDataset; -//import com.azure.core.http.HttpClient; -//import com.azure.core.util.BinaryData; -//import com.azure.core.util.Configuration; -//import org.junit.jupiter.api.Assertions; -//import org.junit.jupiter.api.Disabled; -//import org.junit.jupiter.params.ParameterizedTest; -//import org.junit.jupiter.params.provider.MethodSource; -//import reactor.core.publisher.Mono; -//import reactor.test.StepVerifier; -//import java.time.Duration; -//import java.util.HashMap; -//import java.util.Map; -//import java.util.concurrent.atomic.AtomicBoolean; -// -//import static com.azure.ai.projects.TestUtils.DISPLAY_NAME_WITH_ARGUMENTS; -// -//public class EvaluationsAsyncClientTest extends ClientTestBase { -// -// private AIProjectClientBuilder clientBuilder; -// private EvaluationsAsyncClient evaluationsAsyncClient; -// private DatasetsAsyncClient datasetsAsyncClient; -// -// private void setup(HttpClient httpClient) { -// clientBuilder = getClientBuilder(httpClient); -// evaluationsAsyncClient = clientBuilder.buildEvaluationsAsyncClient(); -// datasetsAsyncClient = clientBuilder.buildDatasetsAsyncClient(); -// } -// -// /** -// * Helper method to verify an Evaluation has valid properties. -// * @param evaluation The evaluation to validate -// * @param expectedDisplayName The expected display name of the evaluation, or null if no specific name is expected -// * @param expectedStatus The expected status, or null if no specific status is expected -// */ -// private void assertValidEvaluation(Evaluation evaluation, String expectedDisplayName, String expectedStatus) { -// Assertions.assertNotNull(evaluation); -// Assertions.assertNotNull(evaluation.getDisplayName()); -// Assertions.assertNotNull(evaluation.getStatus()); -// Assertions.assertNotNull(evaluation.getData()); -// Assertions.assertNotNull(evaluation.getData().getType()); -// Assertions.assertNotNull(evaluation.getEvaluators()); -// Assertions.assertFalse(evaluation.getEvaluators().isEmpty()); -// -// if (expectedDisplayName != null) { -// Assertions.assertEquals(expectedDisplayName, evaluation.getDisplayName()); -// } -// -// if (expectedStatus != null) { -// Assertions.assertEquals(expectedStatus, evaluation.getStatus()); -// } -// } -// -// @Disabled -// @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) -// @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") -// public void testListEvaluationsAsync(HttpClient httpClient) { -// setup(httpClient); -// -// // Verify that listing evaluations returns results -// AtomicBoolean hasAtLeastOneEvaluation = new AtomicBoolean(false); -// -// StepVerifier.create(evaluationsAsyncClient.listEvaluations().take(1)).assertNext(evaluation -> { -// hasAtLeastOneEvaluation.set(true); -// assertValidEvaluation(evaluation, null, null); -// }).verifyComplete(); -// -// // Note: This test will pass even if there are no evaluations, -// // as we're only verifying the API works correctly -// System.out.println("Evaluation list retrieved successfully" -// + (hasAtLeastOneEvaluation.get() ? " with at least one evaluation" : " (empty list)")); -// } -// -// @Disabled -// @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) -// @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") -// public void testGetEvaluationAsync(HttpClient httpClient) { -// setup(httpClient); -// -// String evaluationId = Configuration.getGlobalConfiguration().get("TEST_EVALUATION_ID", "test-evaluation-id"); -// -// StepVerifier.create(evaluationsAsyncClient.getEvaluation(evaluationId).doOnNext(evaluation -> { -// // Verify the evaluation properties -// assertValidEvaluation(evaluation, null, null); -// -// if (evaluation.getTags() != null) { -// // Verify tags are properly structured if present -// evaluation.getTags().forEach((key, value) -> { -// Assertions.assertNotNull(key); -// Assertions.assertNotNull(value); -// }); -// } -// -// System.out.println("Evaluation retrieved successfully: " + evaluation.getDisplayName()); -// System.out.println("Status: " + evaluation.getStatus()); -// }).timeout(Duration.ofSeconds(30))).verifyComplete(); -// } -// -// @Disabled -// @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) -// @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") -// public void testCreateEvaluationAsync(HttpClient httpClient) { -// setup(httpClient); -// -// String datasetName = Configuration.getGlobalConfiguration().get("TEST_DATASET_NAME", "test-dataset"); -// String version = Configuration.getGlobalConfiguration().get("TEST_DATASET_VERSION", "1"); -// -// // Get a dataset to use for the evaluation -// Mono datasetVersionMono = datasetsAsyncClient.getDatasetVersion(datasetName, version); -// -// StepVerifier.create(datasetVersionMono.flatMap(datasetVersion -> { -// // Create evaluation definition -// InputDataset dataset = new InputDataset(datasetVersion.getId()); -// Evaluation evaluationToCreate = new Evaluation(dataset, -// mapOf("relevance", -// new EvaluatorConfiguration(EvaluatorId.RELEVANCE.getValue()) -// .setInitParams(mapOf("deployment_name", BinaryData.fromObject("gpt-4o"))))) -// .setDisplayName("Test Async Evaluation " + System.currentTimeMillis()) -// .setDescription("This is a test evaluation created by the EvaluationsAsyncClientTest"); -// -// // Create the evaluation -// return evaluationsAsyncClient.createEvaluation(evaluationToCreate); -// })).assertNext(createdEvaluation -> { -// // Verify the created evaluation -// assertValidEvaluation(createdEvaluation, null, null); -// Assertions.assertTrue(createdEvaluation.getDisplayName().startsWith("Test Async Evaluation")); -// Assertions.assertTrue(createdEvaluation.getEvaluators().containsKey("relevance")); -// -// System.out.println("Evaluation created successfully: " + createdEvaluation.getDisplayName()); -// System.out.println("Initial status: " + createdEvaluation.getStatus()); -// }).verifyComplete(); -// } -// -// @Disabled -// @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) -// @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") -// public void testEvaluationStatusCheckAsync(HttpClient httpClient) { -// setup(httpClient); -// -// String evaluationId = Configuration.getGlobalConfiguration().get("TEST_EVALUATION_ID", "test-evaluation-id"); -// -// StepVerifier.create(evaluationsAsyncClient.getEvaluation(evaluationId)).assertNext(evaluation -> { -// // Verify status is one of the expected values -// Assertions.assertNotNull(evaluation.getStatus()); -// String status = evaluation.getStatus(); -// -// // Status should be one of: Running, Succeeded, Failed, Canceled, etc. -// boolean isValidStatus = "Running".equals(status) -// || "Succeeded".equals(status) -// || "Failed".equals(status) -// || "Canceled".equals(status) -// || "Queued".equals(status) -// || "Created".equals(status); -// -// Assertions.assertTrue(isValidStatus, "Unexpected evaluation status: " + status); -// -// System.out.println("Evaluation status check passed: " + status); -// }).verifyComplete(); -// } -// -// // Helper method for creating maps -// private static Map mapOf(Object... inputs) { -// Map map = new HashMap<>(); -// for (int i = 0; i < inputs.length; i += 2) { -// String key = (String) inputs[i]; -// @SuppressWarnings("unchecked") -// T value = (T) inputs[i + 1]; -// map.put(key, value); -// } -// return map; -// } -//} +package com.azure.ai.projects; + +import com.azure.core.http.HttpClient; +import com.openai.core.RequestOptions; +import com.openai.core.Timeout; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import java.time.Duration; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import static com.azure.ai.projects.TestUtils.DISPLAY_NAME_WITH_ARGUMENTS; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class EvaluationsAsyncClientTest extends ClientTestBase { + + @Disabled("Flaky test") + @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) + @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") + public void timeoutResponse(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + EvaluationsAsyncClient client = getEvaluationsAsyncClient(httpClient, serviceVersion); + RequestOptions requestOptions + = RequestOptions.builder().timeout(Timeout.builder().read(Duration.ofMillis(1)).build()).build(); + + ExecutionException thrown = assertThrows(ExecutionException.class, + () -> client.getOpenAIClient().retrieve("I probably don't exist", requestOptions).get()); + assertInstanceOf(TimeoutException.class, thrown.getCause()); + } + // + // private AIProjectClientBuilder clientBuilder; + // private EvaluationsAsyncClient evaluationsAsyncClient; + // private DatasetsAsyncClient datasetsAsyncClient; + // + // private void setup(HttpClient httpClient) { + // clientBuilder = getClientBuilder(httpClient); + // evaluationsAsyncClient = clientBuilder.buildEvaluationsAsyncClient(); + // datasetsAsyncClient = clientBuilder.buildDatasetsAsyncClient(); + // } + // + // /** + // * Helper method to verify an Evaluation has valid properties. + // * @param evaluation The evaluation to validate + // * @param expectedDisplayName The expected display name of the evaluation, or null if no specific name is expected + // * @param expectedStatus The expected status, or null if no specific status is expected + // */ + // private void assertValidEvaluation(Evaluation evaluation, String expectedDisplayName, String expectedStatus) { + // Assertions.assertNotNull(evaluation); + // Assertions.assertNotNull(evaluation.getDisplayName()); + // Assertions.assertNotNull(evaluation.getStatus()); + // Assertions.assertNotNull(evaluation.getData()); + // Assertions.assertNotNull(evaluation.getData().getType()); + // Assertions.assertNotNull(evaluation.getEvaluators()); + // Assertions.assertFalse(evaluation.getEvaluators().isEmpty()); + // + // if (expectedDisplayName != null) { + // Assertions.assertEquals(expectedDisplayName, evaluation.getDisplayName()); + // } + // + // if (expectedStatus != null) { + // Assertions.assertEquals(expectedStatus, evaluation.getStatus()); + // } + // } + // + // @Disabled + // @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) + // @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") + // public void testListEvaluationsAsync(HttpClient httpClient) { + // setup(httpClient); + // + // // Verify that listing evaluations returns results + // AtomicBoolean hasAtLeastOneEvaluation = new AtomicBoolean(false); + // + // StepVerifier.create(evaluationsAsyncClient.listEvaluations().take(1)).assertNext(evaluation -> { + // hasAtLeastOneEvaluation.set(true); + // assertValidEvaluation(evaluation, null, null); + // }).verifyComplete(); + // + // // Note: This test will pass even if there are no evaluations, + // // as we're only verifying the API works correctly + // System.out.println("Evaluation list retrieved successfully" + // + (hasAtLeastOneEvaluation.get() ? " with at least one evaluation" : " (empty list)")); + // } + // + // @Disabled + // @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) + // @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") + // public void testGetEvaluationAsync(HttpClient httpClient) { + // setup(httpClient); + // + // String evaluationId = Configuration.getGlobalConfiguration().get("TEST_EVALUATION_ID", "test-evaluation-id"); + // + // StepVerifier.create(evaluationsAsyncClient.getEvaluation(evaluationId).doOnNext(evaluation -> { + // // Verify the evaluation properties + // assertValidEvaluation(evaluation, null, null); + // + // if (evaluation.getTags() != null) { + // // Verify tags are properly structured if present + // evaluation.getTags().forEach((key, value) -> { + // Assertions.assertNotNull(key); + // Assertions.assertNotNull(value); + // }); + // } + // + // System.out.println("Evaluation retrieved successfully: " + evaluation.getDisplayName()); + // System.out.println("Status: " + evaluation.getStatus()); + // }).timeout(Duration.ofSeconds(30))).verifyComplete(); + // } + // + // @Disabled + // @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) + // @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") + // public void testCreateEvaluationAsync(HttpClient httpClient) { + // setup(httpClient); + // + // String datasetName = Configuration.getGlobalConfiguration().get("TEST_DATASET_NAME", "test-dataset"); + // String version = Configuration.getGlobalConfiguration().get("TEST_DATASET_VERSION", "1"); + // + // // Get a dataset to use for the evaluation + // Mono datasetVersionMono = datasetsAsyncClient.getDatasetVersion(datasetName, version); + // + // StepVerifier.create(datasetVersionMono.flatMap(datasetVersion -> { + // // Create evaluation definition + // InputDataset dataset = new InputDataset(datasetVersion.getId()); + // Evaluation evaluationToCreate = new Evaluation(dataset, + // mapOf("relevance", + // new EvaluatorConfiguration(EvaluatorId.RELEVANCE.getValue()) + // .setInitParams(mapOf("deployment_name", BinaryData.fromObject("gpt-4o"))))) + // .setDisplayName("Test Async Evaluation " + System.currentTimeMillis()) + // .setDescription("This is a test evaluation created by the EvaluationsAsyncClientTest"); + // + // // Create the evaluation + // return evaluationsAsyncClient.createEvaluation(evaluationToCreate); + // })).assertNext(createdEvaluation -> { + // // Verify the created evaluation + // assertValidEvaluation(createdEvaluation, null, null); + // Assertions.assertTrue(createdEvaluation.getDisplayName().startsWith("Test Async Evaluation")); + // Assertions.assertTrue(createdEvaluation.getEvaluators().containsKey("relevance")); + // + // System.out.println("Evaluation created successfully: " + createdEvaluation.getDisplayName()); + // System.out.println("Initial status: " + createdEvaluation.getStatus()); + // }).verifyComplete(); + // } + // + // @Disabled + // @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) + // @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") + // public void testEvaluationStatusCheckAsync(HttpClient httpClient) { + // setup(httpClient); + // + // String evaluationId = Configuration.getGlobalConfiguration().get("TEST_EVALUATION_ID", "test-evaluation-id"); + // + // StepVerifier.create(evaluationsAsyncClient.getEvaluation(evaluationId)).assertNext(evaluation -> { + // // Verify status is one of the expected values + // Assertions.assertNotNull(evaluation.getStatus()); + // String status = evaluation.getStatus(); + // + // // Status should be one of: Running, Succeeded, Failed, Canceled, etc. + // boolean isValidStatus = "Running".equals(status) + // || "Succeeded".equals(status) + // || "Failed".equals(status) + // || "Canceled".equals(status) + // || "Queued".equals(status) + // || "Created".equals(status); + // + // Assertions.assertTrue(isValidStatus, "Unexpected evaluation status: " + status); + // + // System.out.println("Evaluation status check passed: " + status); + // }).verifyComplete(); + // } + // + // // Helper method for creating maps + // private static Map mapOf(Object... inputs) { + // Map map = new HashMap<>(); + // for (int i = 0; i < inputs.length; i += 2) { + // String key = (String) inputs[i]; + // @SuppressWarnings("unchecked") + // T value = (T) inputs[i + 1]; + // map.put(key, value); + // } + // return map; + // } +} diff --git a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/EvaluationsClientTest.java b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/EvaluationsClientTest.java index b71bb9c0fad6..3c68fff8f663 100644 --- a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/EvaluationsClientTest.java +++ b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/EvaluationsClientTest.java @@ -1,197 +1,207 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -//package com.azure.ai.projects; -// -//import com.azure.ai.projects.models.DatasetVersion; -//import com.azure.ai.projects.models.Evaluation; -//import com.azure.ai.projects.models.EvaluatorConfiguration; -//import com.azure.ai.projects.models.EvaluatorId; -//import com.azure.ai.projects.models.InputDataset; -//import com.azure.core.http.HttpClient; -//import com.azure.core.util.BinaryData; -//import com.azure.core.util.Configuration; -//import org.junit.jupiter.api.Assertions; -//import org.junit.jupiter.api.Disabled; -//import org.junit.jupiter.params.ParameterizedTest; -//import org.junit.jupiter.params.provider.MethodSource; -//import java.util.HashMap; -//import java.util.Map; -// -//import static com.azure.ai.projects.TestUtils.DISPLAY_NAME_WITH_ARGUMENTS; -// -//public class EvaluationsClientTest extends ClientTestBase { -// -// private AIProjectClientBuilder clientBuilder; -// private EvaluationsClient evaluationsClient; -// private DatasetsClient datasetsClient; -// -// private void setup(HttpClient httpClient) { -// clientBuilder = getClientBuilder(httpClient); -// evaluationsClient = clientBuilder.buildEvaluationsClient(); -// datasetsClient = clientBuilder.buildDatasetsClient(); -// } -// -// /** -// * Helper method to verify an Evaluation has valid properties. -// * @param evaluation The evaluation to validate -// * @param expectedDisplayName The expected display name of the evaluation, or null if no specific name is expected -// * @param expectedStatus The expected status, or null if no specific status is expected -// */ -// private void assertValidEvaluation(Evaluation evaluation, String expectedDisplayName, String expectedStatus) { -// Assertions.assertNotNull(evaluation); -// Assertions.assertNotNull(evaluation.getDisplayName()); -// Assertions.assertNotNull(evaluation.getStatus()); -// Assertions.assertNotNull(evaluation.getData()); -// Assertions.assertNotNull(evaluation.getData().getType()); -// Assertions.assertNotNull(evaluation.getEvaluators()); -// Assertions.assertFalse(evaluation.getEvaluators().isEmpty()); -// -// if (expectedDisplayName != null) { -// Assertions.assertEquals(expectedDisplayName, evaluation.getDisplayName()); -// } -// -// if (expectedStatus != null) { -// Assertions.assertEquals(expectedStatus, evaluation.getStatus()); -// } -// } -// -// @Disabled -// @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) -// @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") -// public void testListEvaluations(HttpClient httpClient) { -// setup(httpClient); -// -// // Verify that listing evaluations returns results -// Iterable evaluations = evaluationsClient.listEvaluations(); -// Assertions.assertNotNull(evaluations); -// -// // Verify that at least one evaluation can be retrieved if available -// boolean hasAtLeastOneEvaluation = false; -// for (Evaluation evaluation : evaluations) { -// hasAtLeastOneEvaluation = true; -// assertValidEvaluation(evaluation, null, null); -// break; -// } -// -// // Note: This test will pass even if there are no evaluations, -// // as we're only verifying the API works correctly -// System.out.println("Evaluation list retrieved successfully" -// + (hasAtLeastOneEvaluation ? " with at least one evaluation" : " (empty list)")); -// } -// -// @Disabled -// @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) -// @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") -// public void testGetEvaluation(HttpClient httpClient) { -// setup(httpClient); -// -// String evaluationId = Configuration.getGlobalConfiguration().get("TEST_EVALUATION_ID", "test-evaluation-id"); -// -// try { -// Evaluation evaluation = evaluationsClient.getEvaluation(evaluationId); -// -// // Verify the evaluation properties -// assertValidEvaluation(evaluation, null, null); -// -// if (evaluation.getTags() != null) { -// // Verify tags are properly structured if present -// evaluation.getTags().forEach((key, value) -> { -// Assertions.assertNotNull(key); -// Assertions.assertNotNull(value); -// }); -// } -// -// System.out.println("Evaluation retrieved successfully: " + evaluation.getDisplayName()); -// System.out.println("Status: " + evaluation.getStatus()); -// } catch (Exception e) { -// // If the evaluation doesn't exist, this will throw a ResourceNotFoundException -// // We'll handle this case by printing a message and passing the test -// System.out.println("Evaluation not found: " + evaluationId); -// Assertions.assertTrue(e.getMessage().contains("404") || e.getMessage().contains("Not Found")); -// } -// } -// -// @Disabled -// @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) -// @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") -// public void testCreateEvaluation(HttpClient httpClient) { -// setup(httpClient); -// -// String datasetName = Configuration.getGlobalConfiguration().get("TEST_DATASET_NAME", "test-dataset"); -// String version = Configuration.getGlobalConfiguration().get("TEST_DATASET_VERSION", "1"); -// -// try { -// // Get a dataset to use for the evaluation -// DatasetVersion datasetVersion = datasetsClient.getDatasetVersion(datasetName, version); -// -// // Create evaluation definition -// InputDataset dataset = new InputDataset(datasetVersion.getId()); -// Evaluation evaluationToCreate = new Evaluation(dataset, -// mapOf("relevance", -// new EvaluatorConfiguration(EvaluatorId.RELEVANCE.getValue()) -// .setInitParams(mapOf("deployment_name", BinaryData.fromObject("gpt-4o"))))) -// .setDisplayName("Test Evaluation " + System.currentTimeMillis()) -// .setDescription("This is a test evaluation created by the EvaluationsClientTest"); -// -// // Create the evaluation -// Evaluation createdEvaluation = evaluationsClient.createEvaluation(evaluationToCreate); -// -// // Verify the created evaluation -// assertValidEvaluation(createdEvaluation, evaluationToCreate.getDisplayName(), null); -// Assertions.assertEquals(evaluationToCreate.getDescription(), createdEvaluation.getDescription()); -// Assertions.assertTrue(createdEvaluation.getEvaluators().containsKey("relevance")); -// -// System.out.println("Evaluation created successfully: " + createdEvaluation.getDisplayName()); -// System.out.println("Initial status: " + createdEvaluation.getStatus()); -// } catch (Exception e) { -// // If the dataset doesn't exist or there's another issue -// System.out.println("Failed to create evaluation: " + e.getMessage()); -// throw e; -// } -// } -// -// @Disabled -// @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) -// @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") -// public void testEvaluationStatusCheck(HttpClient httpClient) { -// setup(httpClient); -// -// String evaluationId = Configuration.getGlobalConfiguration().get("TEST_EVALUATION_ID", "test-evaluation-id"); -// -// try { -// Evaluation evaluation = evaluationsClient.getEvaluation(evaluationId); -// -// // Verify status is one of the expected values -// Assertions.assertNotNull(evaluation.getStatus()); -// String status = evaluation.getStatus(); -// -// // Status should be one of: Running, Succeeded, Failed, Canceled, etc. -// boolean isValidStatus = "Running".equals(status) -// || "Succeeded".equals(status) -// || "Failed".equals(status) -// || "Canceled".equals(status) -// || "Queued".equals(status) -// || "Created".equals(status); -// -// Assertions.assertTrue(isValidStatus, "Unexpected evaluation status: " + status); -// -// System.out.println("Evaluation status check passed: " + status); -// } catch (Exception e) { -// // If the evaluation doesn't exist, this will throw a ResourceNotFoundException -// System.out.println("Evaluation not found for status check: " + evaluationId); -// Assertions.assertTrue(e.getMessage().contains("404") || e.getMessage().contains("Not Found")); -// } -// } -// -// // Helper method for creating maps -// private static Map mapOf(Object... inputs) { -// Map map = new HashMap<>(); -// for (int i = 0; i < inputs.length; i += 2) { -// String key = (String) inputs[i]; -// @SuppressWarnings("unchecked") -// T value = (T) inputs[i + 1]; -// map.put(key, value); -// } -// return map; -// } -//} +package com.azure.ai.projects; + +import com.azure.core.http.HttpClient; +import com.openai.core.RequestOptions; +import com.openai.core.Timeout; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.time.Duration; +import java.util.concurrent.TimeoutException; + +import static com.azure.ai.projects.TestUtils.DISPLAY_NAME_WITH_ARGUMENTS; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class EvaluationsClientTest extends ClientTestBase { + + @Disabled("Flaky test") + @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) + @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") + public void timeoutResponse(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + EvaluationsClient client = getEvaluationsClient(httpClient, serviceVersion); + + RequestOptions requestOptions + = RequestOptions.builder().timeout(Timeout.builder().read(Duration.ofMillis(1)).build()).build(); + RuntimeException thrown = assertThrows(RuntimeException.class, + () -> client.getOpenAIClient().retrieve("I probably don't exist", requestOptions)); + assertInstanceOf(TimeoutException.class, thrown.getCause()); + } + + // private AIProjectClientBuilder clientBuilder; + // private EvaluationsClient evaluationsClient; + // private DatasetsClient datasetsClient; + // + // private void setup(HttpClient httpClient) { + // clientBuilder = getClientBuilder(httpClient); + // evaluationsClient = clientBuilder.buildEvaluationsClient(); + // datasetsClient = clientBuilder.buildDatasetsClient(); + // } + // + // /** + // * Helper method to verify an Evaluation has valid properties. + // * @param evaluation The evaluation to validate + // * @param expectedDisplayName The expected display name of the evaluation, or null if no specific name is expected + // * @param expectedStatus The expected status, or null if no specific status is expected + // */ + // private void assertValidEvaluation(Evaluation evaluation, String expectedDisplayName, String expectedStatus) { + // Assertions.assertNotNull(evaluation); + // Assertions.assertNotNull(evaluation.getDisplayName()); + // Assertions.assertNotNull(evaluation.getStatus()); + // Assertions.assertNotNull(evaluation.getData()); + // Assertions.assertNotNull(evaluation.getData().getType()); + // Assertions.assertNotNull(evaluation.getEvaluators()); + // Assertions.assertFalse(evaluation.getEvaluators().isEmpty()); + // + // if (expectedDisplayName != null) { + // Assertions.assertEquals(expectedDisplayName, evaluation.getDisplayName()); + // } + // + // if (expectedStatus != null) { + // Assertions.assertEquals(expectedStatus, evaluation.getStatus()); + // } + // } + // + // @Disabled + // @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) + // @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") + // public void testListEvaluations(HttpClient httpClient) { + // setup(httpClient); + // + // // Verify that listing evaluations returns results + // Iterable evaluations = evaluationsClient.listEvaluations(); + // Assertions.assertNotNull(evaluations); + // + // // Verify that at least one evaluation can be retrieved if available + // boolean hasAtLeastOneEvaluation = false; + // for (Evaluation evaluation : evaluations) { + // hasAtLeastOneEvaluation = true; + // assertValidEvaluation(evaluation, null, null); + // break; + // } + // + // // Note: This test will pass even if there are no evaluations, + // // as we're only verifying the API works correctly + // System.out.println("Evaluation list retrieved successfully" + // + (hasAtLeastOneEvaluation ? " with at least one evaluation" : " (empty list)")); + // } + // + // @Disabled + // @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) + // @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") + // public void testGetEvaluation(HttpClient httpClient) { + // setup(httpClient); + // + // String evaluationId = Configuration.getGlobalConfiguration().get("TEST_EVALUATION_ID", "test-evaluation-id"); + // + // try { + // Evaluation evaluation = evaluationsClient.getEvaluation(evaluationId); + // + // // Verify the evaluation properties + // assertValidEvaluation(evaluation, null, null); + // + // if (evaluation.getTags() != null) { + // // Verify tags are properly structured if present + // evaluation.getTags().forEach((key, value) -> { + // Assertions.assertNotNull(key); + // Assertions.assertNotNull(value); + // }); + // } + // + // System.out.println("Evaluation retrieved successfully: " + evaluation.getDisplayName()); + // System.out.println("Status: " + evaluation.getStatus()); + // } catch (Exception e) { + // // If the evaluation doesn't exist, this will throw a ResourceNotFoundException + // // We'll handle this case by printing a message and passing the test + // System.out.println("Evaluation not found: " + evaluationId); + // Assertions.assertTrue(e.getMessage().contains("404") || e.getMessage().contains("Not Found")); + // } + // } + // + // @Disabled + // @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) + // @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") + // public void testCreateEvaluation(HttpClient httpClient) { + // setup(httpClient); + // + // String datasetName = Configuration.getGlobalConfiguration().get("TEST_DATASET_NAME", "test-dataset"); + // String version = Configuration.getGlobalConfiguration().get("TEST_DATASET_VERSION", "1"); + // + // try { + // // Get a dataset to use for the evaluation + // DatasetVersion datasetVersion = datasetsClient.getDatasetVersion(datasetName, version); + // + // // Create evaluation definition + // InputDataset dataset = new InputDataset(datasetVersion.getId()); + // Evaluation evaluationToCreate = new Evaluation(dataset, + // mapOf("relevance", + // new EvaluatorConfiguration(EvaluatorId.RELEVANCE.getValue()) + // .setInitParams(mapOf("deployment_name", BinaryData.fromObject("gpt-4o"))))) + // .setDisplayName("Test Evaluation " + System.currentTimeMillis()) + // .setDescription("This is a test evaluation created by the EvaluationsClientTest"); + // + // // Create the evaluation + // Evaluation createdEvaluation = evaluationsClient.createEvaluation(evaluationToCreate); + // + // // Verify the created evaluation + // assertValidEvaluation(createdEvaluation, evaluationToCreate.getDisplayName(), null); + // Assertions.assertEquals(evaluationToCreate.getDescription(), createdEvaluation.getDescription()); + // Assertions.assertTrue(createdEvaluation.getEvaluators().containsKey("relevance")); + // + // System.out.println("Evaluation created successfully: " + createdEvaluation.getDisplayName()); + // System.out.println("Initial status: " + createdEvaluation.getStatus()); + // } catch (Exception e) { + // // If the dataset doesn't exist or there's another issue + // System.out.println("Failed to create evaluation: " + e.getMessage()); + // throw e; + // } + // } + // + // @Disabled + // @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) + // @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") + // public void testEvaluationStatusCheck(HttpClient httpClient) { + // setup(httpClient); + // + // String evaluationId = Configuration.getGlobalConfiguration().get("TEST_EVALUATION_ID", "test-evaluation-id"); + // + // try { + // Evaluation evaluation = evaluationsClient.getEvaluation(evaluationId); + // + // // Verify status is one of the expected values + // Assertions.assertNotNull(evaluation.getStatus()); + // String status = evaluation.getStatus(); + // + // // Status should be one of: Running, Succeeded, Failed, Canceled, etc. + // boolean isValidStatus = "Running".equals(status) + // || "Succeeded".equals(status) + // || "Failed".equals(status) + // || "Canceled".equals(status) + // || "Queued".equals(status) + // || "Created".equals(status); + // + // Assertions.assertTrue(isValidStatus, "Unexpected evaluation status: " + status); + // + // System.out.println("Evaluation status check passed: " + status); + // } catch (Exception e) { + // // If the evaluation doesn't exist, this will throw a ResourceNotFoundException + // System.out.println("Evaluation not found for status check: " + evaluationId); + // Assertions.assertTrue(e.getMessage().contains("404") || e.getMessage().contains("Not Found")); + // } + // } + // + // // Helper method for creating maps + // private static Map mapOf(Object... inputs) { + // Map map = new HashMap<>(); + // for (int i = 0; i < inputs.length; i += 2) { + // String key = (String) inputs[i]; + // @SuppressWarnings("unchecked") + // T value = (T) inputs[i + 1]; + // map.put(key, value); + // } + // return map; + // } +} diff --git a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/IndexesAsyncClientTest.java b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/IndexesAsyncClientTest.java index e6f25cc9ce0e..0161ca805272 100644 --- a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/IndexesAsyncClientTest.java +++ b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/IndexesAsyncClientTest.java @@ -21,40 +21,11 @@ @Disabled("Disabled for lack of recordings. Needs to be enabled on the Public Preview release.") public class IndexesAsyncClientTest extends ClientTestBase { - private AIProjectClientBuilder clientBuilder; - private IndexesAsyncClient indexesAsyncClient; - - private void setup(HttpClient httpClient) { - clientBuilder = getClientBuilder(httpClient); - indexesAsyncClient = clientBuilder.buildIndexesAsyncClient(); - } - - /** - * Helper method to verify an Index has valid properties. - * @param index The index to validate - * @param expectedName The expected name of the index, or null if no specific name is expected - * @param expectedVersion The expected version of the index, or null if no specific version is expected - */ - private void assertValidIndex(Index index, String expectedName, String expectedVersion) { - Assertions.assertNotNull(index); - Assertions.assertNotNull(index.getName()); - Assertions.assertNotNull(index.getVersion()); - Assertions.assertNotNull(index.getType()); - - if (expectedName != null) { - Assertions.assertEquals(expectedName, index.getName()); - } - - if (expectedVersion != null) { - Assertions.assertEquals(expectedVersion, index.getVersion()); - } - } - @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testListIndexesAsync(HttpClient httpClient) { - setup(httpClient); + public void testListIndexesAsync(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + IndexesAsyncClient indexesAsyncClient = getIndexesAsyncClient(httpClient, serviceVersion); // Collect indexes into a list for verification List indexList = new ArrayList<>(); @@ -72,8 +43,8 @@ public void testListIndexesAsync(HttpClient httpClient) { @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testListIndexVersionsAsync(HttpClient httpClient) { - setup(httpClient); + public void testListIndexVersionsAsync(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + IndexesAsyncClient indexesAsyncClient = getIndexesAsyncClient(httpClient, serviceVersion); String indexName = Configuration.getGlobalConfiguration().get("TEST_INDEX_NAME", "test-index"); List versionList = new ArrayList<>(); @@ -97,8 +68,8 @@ public void testListIndexVersionsAsync(HttpClient httpClient) { @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testGetIndexAsync(HttpClient httpClient) { - setup(httpClient); + public void testGetIndexAsync(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + IndexesAsyncClient indexesAsyncClient = getIndexesAsyncClient(httpClient, serviceVersion); String indexName = Configuration.getGlobalConfiguration().get("TEST_INDEX_NAME", "test-index"); String indexVersion = Configuration.getGlobalConfiguration().get("TEST_INDEX_VERSION", "1.0"); @@ -120,8 +91,8 @@ public void testGetIndexAsync(HttpClient httpClient) { @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testCreateOrUpdateIndexAsync(HttpClient httpClient) { - setup(httpClient); + public void testCreateOrUpdateIndexAsync(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + IndexesAsyncClient indexesAsyncClient = getIndexesAsyncClient(httpClient, serviceVersion); // Configuration for creating/updating an index String indexName = Configuration.getGlobalConfiguration().get("TEST_INDEX_NAME", "test-index"); @@ -157,8 +128,8 @@ public void testCreateOrUpdateIndexAsync(HttpClient httpClient) { @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testDeleteIndexAsync(HttpClient httpClient) { - setup(httpClient); + public void testDeleteIndexAsync(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + IndexesAsyncClient indexesAsyncClient = getIndexesAsyncClient(httpClient, serviceVersion); String indexName = Configuration.getGlobalConfiguration().get("TEST_INDEX_NAME", "test-index"); String indexVersion = Configuration.getGlobalConfiguration().get("TEST_INDEX_VERSION", "1.0"); diff --git a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/IndexesClientTest.java b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/IndexesClientTest.java index a821d7919969..863a5a1d9f40 100644 --- a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/IndexesClientTest.java +++ b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/IndexesClientTest.java @@ -16,40 +16,11 @@ @Disabled("Disabled for lack of recordings. Needs to be enabled on the Public Preview release.") public class IndexesClientTest extends ClientTestBase { - private AIProjectClientBuilder clientBuilder; - private IndexesClient indexesClient; - - private void setup(HttpClient httpClient) { - clientBuilder = getClientBuilder(httpClient); - indexesClient = clientBuilder.buildIndexesClient(); - } - - /** - * Helper method to verify an Index has valid properties. - * @param index The index to validate - * @param expectedName The expected name of the index, or null if no specific name is expected - * @param expectedVersion The expected version of the index, or null if no specific version is expected - */ - private void assertValidIndex(Index index, String expectedName, String expectedVersion) { - Assertions.assertNotNull(index); - Assertions.assertNotNull(index.getName()); - Assertions.assertNotNull(index.getVersion()); - Assertions.assertNotNull(index.getType()); - - if (expectedName != null) { - Assertions.assertEquals(expectedName, index.getName()); - } - - if (expectedVersion != null) { - Assertions.assertEquals(expectedVersion, index.getVersion()); - } - } - @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testListIndexes(HttpClient httpClient) { - setup(httpClient); + public void testListIndexes(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + IndexesClient indexesClient = getIndexesClient(httpClient, serviceVersion); // Verify that listing indexes returns results Iterable indexes = indexesClient.listLatest(); @@ -72,8 +43,8 @@ public void testListIndexes(HttpClient httpClient) { @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testListIndexVersions(HttpClient httpClient) { - setup(httpClient); + public void testListIndexVersions(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + IndexesClient indexesClient = getIndexesClient(httpClient, serviceVersion); String indexName = Configuration.getGlobalConfiguration().get("TEST_INDEX_NAME", "test-index"); @@ -103,8 +74,8 @@ public void testListIndexVersions(HttpClient httpClient) { @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testGetIndex(HttpClient httpClient) { - setup(httpClient); + public void testGetIndex(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + IndexesClient indexesClient = getIndexesClient(httpClient, serviceVersion); String indexName = Configuration.getGlobalConfiguration().get("TEST_INDEX_NAME", "test-index"); String indexVersion = Configuration.getGlobalConfiguration().get("TEST_INDEX_VERSION", "1.0"); @@ -129,8 +100,8 @@ public void testGetIndex(HttpClient httpClient) { @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testCreateOrUpdateIndex(HttpClient httpClient) { - setup(httpClient); + public void testCreateOrUpdateIndex(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + IndexesClient indexesClient = getIndexesClient(httpClient, serviceVersion); // Configuration for creating/updating an index String indexName = Configuration.getGlobalConfiguration().get("TEST_INDEX_NAME", "test-index"); @@ -168,8 +139,8 @@ public void testCreateOrUpdateIndex(HttpClient httpClient) { @Disabled @ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS) @MethodSource("com.azure.ai.projects.TestUtils#getTestParameters") - public void testDeleteIndex(HttpClient httpClient) { - setup(httpClient); + public void testDeleteIndex(HttpClient httpClient, AIProjectsServiceVersion serviceVersion) { + IndexesClient indexesClient = getIndexesClient(httpClient, serviceVersion); String indexName = Configuration.getGlobalConfiguration().get("TEST_INDEX_NAME", "test-index"); String indexVersion = Configuration.getGlobalConfiguration().get("TEST_INDEX_VERSION", "1.0"); diff --git a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/TestUtils.java b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/TestUtils.java index 3b5e02783245..19e9ed348a45 100644 --- a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/TestUtils.java +++ b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/TestUtils.java @@ -22,10 +22,9 @@ public class TestUtils { * @return A stream of HttpClients to test. */ static Stream getTestParameters() { - // when this issues is closed, the newer version of junit will have better support for - // cartesian product of arguments - https://github.com/junit-team/junit5/issues/1427 List argumentsList = new ArrayList<>(); - getHttpClients().forEach(httpClient -> argumentsList.add(Arguments.of(httpClient))); + getHttpClients().forEach( + httpClient -> argumentsList.add(Arguments.of(httpClient, AIProjectsServiceVersion.V2025_11_15_PREVIEW))); return argumentsList.stream(); } } From e1ad861eddf6992c365ebed7b6b96df6072cf1ee Mon Sep 17 00:00:00 2001 From: Jose Alvarez Date: Wed, 14 Jan 2026 14:37:28 +0100 Subject: [PATCH 5/6] Added tests for httpclient helpers --- .../http/HttpClientHelperTests.java | 306 ++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/implementation/http/HttpClientHelperTests.java diff --git a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/implementation/http/HttpClientHelperTests.java b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/implementation/http/HttpClientHelperTests.java new file mode 100644 index 000000000000..f67de408b535 --- /dev/null +++ b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/implementation/http/HttpClientHelperTests.java @@ -0,0 +1,306 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.ai.projects.implementation.http; + +import com.azure.core.http.HttpClient; +import com.azure.core.http.HttpHeaders; +import com.azure.core.http.HttpPipelineBuilder; +import com.azure.core.http.HttpRequest; +import com.azure.core.http.HttpResponse; +import com.azure.core.test.http.MockHttpResponse; +import com.azure.core.util.Context; +import com.openai.core.http.HttpRequestBody; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import reactor.core.publisher.Mono; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +class HttpClientHelperTests { + + @Test + void executeAsyncCompletesSuccessfully() { + RecordingHttpClient recordingClient + = new RecordingHttpClient(request -> createMockResponse(request, 204, new HttpHeaders(), "")); + com.openai.core.http.HttpClient openAiClient + = HttpClientHelper.mapToOpenAIHttpClient(new HttpPipelineBuilder().httpClient(recordingClient).build()); + + com.openai.core.http.HttpRequest openAiRequest = createOpenAiRequest(); + + CompletableFuture future = openAiClient.executeAsync(openAiRequest); + try (com.openai.core.http.HttpResponse response = future.join()) { + assertEquals(204, response.statusCode()); + } catch (Exception e) { + fail("Exception thrown while reading response", e); + } + assertEquals(1, recordingClient.getSendCount()); + } + + @Test + void executeWithNullRequestBodySucceeds() throws Exception { + RecordingHttpClient recordingClient = new RecordingHttpClient(request -> { + // Verify the request has no body (or empty body) + com.azure.core.util.BinaryData bodyData = request.getBodyAsBinaryData(); + if (bodyData != null) { + assertEquals(0, bodyData.toBytes().length); + } + return createMockResponse(request, 200, new HttpHeaders(), "success"); + }); + com.openai.core.http.HttpClient openAiClient + = HttpClientHelper.mapToOpenAIHttpClient(new HttpPipelineBuilder().httpClient(recordingClient).build()); + + com.openai.core.http.HttpRequest openAiRequest = com.openai.core.http.HttpRequest.builder() + .method(com.openai.core.http.HttpMethod.GET) + .baseUrl("https://example.com") + .addPathSegment("test") + .build(); + + try (com.openai.core.http.HttpResponse response = openAiClient.execute(openAiRequest)) { + assertEquals(200, response.statusCode()); + assertEquals("success", new String(readAllBytes(response.body()), StandardCharsets.UTF_8)); + } + } + + @Disabled("Body gets eagerly evaluated. Instrumentation could be wrong.") + @Test + void executeThrowsUncheckedIOExceptionOnBodyBufferingFailure() { + RecordingHttpClient recordingClient + = new RecordingHttpClient(request -> createMockResponse(request, 200, new HttpHeaders(), "")); + com.openai.core.http.HttpClient openAiClient + = HttpClientHelper.mapToOpenAIHttpClient(new HttpPipelineBuilder().httpClient(recordingClient).build()); + + com.openai.core.http.HttpRequest openAiRequest = com.openai.core.http.HttpRequest.builder() + .method(com.openai.core.http.HttpMethod.POST) + .baseUrl("https://example.com") + .body(new FailingHttpRequestBody()) + .build(); + + RuntimeException exception = assertThrows(RuntimeException.class, () -> { + openAiClient.execute(openAiRequest); + }); + // Verify the error is related to body buffering failure + boolean hasBufferMessage = exception.getMessage() != null && exception.getMessage().contains("buffer"); + boolean hasIOCause = exception.getCause() instanceof IOException; + assertTrue(hasBufferMessage || hasIOCause, "Expected error related to buffer failure, got: " + exception); + } + + @Test + void executeThrowsExceptionOnMalformedUrl() { + RecordingHttpClient recordingClient + = new RecordingHttpClient(request -> createMockResponse(request, 200, new HttpHeaders(), "")); + com.openai.core.http.HttpClient openAiClient + = HttpClientHelper.mapToOpenAIHttpClient(new HttpPipelineBuilder().httpClient(recordingClient).build()); + + com.openai.core.http.HttpRequest openAiRequest = com.openai.core.http.HttpRequest.builder() + .method(com.openai.core.http.HttpMethod.GET) + .baseUrl("not-a-valid-url") + .build(); + + // Malformed URLs should throw an exception (typically IllegalArgumentException or IllegalStateException) + assertThrows(RuntimeException.class, () -> { + openAiClient.execute(openAiRequest); + }); + } + + @Disabled("Body gets eagerly evaluated. Instrumentation could be wrong.") + @Test + void executeAsyncPropagatesRequestBuildingErrors() { + RecordingHttpClient recordingClient + = new RecordingHttpClient(request -> createMockResponse(request, 200, new HttpHeaders(), "")); + com.openai.core.http.HttpClient openAiClient + = HttpClientHelper.mapToOpenAIHttpClient(new HttpPipelineBuilder().httpClient(recordingClient).build()); + + com.openai.core.http.HttpRequest openAiRequest = com.openai.core.http.HttpRequest.builder() + .method(com.openai.core.http.HttpMethod.POST) + .baseUrl("https://example.com") + .body(new FailingHttpRequestBody()) + .build(); + + CompletableFuture future = openAiClient.executeAsync(openAiRequest); + + Exception exception = assertThrows(Exception.class, future::join); + Throwable cause = exception.getCause(); + assertNotNull(cause, "Expected a cause for the exception"); + // The error should be related to request building/buffering failure + assertTrue(cause instanceof RuntimeException, "Expected RuntimeException, got: " + cause.getClass().getName()); + } + + @Test + void executeAsyncPropagatesHttpClientFailures() { + FailingHttpClient failingClient = new FailingHttpClient(new RuntimeException("Network error")); + com.openai.core.http.HttpClient openAiClient + = HttpClientHelper.mapToOpenAIHttpClient(new HttpPipelineBuilder().httpClient(failingClient).build()); + + com.openai.core.http.HttpRequest openAiRequest = com.openai.core.http.HttpRequest.builder() + .method(com.openai.core.http.HttpMethod.GET) + .baseUrl("https://example.com") + .build(); + + CompletableFuture future = openAiClient.executeAsync(openAiRequest); + + Exception exception = assertThrows(Exception.class, future::join); + Throwable cause = exception.getCause(); + assertNotNull(cause); + assertTrue(cause instanceof RuntimeException); + assertEquals("Network error", cause.getMessage()); + } + + private static com.openai.core.http.HttpRequest createOpenAiRequest() { + return com.openai.core.http.HttpRequest.builder() + .method(com.openai.core.http.HttpMethod.POST) + .baseUrl("https://example.com") + .addPathSegment("path") + .addPathSegment("segment") + .putHeader("X-Test", "alpha") + .putHeaders("X-Multi", Arrays.asList("first", "second")) + .putQueryParam("q", "a b") + .body(new TestHttpRequestBody("payload", "text/plain")) + .build(); + } + + private static MockHttpResponse createMockResponse(HttpRequest request, int statusCode, HttpHeaders headers, + String body) { + byte[] bytes = body == null ? new byte[0] : body.getBytes(StandardCharsets.UTF_8); + return new MockHttpResponse(request, statusCode, headers, bytes); + } + + private static byte[] readAllBytes(InputStream stream) throws IOException { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + byte[] chunk = new byte[4096]; + int read; + while ((read = stream.read(chunk)) != -1) { + buffer.write(chunk, 0, read); + } + return buffer.toByteArray(); + } + + private static final class RecordingHttpClient implements HttpClient { + private final Function responseFactory; + private HttpRequest lastRequest; + private int sendCount; + + private RecordingHttpClient(Function responseFactory) { + this.responseFactory = responseFactory; + } + + @Override + public Mono send(HttpRequest request) { + this.lastRequest = request; + this.sendCount++; + return Mono.just(responseFactory.apply(request)); + } + + @Override + public Mono send(HttpRequest request, Context context) { + return send(request); + } + + HttpRequest getLastRequest() { + return lastRequest; + } + + int getSendCount() { + return sendCount; + } + } + + private static final class TestHttpRequestBody implements HttpRequestBody { + private final byte[] content; + private final String contentType; + + private TestHttpRequestBody(String content, String contentType) { + this.content = content.getBytes(StandardCharsets.UTF_8); + this.contentType = contentType; + } + + @Override + public void writeTo(OutputStream outputStream) { + try { + outputStream.write(content); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public String contentType() { + return contentType; + } + + @Override + public long contentLength() { + return content.length; + } + + @Override + public boolean repeatable() { + return true; + } + + @Override + public void close() { + // no-op + } + } + + private static final class FailingHttpRequestBody implements HttpRequestBody { + @Override + public void writeTo(OutputStream outputStream) { + // Simulate an I/O failure during body write + throw new UncheckedIOException(new IOException("Simulated I/O failure during body write")); + } + + @Override + public String contentType() { + return "application/octet-stream"; + } + + @Override + public long contentLength() { + return -1; + } + + @Override + public boolean repeatable() { + return false; + } + + @Override + public void close() { + // no-op + } + } + + private static final class FailingHttpClient implements HttpClient { + private final RuntimeException error; + + private FailingHttpClient(RuntimeException error) { + this.error = error; + } + + @Override + public Mono send(HttpRequest request) { + return Mono.error(error); + } + + @Override + public Mono send(HttpRequest request, Context context) { + return send(request); + } + } +} \ No newline at end of file From 84159454959417a91f7746c4eee70d2f5993a240 Mon Sep 17 00:00:00 2001 From: Jose Alvarez Date: Wed, 14 Jan 2026 14:47:08 +0100 Subject: [PATCH 6/6] Formatter --- .../http/HttpClientHelperTests.java | 78 +++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/implementation/http/HttpClientHelperTests.java b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/implementation/http/HttpClientHelperTests.java index f67de408b535..a24bdb41b087 100644 --- a/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/implementation/http/HttpClientHelperTests.java +++ b/sdk/ai/azure-ai-projects/src/test/java/com/azure/ai/projects/implementation/http/HttpClientHelperTests.java @@ -36,9 +36,9 @@ class HttpClientHelperTests { @Test void executeAsyncCompletesSuccessfully() { RecordingHttpClient recordingClient - = new RecordingHttpClient(request -> createMockResponse(request, 204, new HttpHeaders(), "")); + = new RecordingHttpClient(request -> createMockResponse(request, 204, new HttpHeaders(), "")); com.openai.core.http.HttpClient openAiClient - = HttpClientHelper.mapToOpenAIHttpClient(new HttpPipelineBuilder().httpClient(recordingClient).build()); + = HttpClientHelper.mapToOpenAIHttpClient(new HttpPipelineBuilder().httpClient(recordingClient).build()); com.openai.core.http.HttpRequest openAiRequest = createOpenAiRequest(); @@ -62,13 +62,13 @@ void executeWithNullRequestBodySucceeds() throws Exception { return createMockResponse(request, 200, new HttpHeaders(), "success"); }); com.openai.core.http.HttpClient openAiClient - = HttpClientHelper.mapToOpenAIHttpClient(new HttpPipelineBuilder().httpClient(recordingClient).build()); + = HttpClientHelper.mapToOpenAIHttpClient(new HttpPipelineBuilder().httpClient(recordingClient).build()); com.openai.core.http.HttpRequest openAiRequest = com.openai.core.http.HttpRequest.builder() - .method(com.openai.core.http.HttpMethod.GET) - .baseUrl("https://example.com") - .addPathSegment("test") - .build(); + .method(com.openai.core.http.HttpMethod.GET) + .baseUrl("https://example.com") + .addPathSegment("test") + .build(); try (com.openai.core.http.HttpResponse response = openAiClient.execute(openAiRequest)) { assertEquals(200, response.statusCode()); @@ -80,15 +80,15 @@ void executeWithNullRequestBodySucceeds() throws Exception { @Test void executeThrowsUncheckedIOExceptionOnBodyBufferingFailure() { RecordingHttpClient recordingClient - = new RecordingHttpClient(request -> createMockResponse(request, 200, new HttpHeaders(), "")); + = new RecordingHttpClient(request -> createMockResponse(request, 200, new HttpHeaders(), "")); com.openai.core.http.HttpClient openAiClient - = HttpClientHelper.mapToOpenAIHttpClient(new HttpPipelineBuilder().httpClient(recordingClient).build()); + = HttpClientHelper.mapToOpenAIHttpClient(new HttpPipelineBuilder().httpClient(recordingClient).build()); com.openai.core.http.HttpRequest openAiRequest = com.openai.core.http.HttpRequest.builder() - .method(com.openai.core.http.HttpMethod.POST) - .baseUrl("https://example.com") - .body(new FailingHttpRequestBody()) - .build(); + .method(com.openai.core.http.HttpMethod.POST) + .baseUrl("https://example.com") + .body(new FailingHttpRequestBody()) + .build(); RuntimeException exception = assertThrows(RuntimeException.class, () -> { openAiClient.execute(openAiRequest); @@ -102,14 +102,14 @@ void executeThrowsUncheckedIOExceptionOnBodyBufferingFailure() { @Test void executeThrowsExceptionOnMalformedUrl() { RecordingHttpClient recordingClient - = new RecordingHttpClient(request -> createMockResponse(request, 200, new HttpHeaders(), "")); + = new RecordingHttpClient(request -> createMockResponse(request, 200, new HttpHeaders(), "")); com.openai.core.http.HttpClient openAiClient - = HttpClientHelper.mapToOpenAIHttpClient(new HttpPipelineBuilder().httpClient(recordingClient).build()); + = HttpClientHelper.mapToOpenAIHttpClient(new HttpPipelineBuilder().httpClient(recordingClient).build()); com.openai.core.http.HttpRequest openAiRequest = com.openai.core.http.HttpRequest.builder() - .method(com.openai.core.http.HttpMethod.GET) - .baseUrl("not-a-valid-url") - .build(); + .method(com.openai.core.http.HttpMethod.GET) + .baseUrl("not-a-valid-url") + .build(); // Malformed URLs should throw an exception (typically IllegalArgumentException or IllegalStateException) assertThrows(RuntimeException.class, () -> { @@ -121,15 +121,15 @@ void executeThrowsExceptionOnMalformedUrl() { @Test void executeAsyncPropagatesRequestBuildingErrors() { RecordingHttpClient recordingClient - = new RecordingHttpClient(request -> createMockResponse(request, 200, new HttpHeaders(), "")); + = new RecordingHttpClient(request -> createMockResponse(request, 200, new HttpHeaders(), "")); com.openai.core.http.HttpClient openAiClient - = HttpClientHelper.mapToOpenAIHttpClient(new HttpPipelineBuilder().httpClient(recordingClient).build()); + = HttpClientHelper.mapToOpenAIHttpClient(new HttpPipelineBuilder().httpClient(recordingClient).build()); com.openai.core.http.HttpRequest openAiRequest = com.openai.core.http.HttpRequest.builder() - .method(com.openai.core.http.HttpMethod.POST) - .baseUrl("https://example.com") - .body(new FailingHttpRequestBody()) - .build(); + .method(com.openai.core.http.HttpMethod.POST) + .baseUrl("https://example.com") + .body(new FailingHttpRequestBody()) + .build(); CompletableFuture future = openAiClient.executeAsync(openAiRequest); @@ -144,12 +144,12 @@ void executeAsyncPropagatesRequestBuildingErrors() { void executeAsyncPropagatesHttpClientFailures() { FailingHttpClient failingClient = new FailingHttpClient(new RuntimeException("Network error")); com.openai.core.http.HttpClient openAiClient - = HttpClientHelper.mapToOpenAIHttpClient(new HttpPipelineBuilder().httpClient(failingClient).build()); + = HttpClientHelper.mapToOpenAIHttpClient(new HttpPipelineBuilder().httpClient(failingClient).build()); com.openai.core.http.HttpRequest openAiRequest = com.openai.core.http.HttpRequest.builder() - .method(com.openai.core.http.HttpMethod.GET) - .baseUrl("https://example.com") - .build(); + .method(com.openai.core.http.HttpMethod.GET) + .baseUrl("https://example.com") + .build(); CompletableFuture future = openAiClient.executeAsync(openAiRequest); @@ -162,19 +162,19 @@ void executeAsyncPropagatesHttpClientFailures() { private static com.openai.core.http.HttpRequest createOpenAiRequest() { return com.openai.core.http.HttpRequest.builder() - .method(com.openai.core.http.HttpMethod.POST) - .baseUrl("https://example.com") - .addPathSegment("path") - .addPathSegment("segment") - .putHeader("X-Test", "alpha") - .putHeaders("X-Multi", Arrays.asList("first", "second")) - .putQueryParam("q", "a b") - .body(new TestHttpRequestBody("payload", "text/plain")) - .build(); + .method(com.openai.core.http.HttpMethod.POST) + .baseUrl("https://example.com") + .addPathSegment("path") + .addPathSegment("segment") + .putHeader("X-Test", "alpha") + .putHeaders("X-Multi", Arrays.asList("first", "second")) + .putQueryParam("q", "a b") + .body(new TestHttpRequestBody("payload", "text/plain")) + .build(); } private static MockHttpResponse createMockResponse(HttpRequest request, int statusCode, HttpHeaders headers, - String body) { + String body) { byte[] bytes = body == null ? new byte[0] : body.getBytes(StandardCharsets.UTF_8); return new MockHttpResponse(request, statusCode, headers, bytes); } @@ -303,4 +303,4 @@ public Mono send(HttpRequest request, Context context) { return send(request); } } -} \ No newline at end of file +}