Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,12 +370,12 @@ are exercised. The Jib JVM path runs in `build-and-publish.yml`.

### Solr Version Compatibility Testing

The Solr Docker image used in tests is configurable via the `solr.test.image` system property (default: `solr:9.9-slim`):
The Solr Docker image used in tests is pinned as `test-image-solr` in `gradle/libs.versions.toml` (the Grafana LGTM image for the OTLP test as `test-image-lgtm`), matching the literal default in `TestcontainersConfiguration`/`OtlpExportIntegrationTest`. Override the Solr image for one run with the `solr.test.image` system property:

```bash
./gradlew test -Dsolr.test.image=solr:8.11-slim # Solr 8.11
./gradlew test -Dsolr.test.image=solr:9.4-slim # Solr 9.4
./gradlew test -Dsolr.test.image=solr:9.9-slim # Solr 9.9 (default)
./gradlew test -Dsolr.test.image=solr:9.9.0-slim # Solr 9.9 (the pinned default)
./gradlew test -Dsolr.test.image=solr:9.10-slim # Solr 9.10
./gradlew test -Dsolr.test.image=solr:10-slim # Solr 10
```
Expand All @@ -393,7 +393,7 @@ Remaining known differences from Solr 9:
- **`/admin/mbeans` removed:** Cache and handler stats from `getCollectionStats()` will always be `null` on Solr 10. A future migration to `/admin/metrics` will restore these metrics.
- **Metrics migration:** Dropwizard metrics replaced by OpenTelemetry. Metric names switch to snake_case in Solr 10.
- **SolrJ base URL:** Already uses root URLs — **no change needed**.
- **SolrJ version:** `solr-solrj` is on 10.0.0 (`gradle/libs.versions.toml`), released to Maven Central and bumped in #58. Jetty artifacts are declared versionless and managed by Spring Boot's BOM, so there is no separate Jetty pin to update. Note the client is *newer* than the default test server: `solr.test.image` defaults to `solr:9.9-slim`, so the standard build exercises a SolrJ 10 client against Solr 9.9.
- **SolrJ version:** `solr-solrj` is on 10.0.0 (`gradle/libs.versions.toml`), released to Maven Central and bumped in #58. Jetty artifacts are declared versionless and managed by Spring Boot's BOM, so there is no separate Jetty pin to update. Note the client is *newer* than the default test server: `solr.test.image` defaults to `solr:9.9.0-slim`, so the standard build exercises a SolrJ 10 client against Solr 9.9.

## Key Configuration

Expand Down
19 changes: 15 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,18 @@ springBoot {
buildInfo()
}

// Testcontainers image pins live in gradle/libs.versions.toml; -Dsolr.test.image
// and -Dlgtm.test.image override them for a single run (e.g. the Solr
// compatibility matrix in CI).
val solrPin =
libs.versions.test.image.solr
.get()
val lgtmPin =
libs.versions.test.image.lgtm
.get()
val solrTestImage = System.getProperty("solr.test.image", solrPin)
val lgtmTestImage = System.getProperty("lgtm.test.image", lgtmPin)

tasks.withType<Test> {
useJUnitPlatform {
// Only exclude docker integration tests from regular test runs, not from dockerIntegrationTest
Expand All @@ -218,8 +230,9 @@ tasks.withType<Test> {
if (name != "dockerIntegrationTest") {
dependsOn(tasks.bootJar)
}
// Forward solr.test.image system property to test JVMs for Solr version compatibility testing
systemProperty("solr.test.image", System.getProperty("solr.test.image", "solr:9.9-slim"))
// Forward the Testcontainers image pins (or a per-run -D override) to test JVMs.
systemProperty("solr.test.image", solrTestImage)
systemProperty("lgtm.test.image", lgtmTestImage)
if (name != "dockerIntegrationTest") {
finalizedBy(tasks.jacocoTestReport)
}
Expand Down Expand Up @@ -255,8 +268,6 @@ tasks.register<Test>("integrationTest") {
testClassesDirs = sourceSets["test"].output.classesDirs
classpath = sourceSets["test"].runtimeClasspath

systemProperty("solr.test.image", System.getProperty("solr.test.image", "solr:9.9-slim"))

mustRunAfter(tasks.named("unitTest"))
finalizedBy(tasks.jacocoTestReport)

Expand Down
6 changes: 3 additions & 3 deletions dev-docs/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,12 @@ This runs tests tagged with `@Tag("docker-integration")` which verify:

### Solr Version Compatibility

Tests run against `solr:9.9-slim` by default. Point them at another Solr version with the `solr.test.image` system property:
Tests run against the Solr image pinned as `test-image-solr` in `gradle/libs.versions.toml` by default (the LGTM image for the OTLP test is pinned there too, as `test-image-lgtm`). Point them at another Solr version with the `solr.test.image` system property:

```bash
./gradlew test -Dsolr.test.image=solr:8.11-slim # Solr 8.11
./gradlew test -Dsolr.test.image=solr:9.4-slim # Solr 9.4
./gradlew test -Dsolr.test.image=solr:9.9-slim # Solr 9.9 (default)
./gradlew test -Dsolr.test.image=solr:9.9.0-slim # Solr 9.9 (the pinned default)
./gradlew test -Dsolr.test.image=solr:9.10-slim # Solr 9.10
./gradlew test -Dsolr.test.image=solr:10-slim # Solr 10
```
Expand All @@ -205,7 +205,7 @@ Tests run against `solr:9.9-slim` by default. Point them at another Solr version
endpoint was removed in Solr 10, so `getCacheMetrics()`/`getHandlerMetrics()` catch
`RuntimeException` and return `null` — `cacheStats`/`handlerStats` from `get-collection-stats`
are therefore always `null` on Solr 10 (a future migration to `/admin/metrics` will restore
them). SolrJ is on 10.0.0; since `solr.test.image` defaults to `solr:9.9-slim`, the standard
them). SolrJ is on 10.0.0; since the pinned test image is Solr 9.9, the standard
build runs a SolrJ 10 client against a Solr 9.9 server.

### Test with MCP Inspector
Expand Down
7 changes: 7 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ nullaway = "0.13.1"

# Test dependencies
testcontainers = "1.21.3"
# Docker images started by Testcontainers. Exact tags only (never "latest" or a
# moving minor tag). Forwarded to tests as the solr.test.image / lgtm.test.image
# system properties, which also override them for a single run; the literal
# default at each Testcontainers call site (TestcontainersConfiguration,
# OtlpExportIntegrationTest) must be bumped along with these pins.
test-image-solr = "solr:9.9.0-slim"
test-image-lgtm = "grafana/otel-lgtm:0.33.0"
awaitility = "4.3.0"
opentelemetry-instrumentation-bom = "2.26.1"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ public class TestcontainersConfiguration {

@Bean
SolrContainer solr() {
String solrImage = System.getProperty("solr.test.image", "solr:9.9-slim");
// Pinned in gradle/libs.versions.toml as test-image-solr; -Dsolr.test.image
// overrides it for a single run (e.g. the Solr compatibility matrix in CI).
String solrImage = System.getProperty("solr.test.image", "solr:9.9.0-slim");
return new SolrContainer(DockerImageName.parse(solrImage));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,17 @@
import org.apache.solr.mcp.server.TestcontainersConfiguration;
import org.apache.solr.mcp.server.indexing.IndexingService;
import org.apache.solr.mcp.server.search.SearchService;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Import;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.context.ActiveProfiles;
import org.testcontainers.grafana.LgtmStackContainer;
import org.testcontainers.junit.jupiter.Container;
Expand Down Expand Up @@ -64,16 +67,10 @@
* container integration which auto-configures OTLP export endpoints.
*
* <p>
* <b>NOTE:</b> This test is currently disabled due to a Jetty HTTP client
* ClassNotFoundException when using LgtmStackContainer. The
* testcontainers-grafana module requires
* {@code org.eclipse.jetty.client.transport.HttpClientTransportOverHTTP} which
* is not properly resolved with the current Jetty BOM configuration. This is a
* known issue and can be addressed separately. The core distributed tracing
* functionality is tested by {@link DistributedTracingTest} which uses
* SimpleTracer and passes all tests successfully.
* Runs against a real LGTM container and authenticates its own thread, because
* the http profile keeps method security on. {@link DistributedTracingTest}
* covers the same tracing paths in-memory without a container.
*/
@Disabled("Jetty HTTP client ClassNotFoundException with LgtmStackContainer - see class javadoc")
@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {
Expand All @@ -94,9 +91,12 @@ class OtlpExportIntegrationTest {
* The {@code @ServiceConnection} annotation enables Spring Boot to recognize
* this container for service connection auto-configuration.
*/
// Pinned in gradle/libs.versions.toml as test-image-lgtm; -Dlgtm.test.image
// overrides it for a single run.
@Container
@ServiceConnection
static LgtmStackContainer lgtmStack = new LgtmStackContainer("grafana/otel-lgtm:latest");
static LgtmStackContainer lgtmStack = new LgtmStackContainer(
System.getProperty("lgtm.test.image", "grafana/otel-lgtm:0.33.0"));

@Autowired
private SearchService searchService;
Expand All @@ -118,6 +118,23 @@ static void setUpCollection(@Autowired SolrClient solrClient) throws Exception {
createRequest.process(solrClient);
}

/**
* The http profile keeps secure-by-default on, so the {@code @PreAuthorize}
* service methods this test calls directly need an authenticated principal.
* Over MCP the OAuth2 filter chain supplies one; here the test thread does,
* which keeps method security active rather than switching it off.
*/
@BeforeEach
void authenticateTestThread() {
var authentication = new TestingAuthenticationToken("otlp-test", null, "ROLE_USER");
SecurityContextHolder.getContext().setAuthentication(authentication);
}

@AfterEach
void clearAuthentication() {
SecurityContextHolder.clearContext();
}

@Test
void shouldExportTracesWithoutErrors() throws Exception {
// Given: Some test data
Expand Down
Loading