CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-quarkiverse-langchain4j--quarkus-langchain4j-openai-testing-internal

Internal testing utilities for OpenAI integration with Quarkus LangChain4j

Overview
Eval results
Files

index.mddocs/

Quarkus LangChain4j OpenAI Testing Internal

Internal testing utilities for the Quarkus LangChain4j OpenAI integration. This package provides WireMock-based test infrastructure that enables developers to write integration tests for OpenAI API interactions without making actual API calls. It includes a base test class with assertion helpers, a response transformer for dynamic test configuration, and pre-configured WireMock mappings for common OpenAI endpoints.

Package Information

  • Package Name: quarkus-langchain4j-openai-testing-internal
  • Group ID: io.quarkiverse.langchain4j
  • Package Type: maven
  • Language: Java
  • Installation: Add to pom.xml in test scope:
<dependency>
    <groupId>io.quarkiverse.langchain4j</groupId>
    <artifactId>quarkus-langchain4j-openai-testing-internal</artifactId>
    <version>1.7.4</version>
    <scope>test</scope>
</dependency>

Core Imports

import io.quarkiverse.langchain4j.openai.testing.internal.OpenAiBaseTest;
import io.quarkiverse.langchain4j.openai.testing.internal.ChatCompletionTransformer;
import io.quarkiverse.langchain4j.openai.testing.internal.OpenAiBaseTest.MessageContent;

Basic Usage

import io.quarkiverse.langchain4j.openai.testing.internal.OpenAiBaseTest;
import org.junit.jupiter.api.Test;
import java.util.Map;

public class MyOpenAiTest extends OpenAiBaseTest {

    @Test
    public void testChatCompletion() throws Exception {
        // Set expected chat completion response
        setChatCompletionMessageContent("Hello from OpenAI!");

        // Execute your test code that calls OpenAI API
        // The WireMock server will intercept requests

        // Validate the request that was sent
        Map<String, Object> requestAsMap = getRequestAsMap();
        assertSingleRequestMessage(requestAsMap, "Test prompt");
    }
}

Architecture

This testing library is built on WireMock and provides three main components:

  • OpenAiBaseTest: Base test class that extends WiremockAware, providing assertion utilities and request inspection methods for validating OpenAI API interactions
  • ChatCompletionTransformer: WireMock response transformer that dynamically injects chat completion content via system properties, enabling test-specific response configuration
  • WiremockConfigBuilderCustomizer: Automatic configuration of WireMock DevServices with response templating, extension scanning, and pre-configured OpenAI endpoint mappings

Tests extend OpenAiBaseTest to inherit WireMock infrastructure and assertion helpers. The ChatCompletionTransformer automatically loads as a WireMock extension and transforms responses based on test configuration. Pre-configured stub mappings for /v1/chat/completions and /v1/images/generations endpoints are automatically loaded from the classpath.

Capabilities

Base Test Class

OpenAiBaseTest provides the foundation for OpenAI integration tests, extending WiremockAware with OpenAI-specific assertion utilities.

public abstract class OpenAiBaseTest extends WiremockAware {

    /**
     * Jackson ObjectMapper instance for JSON processing.
     * Initialized by beforeAll() lifecycle method.
     * Can be used by test subclasses for custom JSON operations.
     */
    static ObjectMapper mapper;

    /**
     * JUnit 5 lifecycle method that initializes Jackson ObjectMapper for JSON processing.
     * Annotated with @BeforeAll and called automatically before all tests in the class.
     */
    @BeforeAll
    static void beforeAll();

    /**
     * Sets the expected chat completion message content for the current test.
     * This content will be injected into WireMock responses via ChatCompletionTransformer.
     *
     * @param content The message content to return in chat completion responses
     */
    protected void setChatCompletionMessageContent(String content);

    /**
     * JUnit 5 lifecycle method that automatically clears chat completion message content
     * after each test. Annotated with @AfterEach and called automatically.
     */
    @AfterEach
    public void restoreOriginalChatCompletionMessageContent();

    /**
     * Converts the single logged request body to a Map for assertion purposes.
     * Uses Jackson ObjectMapper for JSON deserialization.
     *
     * @return The request body as a Map<String, Object>
     * @throws IOException If JSON parsing fails
     */
    protected Map<String, Object> getRequestAsMap() throws IOException;

    /**
     * Converts raw request body bytes to a Map for assertion purposes.
     * Uses Jackson ObjectMapper for JSON deserialization.
     *
     * @param body The raw request body bytes
     * @return The request body as a Map<String, Object>
     * @throws IOException If JSON parsing fails
     */
    protected Map<String, Object> getRequestAsMap(byte[] body) throws IOException;
}

Message Assertion Helpers

Validate chat completion request message structures.

/**
 * Asserts that the request contains a single message with role "user"
 * and the specified content value.
 *
 * @param requestAsMap The parsed request body as a Map
 * @param value The expected message content
 */
protected static void assertSingleRequestMessage(
    Map<String, Object> requestAsMap,
    String value
);

/**
 * Asserts that the request contains multiple messages matching the provided list.
 * Each MessageContent record specifies the expected role and content for a message.
 *
 * @param requestAsMap The parsed request body as a Map
 * @param messageContents List of expected messages with their roles and content
 */
protected static void assertMultipleRequestMessage(
    Map<String, Object> requestAsMap,
    List<MessageContent> messageContents
);

/**
 * Generic assertion helper for custom message validations.
 * Provides access to the messages array for custom assertion logic.
 *
 * @param requestAsMap The parsed request body as a Map
 * @param messagesAssertions Consumer function to perform custom assertions on messages
 */
protected static void assertMessages(
    Map<String, Object> requestAsMap,
    Consumer<List<? extends Map>> messagesAssertions
);

Function Call Assertion Helpers

Validate function/tool call structures in chat completion requests.

/**
 * Asserts that the request contains a single function/tool with the specified name.
 *
 * @param requestAsMap The parsed request body as a Map
 * @param functionName The expected function/tool name
 */
protected static void assertSingleFunction(
    Map<String, Object> requestAsMap,
    String functionName
);

/**
 * Generic assertion helper for custom function/tool validations.
 * Provides access to the tools/functions array for custom assertion logic.
 *
 * @param requestAsMap The parsed request body as a Map
 * @param functionAssertions Consumer function to perform custom assertions on functions
 */
protected static void assertFunctions(
    Map<String, Object> requestAsMap,
    Consumer<List<? extends Map>> functionAssertions
);

WireMock Infrastructure (Inherited)

OpenAiBaseTest inherits WireMock testing infrastructure from WiremockAware.

/**
 * Returns WireMock URL template for configuration properties.
 * Template: "http://localhost:${quarkus.wiremock.devservices.port}"
 *
 * @return URL template string for use in configuration
 */
public static String wiremockUrlForConfig();

/**
 * Returns WireMock URL with path template for configuration properties.
 *
 * @param path The path to append to the URL template
 * @return URL template string with path for use in configuration
 */
public static String wiremockUrlForConfig(String path);

/**
 * Returns the resolved WireMock URL at runtime.
 *
 * @return The actual WireMock URL with resolved port
 */
public String resolvedWiremockUrl();

/**
 * Returns the resolved WireMock URL with path at runtime.
 *
 * @param path The path to append to the URL
 * @return The actual WireMock URL with path
 */
public String resolvedWiremockUrl(String path);

/**
 * Gets or creates a WireMock client instance.
 *
 * @return WireMock client for interacting with the mock server
 */
protected WireMock wiremock();

/**
 * Gets or creates a WireMock client instance for a specific port.
 *
 * @param port The port number for the WireMock instance
 * @return WireMock client for the specified port
 */
protected WireMock wiremock(Integer port);

/**
 * Gets the resolved WireMock port from configuration.
 *
 * @return The port number WireMock is running on
 */
protected Integer getResolvedWiremockPort();

/**
 * Clears all logged requests in WireMock.
 * Useful for cleaning up between test scenarios.
 */
protected void resetRequests();

/**
 * Clears all stub mappings in WireMock.
 * Useful for removing default stubs and configuring custom behavior.
 */
protected void resetMappings();

/**
 * Gets the single logged request, asserting that exactly one request exists.
 *
 * @return The single logged request
 * @throws AssertionError if the number of requests is not exactly 1
 */
protected LoggedRequest singleLoggedRequest();

/**
 * Gets the single logged request from a specific WireMock instance.
 *
 * @param wireMock The WireMock client instance to query
 * @return The single logged request
 * @throws AssertionError if the number of requests is not exactly 1
 */
protected LoggedRequest singleLoggedRequest(WireMock wireMock);

/**
 * Gets the request body of the single logged request.
 *
 * @return The request body as a byte array
 * @throws AssertionError if the number of requests is not exactly 1
 */
protected byte[] requestBodyOfSingleRequest();

/**
 * Gets request body from a ServeEvent.
 *
 * @param serveEvent The serve event containing request information
 * @return The request body as a byte array
 */
protected byte[] getRequestBody(ServeEvent serveEvent);

Chat Completion Response Transformer

Dynamic response transformation for chat completion tests.

public class ChatCompletionTransformer implements ResponseDefinitionTransformerV2 {

    /**
     * Creates a new ChatCompletionTransformer instance.
     * This constructor is called automatically by WireMock's extension loading mechanism
     * via service loader (META-INF/services).
     */
    public ChatCompletionTransformer();

    /**
     * Transforms response definitions by injecting chat completion message content
     * if the system property is set. This method is called automatically by WireMock
     * for responses configured to use the "completion-transformer".
     *
     * @param serveEvent The serve event containing request and response information
     * @return The transformed response definition
     */
    public ResponseDefinition transform(ServeEvent serveEvent);

    /**
     * Returns the transformer name used to reference this transformer in stub mappings.
     *
     * @return "completion-transformer"
     */
    public String getName();

    /**
     * Sets the chat completion message content via system property.
     * The content will be injected into responses by the transformer.
     * This is typically called from test code or via OpenAiBaseTest.setChatCompletionMessageContent().
     *
     * @param content The message content to inject into responses
     */
    public static void setContent(String content);

    /**
     * Clears the chat completion message content by removing the system property.
     * This is typically called from test cleanup code.
     */
    public static void clearContent();
}

The transformer is automatically registered as a WireMock Extension via service loader mechanism (META-INF/services/com.github.tomakehurst.wiremock.extension.Extension).

WireMock Configuration

Automatic configuration of WireMock DevServices for OpenAI testing.

public class WiremockConfigBuilderCustomizer implements SmallRyeConfigBuilderCustomizer {

    /**
     * Creates a new WiremockConfigBuilderCustomizer instance.
     * This constructor is called automatically during Quarkus configuration phase
     * via service loader (META-INF/services).
     */
    public WiremockConfigBuilderCustomizer();

    /**
     * Configures WireMock DevServices with OpenAI-specific settings.
     * This method is called automatically during Quarkus configuration phase.
     *
     * Sets the following properties:
     * - quarkus.wiremock.devservices.global-response-templating: "true"
     * - quarkus.wiremock.devservices.extension-scanning-enabled: "true"
     * - quarkus.wiremock.devservices.files-mapping: "classpath:/openai"
     *
     * Priority: 500
     *
     * @param builder The SmallRye configuration builder
     */
    public void configBuilder(final SmallRyeConfigBuilder builder);
}

The customizer is automatically registered via service loader mechanism (META-INF/services/io.smallrye.config.SmallRyeConfigBuilderCustomizer).

Types

MessageContent

Record type for specifying expected message content in assertions.

public static record MessageContent(String role, String content) {
    /**
     * Creates a MessageContent record with role and content.
     *
     * @param role The role of the message (e.g., "user", "assistant", "system", "function")
     * @param content The message content (can be null)
     */
}

Usage example:

List<MessageContent> expected = List.of(
    new MessageContent("system", "You are a helpful assistant"),
    new MessageContent("user", "What is the weather?"),
    new MessageContent("assistant", "I'll help you check the weather")
);
assertMultipleRequestMessage(requestAsMap, expected);

External Types

Types from external libraries used in the public API:

// WireMock types
import com.github.tomakehurst.wiremock.extension.ResponseDefinitionTransformerV2;
import com.github.tomakehurst.wiremock.stubbing.ServeEvent;
import com.github.tomakehurst.wiremock.http.ResponseDefinition;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.verification.LoggedRequest;

// SmallRye Config types
import io.smallrye.config.SmallRyeConfigBuilder;
import io.smallrye.config.SmallRyeConfigBuilderCustomizer;

// Jackson types
import wiremock.com.fasterxml.jackson.databind.ObjectMapper;

// Standard Java types
import java.util.Map;
import java.util.List;
import java.util.function.Consumer;
import java.io.IOException;

Pre-configured Mock Endpoints

The package includes WireMock stub mappings in /openai/mappings/completions.json that are automatically loaded:

Chat Completions Endpoint

Endpoint: POST /v1/chat/completions

Request Validation: Validates the presence and structure of:

  • messages array with role and content fields
  • Optional parameters: max_tokens, temperature, top_p, n, stream, frequency_penalty, presence_penalty, logit_bias, user

Response: Returns a chat completion response with the content set via ChatCompletionTransformer. The response uses the template variable {{parameters.ChatCompletionMessageContent}} which is populated from the system property.

Example response structure:

{
  "id": "chatcmpl-123",
  "object": "chat.completion",
  "created": 1677652288,
  "model": "gpt-3.5-turbo",
  "choices": [{
    "index": 0,
    "message": {
      "role": "assistant",
      "content": "{{parameters.ChatCompletionMessageContent}}"
    },
    "finish_reason": "stop"
  }]
}

Image Generation Endpoint

Endpoint: POST /v1/images/generations

Request Validation: Validates the presence and structure of:

  • model (string)
  • prompt (string)
  • n (integer, optional)
  • size (string, optional)
  • quality (string, optional)
  • style (string, optional)
  • response_format (string, optional)

Response: Returns a sample image generation response with a placeholder URL and revised prompt.

Example response structure:

{
  "created": 1677652288,
  "data": [{
    "url": "https://example.com/image.png",
    "revised_prompt": "A revised version of the prompt"
  }]
}

Advanced Usage Examples

Testing Multiple Messages

@Test
public void testMultipleMessages() throws Exception {
    setChatCompletionMessageContent("I can help with that!");

    // Execute test code

    // Validate request had multiple messages
    Map<String, Object> requestAsMap = getRequestAsMap();
    List<MessageContent> expected = List.of(
        new MessageContent("system", "You are a helpful assistant"),
        new MessageContent("user", "Help me with Java")
    );
    assertMultipleRequestMessage(requestAsMap, expected);
}

Testing Function Calls

@Test
public void testFunctionCall() throws Exception {
    setChatCompletionMessageContent("Calling function...");

    // Execute test code that includes function calling

    // Validate request included the expected function
    Map<String, Object> requestAsMap = getRequestAsMap();
    assertSingleFunction(requestAsMap, "get_weather");
}

Custom Message Assertions

@Test
public void testCustomMessageValidation() throws Exception {
    setChatCompletionMessageContent("Response content");

    // Execute test code

    Map<String, Object> requestAsMap = getRequestAsMap();
    assertMessages(requestAsMap, messages -> {
        assertThat(messages).hasSizeGreaterThan(1);
        assertThat(messages.get(0)).containsEntry("role", "system");
    });
}

Custom Function Assertions

@Test
public void testCustomFunctionValidation() throws Exception {
    setChatCompletionMessageContent("Function result");

    // Execute test code

    Map<String, Object> requestAsMap = getRequestAsMap();
    assertFunctions(requestAsMap, functions -> {
        assertThat(functions).hasSize(2);
        assertThat(functions.get(0))
            .containsEntry("type", "function")
            .extracting("function")
            .asInstanceOf(MAP_STRING_OBJECT)
            .containsEntry("name", "get_weather");
    });
}

Using WireMock Infrastructure Directly

@Test
public void testWithDirectWireMockAccess() throws Exception {
    // Reset any existing stubs
    resetMappings();

    // Configure custom stub
    wiremock().register(
        post(urlEqualTo("/v1/chat/completions"))
            .willReturn(aResponse()
                .withStatus(200)
                .withBody("{\"custom\": \"response\"}"))
    );

    // Execute test code

    // Get logged request
    LoggedRequest request = singleLoggedRequest();
    assertThat(request.getUrl()).isEqualTo("/v1/chat/completions");

    // Clean up
    resetRequests();
}

Accessing WireMock URL in Configuration

@TestConfiguration
public class TestConfig {

    @Bean
    public OpenAIClient openAIClient() {
        // Use WireMock URL in configuration
        String baseUrl = OpenAiBaseTest.wiremockUrlForConfig();

        // Configure client with template URL
        // At runtime, this will resolve to actual WireMock port
        return OpenAIClient.builder()
            .baseUrl(baseUrl)
            .build();
    }
}

Dependencies

This package requires the following dependencies:

Core Testing Infrastructure:

  • io.quarkiverse.langchain4j:quarkus-langchain4j-testing-internal (v1.7.4) - Parent testing utilities providing WiremockAware base class

WireMock:

  • org.wiremock:wiremock-standalone - HTTP mocking framework for intercepting OpenAI API calls

Runtime Dependencies (transitive):

  • JUnit 5 - Testing framework (org.junit.jupiter:junit-jupiter-api)
  • AssertJ - Fluent assertions (org.assertj:assertj-core)
  • Jackson - JSON processing (com.fasterxml.jackson.core:jackson-databind)
  • SmallRye Config - Configuration management (io.smallrye.config:smallrye-config)

Notes

  • This is an internal testing dependency marked with <maven.deploy.skip>true</maven.deploy.skip>, intended solely for use within the Quarkus LangChain4j project's test suites
  • The package provides maximum reusability in testing scenarios where developers need to validate OpenAI API integration behavior
  • All WireMock configuration and extension loading happens automatically via service loader mechanisms
  • Pre-configured stub mappings are loaded from classpath:/openai automatically by WiremockConfigBuilderCustomizer
  • The ChatCompletionTransformer uses system properties for configuration, allowing test-specific response content without modifying stub mappings
  • Test cleanup (restoreOriginalChatCompletionMessageContent()) is automatic via JUnit 5 @AfterEach lifecycle

Install with Tessl CLI

npx tessl i tessl/maven-io-quarkiverse-langchain4j--quarkus-langchain4j-openai-testing-internal@1.7.0

docs

index.md

tile.json