Phase 5: Cloud Integration Testing (#139 follow-up)#140
Conversation
…dLevel, KnnLimit) - Add QUERY_ELECTRONICS/GROCERY/SPORTS embedding constants (4D clusters) - testCloudKnnSearch: KNN search with embedding, limit 5, row access assertions - testCloudRrfSearch: RRF search auto-skipped (server unsupported) - testCloudGroupBySearch: GroupBy with category key, maxK=2, rows() access - testCloudBatchSearch: two independent KNN searches, searchCount==2 assertion - testCloudSearchReadLevelIndexAndWal: isolated collection, immediate WAL search - testCloudSearchReadLevelIndexOnly: shared seed, INDEX_ONLY succeeds assertion - testCloudKnnLimitVsSearchLimit: Knn.limit(10) vs Search.limit(3) distinction
…ests
- testCloudSearchFilterMatrix: 8 sub-tests (Where, IDIn, IDNotIn, DocumentContains,
IDNotIn+metadata, Where+DocumentContains, DocumentNotContains, triple Where+IDIn+DocumentContains)
- testCloudSearchPagination: basic limit, limit+offset page2, client-side validation
for limit=0 and negative offset throwing IllegalArgumentException per D-14
- testCloudSearchProjectionPresent: selected fields ID/SCORE/DOCUMENT present,
unselected EMBEDDING is null
- testCloudSearchProjectionCustomKey: Select.key("category") and Select.key("price")
projected via custom metadata key per D-16
…OADMAP updated - 05-02-SUMMARY.md: 11 new test methods covering KNN, batch, GroupBy, ReadLevel, filter matrix (8 combos), pagination with client-side validation, field projection - STATE.md: plan advanced to 2/2, progress 100%, decisions recorded - ROADMAP.md: phase 5 plan progress updated (2 plans, 2 summaries) - REQUIREMENTS.md: CLOUD-01 marked complete
- Loosen assertNull to accept null or [[null]] for embedding projection test - Fix testCloudSearchReadLevelIndexAndWal to search isolated col instead of seedCollection with 3D query embedding
- Add non-empty assertions before all for-each loops to prevent vacuous passes on empty result sets (filter matrix, pagination, projection) - Strengthen WAL assertion from >= 1 to == 3 to properly test contract - Add IAE message validation in pagination catch blocks - Verify KNN/batch top results are from expected clusters - Verify GroupBy returns multiple categories - Replace null guards with assertNotNull in custom key projection - Add non-projected key exclusion checks (in_stock, tags absent) - Make embedding projection assertion handle all server response shapes - Add INDEX_ONLY minimum result count assertion - Convert RRF test from permanent skip to expect-error pattern - Remove unused QUERY_SPORTS constant
- Extract ELECTRONICS_IDS and GROCERY_IDS to class constants (was duplicated in testCloudKnnSearch and testCloudBatchSearch) - Add proper imports for Set/HashSet, remove inline FQNs - Delete dead metadata(String...) helper (zero call sites) - Narrow RRF catch from ChromaException to ChromaClientException - Assert metadata non-null in GroupBy test instead of silently skipping
testCloudForkCountReturnsZeroForNewCollection was calling forkCount() without catching ChromaNotFoundException, causing CI failures when the endpoint is unavailable. Apply the same try/catch/skip pattern used by the self-hosted CollectionApiExtensionsIntegrationTest.
- RRF test: catch ChromaException (not just ChromaClientException) and don't check message content — error format varies by Cloud version - INDEX_ONLY test: allow 0 results since index compaction is async on Cloud; the key assertion is that the call succeeds without error
The Chroma server has no native $rrf operator. The Go client expands
RRF into arithmetic expressions: -(sum(weight_i / (k + rank_i))).
Rewrite buildRrfRankMap to produce the same wire format:
- Each term: $div { left: $val(weight), right: $sum[$val(k), $knn] }
- All terms summed via $sum
- Negated via $mul[$val(-1), sum] (higher-is-better → lower-is-better)
- Normalize flag divides weights by their sum before expansion
Update unit tests for new wire format structure. Enable RRF in both
cloud and self-hosted integration tests (remove permanent skips).
Self-hosted Chroma 1.5.5 may not support arithmetic rank expressions ($sum, $div, $val, $mul) used by the client-side RRF expansion. The server returns an unexpected response causing NPE on deserialization. Skip gracefully with Assume when the server rejects or fails to handle the arithmetic expression tree.
|
@claude PTAL |
|
Claude finished @tazarov's task in 1m 49s —— View job PR Review: Phase 5 - Cloud Integration Testing
OverallSolid PR. The cloud test suite is well-structured with proper credential gating ( A few things worth addressing: Issues1. Catching } catch (NullPointerException e) {
// Server may return an unexpected response format for arithmetic expressions
Assume.assumeTrue("RRF not supported on self-hosted Chroma " + configuredChromaVersion(), false);
}Catching NPE to skip a test masks real bugs. If the server returns an unexpected format, the deserialization layer should throw a meaningful exception, not NPE. This will hide legitimate null-pointer bugs in unrelated code paths during test runs. Consider either:
2. No-op assertion in assertTrue("Schema keys map should not be empty if present",
schema.getKeys().isEmpty() || !schema.getKeys().isEmpty()); // always passes, confirms non-nullThis assertion is a tautology ( 3. The try {
col.modifyConfiguration(UpdateCollectionConfiguration.builder()
.hnswSearchEf(200)
.build());
} catch (IllegalArgumentException e) {
if (!isIndexGroupSwitchError(e)) throw e;
return; // SPANN → HNSW switch not allowed
}
// Always verify after successful modify
Collection fetched = client.getCollection(col.getName());
assertNotNull("Configuration must not be null after HNSW update", fetched.getConfiguration());
assertEquals("HNSW searchEf must round-trip to 200",
Integer.valueOf(200), fetched.getConfiguration().getHnswSearchEf());Same applies to 4. Division by zero in RRF normalization — if (sum > 1e-6) {
for (int i = 0; i < weights.length; i++) weights[i] /= sum;
}If 5. The test asserts that all 3 freshly written records are returned immediately with Nits (non-blocking)
What looks good
| |
…ation - Add inner-list null validation in SearchResultImpl.from() and remove NPE catch in RRF integration test (proper deserialization boundary) - Add zero-weight validation in Rrf.Builder.build() with unit test - Replace tautological schema assertion with assertNotNull - Simplify redundant if/else branches in HNSW/SPANN config round-trip tests - Add assertEventually helper for WAL read-level test (Go-style polling) - Strengthen weak assertion in invalid config transition test - Change buildMeta price param from float to double for type consistency - Remove redundant assertNull inside null-checked branch
The removed NPE catch was masking a missing cloud credential gate — searchCollection is null when CHROMA_API_KEY is absent.
…coverage Production: - Rrf.Builder: reject negative/NaN/infinite weights and non-positive k - ChromaDtos: unify normalization epsilon (1e-6 → 1e-9) to match builder - Rrf/ChromaDtos: fix Javadoc (scores → weights, document normalization) Tests: - assertEventually: use do-while (prevent NPE), retry on ChromaException - Narrow SPANN catch from ChromaException to BadRequest + NotFound - Narrow self-hosted RRF catch to BadRequest + Server + Deserialization - Remove overly broad ChromaServerException catch from fork count test - Add 13 new unit tests: second rank structure, custom k, 3-rank expansion, negative/NaN/infinite weight rejection, k validation, RRF routing
…g catch blocks - assertEventually: catch only ChromaConnectionException + ChromaServerException (non-transient 4xx/deserialization errors now propagate immediately) - SPANN config test: use Assume.assumeTrue instead of silent return - Config transition test: narrow ChromaException to BadRequest + Server - Fix misleading "server-side embeddings" comment - Complete @throws tag on Rrf.Builder.build()
|
@claude PTAL |
|
Claude finished @tazarov's task in 1m 49s —— View job PR Review: Phase 5 - Cloud Integration Testing
OverallGood work. The RRF client-side expansion into arithmetic rank expressions is well-designed, the Rrf builder now has proper input validation (NaN, infinity, negative weights, zero k, all-zero weights), and the I see the previous review's feedback has been largely addressed — the NPE catch was replaced with typed exception catches, the tautological assertion was removed, and A few remaining items: Issues1. Normalization guard in if (sum > 1e-9) {
for (int i = 0; i < weights.length; i++) weights[i] /= sum;
}
2. The test catches 3. assertTrue("INDEX_ONLY result count must be >= 0 and <= 15",
result.getIds().get(0).size() >= 0 && result.getIds().get(0).size() <= 15);
4. } catch (ChromaNotFoundException e) {
Assume.assumeTrue("forkCount endpoint not available on this Chroma Cloud account", false);
}The PR description mentions "gracefully handles 404/500", but only 404 ( Nits (non-blocking)
What looks good
|
… up style - Fail testCloudInvalidConfigTransitionRejected when known index group transition is silently accepted; strengthen catch-block assertions - Remove tautological `>= 0` check on List.size() in INDEX_ONLY test - Add ChromaServerException catch to forkCount test for cloud 5xx - Remove double blank lines after .execute() calls (style consistency) - Add comment about Integer→Double JSON round-trip in buildMeta
|
@claude again |
|
Claude finished @tazarov's task in 1m 36s —— View job PR Review: Phase 5 - Cloud Integration Testing
OverallLooking good. The previous review feedback has been well addressed: the NPE catch was replaced with typed exception catches, the tautological assertion was replaced with A few remaining items: Issues1. The test now correctly 2. Normalization guard in if (sum > 1e-9) {
for (int i = 0; i < weights.length; i++) weights[i] /= sum;
}As flagged in the previous review,
This is low-risk but it's dead code that could mask future bugs if the validation in Nits (non-blocking)
What looks good
LGTM — the two items above are minor. Ship it. |
…ption - Replace silent no-op in buildRrfRankMap with IllegalStateException when weight sum is effectively zero (unreachable via Rrf.build() validation, but will crash loudly if the invariant is ever broken) - Remove remaining double blank lines after .execute() calls
Summary
Phase 5: Cloud Integration Testing
Goal: Build deterministic cloud parity test suites that validate search, schema/index, and array metadata behavior against Chroma Cloud.
Status: Verified ✓ (human verification pending for live cloud tests)
Adds a comprehensive cloud integration test suite (
SearchApiCloudIntegrationTest) with 23 test methods covering search parity (CLOUD-01), schema/index round-trips (CLOUD-02), and array metadata handling (CLOUD-03). All tests gate on cloud credentials for graceful CI skip. Also adds client-side mixed-type array validation inChromaHttpCollectionwith 18 unit tests.Changes
Plan 05-01: Schema/Index + Array Metadata Cloud Tests
SearchApiCloudIntegrationTestwith shared cloud infrastructure and 12 test methodsChromaHttpCollection.validateMetadataArrayTypes()MetadataValidationTestwith 18 unit tests including 3 behavioral wiring testsKey files:
src/test/java/tech/amikos/chromadb/v2/SearchApiCloudIntegrationTest.javasrc/main/java/tech/amikos/chromadb/v2/ChromaHttpCollection.java(validation wiring)src/test/java/tech/amikos/chromadb/v2/MetadataValidationTest.javaPlan 05-02: CLOUD-01 Search Parity Tests
Plan 05-03: Gap Closure
testCloudSearchProjectionPresentassertion to accept[[null]]server responsetestCloudSearchReadLevelIndexAndWalto search isolated collection with 3D embeddingRequirements Addressed
Verification
mvn compilepassesCHROMA_API_KEY,CHROMA_TENANT,CHROMA_DATABASEenv varsKey Decisions
validateMetadataArrayTypesusesChromaBadRequestExceptionwith typed errorCode strings (MIXED_TYPE_ARRAY, NULL_ARRAY_ELEMENT)ChromaHttpCollection.from()with stub client at localhost:1 — validation fires before network callnullor[[null]]— server returns[[null]]for unselected embeddingsTest Plan
mvn test -Dtest=MetadataValidationTest— 18 unit tests pass offlinemvn test -Dtest=SearchApiCloudIntegrationTestwithout credentials — all tests skip, 0 failuresmvn test -Dtest=SearchApiCloudIntegrationTestwith cloud credentials — 22 tests pass, 1 skips (RRF unsupported)