1+ name : V2 API Nightly Tests
2+
3+ on :
4+ schedule :
5+ # Run at 2 AM UTC every day
6+ - cron : ' 0 2 * * *'
7+ workflow_dispatch :
8+ inputs :
9+ test_experimental :
10+ description : ' Test experimental ChromaDB features'
11+ required : false
12+ type : boolean
13+ default : false
14+
15+ env :
16+ MAVEN_OPTS : -Xmx4096m -Xms1024m
17+
18+ jobs :
19+ comprehensive-v2-tests :
20+ name : Comprehensive V2 API Tests
21+ runs-on : ubuntu-latest
22+ strategy :
23+ fail-fast : false
24+ matrix :
25+ chroma-version :
26+ - ' 0.4.24' # Minimum supported version
27+ - ' 0.5.0'
28+ - ' 0.5.5'
29+ - ' 0.5.10'
30+ - ' 0.5.15'
31+ - ' 0.5.20'
32+ - ' latest'
33+ java-version : [8, 11, 17, 21]
34+ exclude :
35+ # Java 21 only with newer ChromaDB versions
36+ - java-version : 21
37+ chroma-version : ' 0.4.24'
38+ - java-version : 21
39+ chroma-version : ' 0.5.0'
40+
41+ steps :
42+ - name : Checkout code
43+ uses : actions/checkout@v4
44+
45+ - name : Set up Java ${{ matrix.java-version }}
46+ uses : actions/setup-java@v4
47+ with :
48+ java-version : ${{ matrix.java-version }}
49+ distribution : ' temurin'
50+ cache : ' maven'
51+
52+ - name : Start ChromaDB container
53+ run : |
54+ docker run -d \
55+ --name chroma-${{ matrix.chroma-version }} \
56+ -p 8000:8000 \
57+ -e ALLOW_RESET=TRUE \
58+ -e IS_PERSISTENT=FALSE \
59+ chromadb/chroma:${{ matrix.chroma-version }}
60+
61+ # Wait for ChromaDB to be ready
62+ echo "Waiting for ChromaDB to start..."
63+ for i in {1..60}; do
64+ if docker exec chroma-${{ matrix.chroma-version }} curl -f http://localhost:8000/api/v1 > /dev/null 2>&1; then
65+ echo "ChromaDB is ready!"
66+ break
67+ fi
68+ echo "Waiting... ($i/60)"
69+ sleep 2
70+ done
71+
72+ - name : Check ChromaDB health
73+ run : |
74+ curl -v http://localhost:8000/api/v1
75+ docker logs chroma-${{ matrix.chroma-version }} | tail -20
76+
77+ - name : Run V2 API tests
78+ run : |
79+ mvn clean test \
80+ -Dtest="tech.amikos.chromadb.v2.**" \
81+ -DfailIfNoTests=false \
82+ -Dchroma.url=http://localhost:8000
83+ env :
84+ CHROMA_VERSION : ${{ matrix.chroma-version }}
85+ CHROMA_URL : http://localhost:8000
86+
87+ - name : Generate detailed test report
88+ if : always()
89+ run : |
90+ mvn surefire-report:report-only
91+ mvn site -DgenerateReports=false
92+
93+ - name : Collect container logs
94+ if : failure()
95+ run : |
96+ docker logs chroma-${{ matrix.chroma-version }} > chroma-logs-${{ matrix.chroma-version }}-java-${{ matrix.java-version }}.txt
97+
98+ - name : Upload test artifacts
99+ if : always()
100+ uses : actions/upload-artifact@v3
101+ with :
102+ name : nightly-v2-chroma-${{ matrix.chroma-version }}-java-${{ matrix.java-version }}
103+ path : |
104+ target/surefire-reports/
105+ target/site/
106+ chroma-logs-*.txt
107+
108+ - name : Stop ChromaDB container
109+ if : always()
110+ run : docker stop chroma-${{ matrix.chroma-version }} && docker rm chroma-${{ matrix.chroma-version }}
111+
112+ stress-tests :
113+ name : V2 API Stress Tests
114+ runs-on : ubuntu-latest
115+
116+ services :
117+ chroma :
118+ image : chromadb/chroma:latest
119+ ports :
120+ - 8000:8000
121+ env :
122+ ALLOW_RESET : ' TRUE'
123+ IS_PERSISTENT : ' TRUE'
124+ PERSIST_DIRECTORY : ' /chroma/data'
125+ options : >-
126+ --health-cmd "curl -f http://localhost:8000/api/v1 || exit 1"
127+ --health-interval 10s
128+ --health-timeout 5s
129+ --health-retries 5
130+ --mount type=tmpfs,destination=/chroma/data
131+
132+ steps :
133+ - name : Checkout code
134+ uses : actions/checkout@v4
135+
136+ - name : Set up Java 17
137+ uses : actions/setup-java@v4
138+ with :
139+ java-version : ' 17'
140+ distribution : ' temurin'
141+ cache : ' maven'
142+
143+ - name : Create stress test
144+ run : |
145+ cat > src/test/java/tech/amikos/chromadb/v2/V2StressTest.java << 'EOF'
146+ package tech.amikos.chromadb.v2;
147+
148+ import org.junit.BeforeClass;
149+ import org.junit.Test;
150+ import tech.amikos.chromadb.v2.auth.AuthProvider;
151+ import tech.amikos.chromadb.v2.client.Collection;
152+ import tech.amikos.chromadb.v2.client.ServerClient;
153+ import tech.amikos.chromadb.v2.model.*;
154+
155+ import java.util.*;
156+ import java.util.concurrent.*;
157+ import java.util.stream.Collectors;
158+ import java.util.stream.IntStream;
159+
160+ import static org.junit.Assert.*;
161+
162+ public class V2StressTest {
163+ private static ServerClient client;
164+
165+ @BeforeClass
166+ public static void setup() {
167+ client = ServerClient.builder()
168+ .baseUrl("http://localhost:8000")
169+ .auth(AuthProvider.none())
170+ .connectTimeout(60)
171+ .readTimeout(60)
172+ .writeTimeout(60)
173+ .build();
174+ }
175+
176+ @Test
177+ public void testLargeScale() throws Exception {
178+ String collectionName = "stress_test_" + UUID.randomUUID().toString().substring(0, 8);
179+ Collection collection = client.createCollection(collectionName);
180+
181+ // Add 10,000 records in batches of 100
182+ for (int batch = 0; batch < 100; batch++) {
183+ List<String> ids = new ArrayList<>();
184+ List<List<Float>> embeddings = new ArrayList<>();
185+ List<Map<String, Object>> metadatas = new ArrayList<>();
186+
187+ for (int i = 0; i < 100; i++) {
188+ int recordId = batch * 100 + i;
189+ ids.add("id_" + recordId);
190+
191+ // Create random embedding
192+ List<Float> embedding = new ArrayList<>();
193+ Random rand = new Random(recordId);
194+ for (int j = 0; j < 384; j++) {
195+ embedding.add(rand.nextFloat());
196+ }
197+ embeddings.add(embedding);
198+
199+ metadatas.add(Map.of(
200+ "batch", batch,
201+ "index", i,
202+ "category", "category_" + (recordId % 10)
203+ ));
204+ }
205+
206+ collection.add(builder -> builder
207+ .ids(ids)
208+ .embeddings(embeddings)
209+ .metadatas(metadatas)
210+ );
211+
212+ if (batch % 10 == 0) {
213+ System.out.println("Added " + ((batch + 1) * 100) + " records");
214+ }
215+ }
216+
217+ assertEquals(10000, collection.count());
218+ System.out.println("Successfully added 10,000 records");
219+
220+ // Test queries
221+ Random rand = new Random();
222+ List<Float> queryEmbedding = IntStream.range(0, 384)
223+ .mapToObj(i -> rand.nextFloat())
224+ .collect(Collectors.toList());
225+
226+ QueryResponse result = collection.query(builder -> builder
227+ .queryEmbeddings(Arrays.asList(queryEmbedding))
228+ .nResults(100)
229+ .include(Include.METADATAS, Include.DISTANCES)
230+ );
231+
232+ assertEquals(1, result.getIds().size());
233+ assertEquals(100, result.getIds().get(0).size());
234+
235+ client.deleteCollection(collectionName);
236+ }
237+
238+ @Test
239+ public void testConcurrentOperations() throws Exception {
240+ String collectionName = "concurrent_test_" + UUID.randomUUID().toString().substring(0, 8);
241+ Collection collection = client.createCollection(collectionName);
242+
243+ ExecutorService executor = Executors.newFixedThreadPool(10);
244+ List<Future<Boolean>> futures = new ArrayList<>();
245+
246+ // Submit 100 concurrent operations
247+ for (int i = 0; i < 100; i++) {
248+ final int taskId = i;
249+ futures.add(executor.submit(() -> {
250+ try {
251+ String id = "concurrent_" + taskId;
252+ List<Float> embedding = IntStream.range(0, 384)
253+ .mapToObj(j -> (float) (taskId * 0.01))
254+ .collect(Collectors.toList());
255+
256+ collection.add(builder -> builder
257+ .ids(Arrays.asList(id))
258+ .embeddings(Arrays.asList(embedding))
259+ );
260+ return true;
261+ } catch (Exception e) {
262+ e.printStackTrace();
263+ return false;
264+ }
265+ }));
266+ }
267+
268+ // Wait for all operations to complete
269+ for (Future<Boolean> future : futures) {
270+ assertTrue(future.get(30, TimeUnit.SECONDS));
271+ }
272+
273+ executor.shutdown();
274+ executor.awaitTermination(1, TimeUnit.MINUTES);
275+
276+ assertEquals(100, collection.count());
277+ client.deleteCollection(collectionName);
278+ }
279+ }
280+ EOF
281+
282+ - name : Run stress tests
283+ run : |
284+ mvn test -Dtest=V2StressTest -DfailIfNoTests=false
285+ env :
286+ CHROMA_URL : http://localhost:8000
287+
288+ - name : Upload stress test results
289+ if : always()
290+ uses : actions/upload-artifact@v3
291+ with :
292+ name : stress-test-results-v2
293+ path : |
294+ target/surefire-reports/TEST-*V2StressTest.xml
295+ target/site/
296+
297+ report-summary :
298+ name : Generate Nightly Report
299+ needs : [comprehensive-v2-tests, stress-tests]
300+ runs-on : ubuntu-latest
301+ if : always()
302+
303+ steps :
304+ - name : Download all artifacts
305+ uses : actions/download-artifact@v3
306+ with :
307+ path : test-artifacts
308+
309+ - name : Generate summary report
310+ run : |
311+ echo "# V2 API Nightly Test Report" > nightly-report.md
312+ echo "Date: $(date -u +"%Y-%m-%d %H:%M:%S UTC")" >> nightly-report.md
313+ echo "" >> nightly-report.md
314+
315+ echo "## Test Coverage Matrix" >> nightly-report.md
316+ echo "| ChromaDB | Java 8 | Java 11 | Java 17 | Java 21 |" >> nightly-report.md
317+ echo "|----------|--------|---------|---------|---------|" >> nightly-report.md
318+
319+ # Process test results
320+ for dir in test-artifacts/nightly-v2-*; do
321+ if [ -d "$dir" ]; then
322+ basename "$dir" | grep -o "chroma-[^-]*" | cut -d- -f2 >> versions.txt
323+ fi
324+ done
325+
326+ sort -u versions.txt > unique-versions.txt || true
327+
328+ while IFS= read -r version; do
329+ if [ -n "$version" ]; then
330+ row="| $version |"
331+ for java in 8 11 17 21; do
332+ if [ -d "test-artifacts/nightly-v2-chroma-${version}-java-${java}" ]; then
333+ if ls test-artifacts/nightly-v2-chroma-${version}-java-${java}/TEST-*.xml 2>/dev/null | head -1 | xargs grep -q 'failures="0".*errors="0"' 2>/dev/null; then
334+ row="$row ✅ |"
335+ else
336+ row="$row ❌ |"
337+ fi
338+ else
339+ row="$row - |"
340+ fi
341+ done
342+ echo "$row" >> nightly-report.md
343+ fi
344+ done < unique-versions.txt || true
345+
346+ echo "" >> nightly-report.md
347+ echo "## Stress Test Results" >> nightly-report.md
348+ if [ -f "test-artifacts/stress-test-results-v2/TEST-*V2StressTest.xml" ]; then
349+ echo "✅ Stress tests completed successfully" >> nightly-report.md
350+ else
351+ echo "⚠️ Stress test results not found" >> nightly-report.md
352+ fi
353+
354+ cat nightly-report.md >> $GITHUB_STEP_SUMMARY
355+
356+ - name : Create issue for failures
357+ if : failure()
358+ uses : actions/github-script@v7
359+ with :
360+ script : |
361+ const date = new Date().toISOString().split('T')[0];
362+ await github.rest.issues.create({
363+ owner: context.repo.owner,
364+ repo: context.repo.repo,
365+ title: `[V2 API] Nightly test failures - ${date}`,
366+ body: `Nightly V2 API tests have failed. Please check the [workflow run](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}) for details.`,
367+ labels: ['bug', 'v2-api', 'nightly-test']
368+ });
0 commit comments