From be3672b435aad709ec8884c90bbeb86b7a47e12b Mon Sep 17 00:00:00 2001 From: Justin Beckwith Date: Thu, 23 Jul 2026 17:41:06 -0700 Subject: [PATCH] tests: serialize shared mock server access --- .../kotlin/com/openai/TestServerExtension.kt | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/openai-java-core/src/test/kotlin/com/openai/TestServerExtension.kt b/openai-java-core/src/test/kotlin/com/openai/TestServerExtension.kt index dbbc3f07c..c19b328da 100644 --- a/openai-java-core/src/test/kotlin/com/openai/TestServerExtension.kt +++ b/openai-java-core/src/test/kotlin/com/openai/TestServerExtension.kt @@ -1,13 +1,23 @@ package com.openai import java.lang.RuntimeException +import java.lang.reflect.Method import java.net.URL +import java.nio.channels.FileChannel +import java.nio.file.Paths +import java.nio.file.StandardOpenOption.CREATE +import java.nio.file.StandardOpenOption.WRITE +import java.util.UUID +import java.util.concurrent.locks.ReentrantLock +import kotlin.concurrent.withLock import org.junit.jupiter.api.extension.BeforeAllCallback import org.junit.jupiter.api.extension.ConditionEvaluationResult import org.junit.jupiter.api.extension.ExecutionCondition import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.api.extension.InvocationInterceptor +import org.junit.jupiter.api.extension.ReflectiveInvocationContext -class TestServerExtension : BeforeAllCallback, ExecutionCondition { +class TestServerExtension : BeforeAllCallback, ExecutionCondition, InvocationInterceptor { override fun beforeAll(context: ExtensionContext?) { try { @@ -40,10 +50,31 @@ class TestServerExtension : BeforeAllCallback, ExecutionCondition { } } + override fun interceptTestMethod( + invocation: InvocationInterceptor.Invocation, + invocationContext: ReflectiveInvocationContext, + extensionContext: ExtensionContext, + ) { + PROCESS_LOCK.withLock { + FileChannel.open(LOCK_FILE, CREATE, WRITE).use { channel -> + channel.lock().use { invocation.proceed() } + } + } + } + companion object { val BASE_URL = System.getenv("TEST_API_BASE_URL") ?: "http://localhost:4010" const val SKIP_TESTS_ENV: String = "SKIP_MOCK_TESTS" + + // JUnit resource locks do not coordinate separate Gradle test-worker JVMs. Use both a + // process-local lock and an OS file lock so tests sharing one mock server cannot race. + private val PROCESS_LOCK = ReentrantLock(true) + private val LOCK_FILE = + Paths.get( + System.getProperty("java.io.tmpdir"), + "openai-java-test-server-${UUID.nameUUIDFromBytes(BASE_URL.toByteArray())}.lock", + ) } }