This directory contains end-to-end (e2e) tests for Antfly that test the full system integration.
A comprehensive e2e test that:
- Starts Antfly in swarm mode programmatically (metadata + store + termite servers)
- Indexes real Antfly documentation using docsaf library (~2,500 document sections)
- Creates table with hybrid search (BM25 + embeddings) and waits for enrichment
- Executes 8 retrieval agent queries with semantic search and reasoning
- Validates answer quality using LLM-as-judge evaluation (faithfulness, relevance, completeness)
What it tests:
- Swarm mode startup and initialization (metadata + store + termite servers)
- Document indexing pipeline (docsaf → LinearMerge)
- Hybrid search (BM25 full-text + vector embeddings)
- Embedding enrichment with Termite chunking service
- Retrieval agent query execution with reasoning
- Answer quality evaluation using LLM-as-judge (evalaf framework)
- Ollama running locally on
http://localhost:11434 - Models:
gemma3:4b- For LLM generation and evaluationembeddinggemma- For generating embeddings
# Install Ollama (macOS)
brew install ollama
# Start Ollama service
ollama serve
# Pull the required models (in another terminal)
ollama pull gemma3:4b
ollama pull embeddinggemmaMost e2e tests run by default. Only tests requiring external services or large model downloads are gated behind environment variables.
make e2e # All default tests (downloads ONNX deps on first run)
make e2e E2E_TEST=TestName # Run specific test
make e2e E2E_TIMEOUT=45m # Custom timeout (default: 30m)go test -short ./e2e
# All e2e tests will be skipped| Flag | Tests | Requires |
|---|---|---|
RUN_ML_TESTS=true |
Eval, retrieval generation, backup/restore with embeddings, remote content (CLIP/CLAP) | Ollama or large ONNX model downloads |
RUN_PG_TESTS=true |
Foreign table queries, CDC replication | Running PostgreSQL instance |
# Run ML tests (requires Ollama or model downloads)
RUN_ML_TESTS=true make e2e E2E_TIMEOUT=45m
# Run PostgreSQL tests
cd e2e && RUN_PG_TESTS=true ANTFLY_E2E_PG_DSN=postgres://... go test -v ./... -timeout 10mBy default, ML e2e tests use Ollama for embeddings and generation. To use Google Gemini instead:
export E2E_PROVIDER=gemini
export GEMINI_API_KEY=your-api-key
RUN_ML_TESTS=true go test -v ./e2e -run TestE2E_RetrievalAgent_DocsEval -timeout 45mThis uses:
gemini-embedding-001for embeddingsgemini-2.5-flashfor text generation
The TestE2E_RetrievalAgent_DocsEval test supports backing up and restoring the database to speed up test runs. This is especially useful since indexing and embedding generation can take 30-40 minutes.
- First run: The test indexes all documentation and generates embeddings, then automatically backs up the database to
e2e/backups/before running evals - Subsequent runs with
RESTORE_DB=true: The test restores from backup instead of re-indexing, reducing test time from ~40 minutes to ~2-3 minutes
Note: The backup happens immediately after indexing/embedding completion, before running the evaluation queries. This ensures you have a backup even if the evals fail.
First run (creates backup):
RUN_ML_TESTS=true go test -v ./e2e -run TestE2E_RetrievalAgent_DocsEval -timeout 45m
# Takes ~40 minutes, creates backup in e2e/backups/Subsequent runs (restore from backup):
RUN_ML_TESTS=true RESTORE_DB=true go test -v ./e2e -run TestE2E_RetrievalAgent_DocsEval -timeout 10m
# Takes ~2-3 minutes, restores from backupBackups are stored in e2e/backups/ and consist of:
docsaf-test-backup-metadata.json- Table structure and configurationdocsaf-test-backup-{shard_id}.tar.zst- Compressed shard data (Pebble database)
These files are automatically excluded from version control via .gitignore.
If restore fails for any reason (backup corrupted, missing files, etc.), the test automatically falls back to the standard indexing workflow.
You should recreate backups when:
- Documentation content has changed significantly
- Table schema or index configuration has changed
- Embedding model has changed (
embeddinggemma→ different model) - Chunking strategy has changed
To recreate, simply run the test without RESTORE_DB=true - it will overwrite the existing backup.
The test automatically saves detailed evaluation reports in Markdown format to e2e/test_results/ after each test run.
Each report includes:
- Summary: Overall pass rate, average scores, total examples
- Evaluator Statistics: Per-evaluator metrics (faithfulness, relevance, completeness)
- Detailed Results: Per-example evaluation breakdown with scores and reasoning
- Failed Examples: Diagnostic information for queries that didn't meet quality thresholds
Reports are saved with timestamps for tracking over time:
e2e/test_results/
├── docsaf_test_2025-12-01_14-30-45.md
├── docsaf_test_2025-12-01_15-22-18.md
└── docsaf_test_2025-12-02_09-15-33.md
These files are automatically excluded from version control via .gitignore.
Simply open the Markdown files in any text editor or Markdown viewer. The reports are human-readable and include:
- Tables with evaluator statistics
- Detailed pass/fail information
- LLM judge reasoning for each evaluation
- Timing information
- Regression tracking: Compare reports across runs to detect quality degradation
- Documentation: Share evaluation results with stakeholders
- Debugging: Analyze why specific queries failed evaluation
- A/B testing: Compare different model configurations or prompts
docsaf_test.go- Main e2e test implementationtest_helpers.go- Utility functions for test setuptest_queries.json- Test dataset with queries and expected keywordsREADME.md- This file
Swarm Management:
startAntflySwarm()- Start metadata + store servers with dynamic portsSwarmInstance.Cleanup()- Graceful shutdown and cleanup
Document Indexing:
indexAntflyDocs()- Index documentation using docsaf librarysetupTableWithIndexes()- Configure table indexes (currently BM25 only)waitForEmbeddings()- Wait for embedding enrichment on a named index
Query & Validation:
executeRetrievalAgentQueries()- Run queries and collect resultsassertEvalReport()- Validate answers meet quality threshold
Helpers:
GetFreePort()- Allocate dynamic ports for serversCreateTestConfig()- Generate test configurationSkipIfOllamaUnavailable()- Skip test if Ollama not runningGetDefaultOllamaConfig()- Standard LLM config
Backup & Restore:
GetBackupDir()- Get absolute path to e2e/backups directoryShouldRestoreFromBackup()- Check if RESTORE_DB environment variable is setBackupExists()- Check if a backup with given ID existsBackupTestDatabase()- Create backup of table to e2e/backupsRestoreTestDatabase()- Restore table from backup and wait for completion
The test indexes these Antfly documentation files:
CLAUDE.md- Project development guideREADME.md- Project overviewsrc/metadata/api.yaml- API specificationexamples/docsaf/README.md- Docsaf documentation
Test queries in test_queries.json ask questions about:
- Multi-raft architecture
- Linear Merge API
- Embedding providers
- Build process
- Docsaf tool
- Hybrid search
- Swarm mode
- Table creation
The test uses LLM-as-judge evaluation with three metrics:
- Faithfulness: Does the answer accurately reflect the source documents?
- Relevance: Does the answer address the user's query?
- Completeness: Does the answer cover all important aspects?
Each query is evaluated by an LLM judge (gemma3:4b) that scores answers on a 0-1 scale.
Pass criteria:
- Individual queries pass if all three metrics score ≥ 0.7
- Test passes if overall pass rate ≥ 60%
Expected keywords are provided for reference but not used for automated validation.
- Add more test scenarios (updates, deletions, multi-table queries)
- Test with larger document sets
- Add performance benchmarks
- Test distributed mode (multiple nodes)
- Add integration with CI/CD pipeline
- Test semantic chunking strategy (in addition to fixed chunking)
- Test Hugot chunking with ONNX models
- Add embedding indexes (aknn_v0)
- Implement proper embedding enrichment status polling
- Use LLM-as-judge for answer quality validation (evalaf framework)
- Integrate Termite chunking service
- Check if ports are already in use
- Ensure data directories are writable
- Check Antfly logs for errors
# Check if Ollama is running
curl http://localhost:11434/api/tags
# Restart Ollama
pkill ollama
ollama serve- Increase timeout:
-timeout 20m - Check system resources (CPU, memory)
- Reduce test data size
- Run
go mod tidyto fix dependencies - Ensure you're using compatible Go version (1.21+)
- Check if pkg/client SDK is up to date
=== RUN TestE2E_RetrievalAgent_DocsEval
docsaf_test.go:426: Starting Antfly swarm...
docsaf_test.go:156: Swarm started successfully
docsaf_test.go:436: Indexing Antfly documentation...
docsaf_test.go:210: Found 156 document sections
docsaf_test.go:227: Linear merge results: upserted=156, skipped=0, deleted=0
docsaf_test.go:443: Setting up indexes...
docsaf_test.go:240: Using default BM25 index
docsaf_test.go:448: Waiting for embedding enrichment...
docsaf_test.go:250: Skipping embedding wait (not using embeddings)
docsaf_test.go:453: Loading test queries...
docsaf_test.go:458: Executing retrieval agent queries...
docsaf_test.go:297: [1/8] Querying: What is Antfly's multi-raft architecture?
docsaf_test.go:346: Answer length: 324 chars, Found keywords: 4/5
docsaf_test.go:297: [2/8] Querying: How do I use the Linear Merge API?
docsaf_test.go:346: Answer length: 412 chars, Found keywords: 5/6
...
docsaf_test.go:463: Validating answer quality...
docsaf_test.go:386: Pass rate: 75.00% (6/8 queries passed)
docsaf_test.go:468: E2E test completed successfully!
--- PASS: TestE2E_RetrievalAgent_DocsEval (47.23s)
PASS
When adding new e2e tests:
- Follow existing patterns (swarm startup, cleanup, validation)
- Use
t.TempDir()for temporary directories - Use dynamic port allocation (
GetFreePort()) - Add proper cleanup with
defer - Include comprehensive logging with
t.Log() - Document test purpose and expected behavior
- Add skip conditions for unavailable dependencies