This commit adds the read path (Cortex) to complement the write path (Spine): ## Crates - stemedb-api: HTTP API with axum + utoipa OpenAPI - /v1/assert, /v1/query, /v1/epoch, /v1/skeptic, /v1/trace, /v1/audit - Metered endpoints with quota enforcement - Ed25519 signature verification - stemedb-lens: Truth resolution lenses - RecencyLens, ConsensusLens, ConfidenceLens - VoteAwareConsensusLens (Ballot Box pattern) - TrustAwareAuthorityLens (The Hive pattern) - SkepticLens (conflict analysis) - EpochAwareLens (paradigm-safe queries) - stemedb-query: Query engine with materialized views ## Storage Extensions - VoteStore: Vote aggregation with cached counts - TrustRankStore: Agent reputation with decay - AuditStore: Query audit trail - IndexStore: SP/P/S index structures - SupersessionStore: Epoch supersession chains ## SDKs - sdk/go/steme: Go HTTP client with Ed25519 signing - sdk/go/adk: ADK-Go tools for AI agents ## Documentation - Updated CLAUDE.md, architecture.md, roadmap.md - New ai-lookup entries for all services - Use case docs for consumer health intelligence - Arena roadmap for simulation advancement Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
216 lines
4.6 KiB
Markdown
216 lines
4.6 KiB
Markdown
# StemeDB Go SDK Integration Tests
|
|
|
|
Integration tests that run the Go SDK against a live StemeDB server.
|
|
|
|
## Quick Start
|
|
|
|
1. **Start the StemeDB API server:**
|
|
|
|
```bash
|
|
cd /path/to/stemedb
|
|
cargo run --bin stemedb-api
|
|
```
|
|
|
|
The server will start on `http://localhost:3000` by default.
|
|
|
|
2. **Run the integration tests:**
|
|
|
|
```bash
|
|
cd sdk/go/steme
|
|
STEMEDB_URL=http://localhost:3000 go test -tags=integration -v
|
|
```
|
|
|
|
## Test Coverage
|
|
|
|
The integration test suite verifies end-to-end behavior:
|
|
|
|
### Core Functionality
|
|
|
|
- **TestIntegration_Health** - Verify health endpoint works
|
|
- **TestIntegration_Assert_Query_Roundtrip** - Create assertion, query it back
|
|
- **TestIntegration_Query_NotFound** - Query for non-existent data returns empty
|
|
|
|
### Lens Resolution
|
|
|
|
- **TestIntegration_Query_WithLens** - Query with different lenses:
|
|
- `Recency` - Latest timestamp wins
|
|
- `Consensus` - Most common value
|
|
- `Authority` - Weighted by agent reputation
|
|
|
|
### Object Types
|
|
|
|
- **TestIntegration_Assert_AllObjectTypes** - Test all supported value types:
|
|
- Text
|
|
- Number
|
|
- Boolean
|
|
- Reference
|
|
|
|
### Filtering & Pagination
|
|
|
|
- **TestIntegration_Query_Limit** - Verify limit parameter works
|
|
- **TestIntegration_Lifecycle** - Filter by lifecycle stage (Proposed, Approved, Deprecated)
|
|
|
|
### Error Handling
|
|
|
|
- **TestIntegration_Assert_InvalidAssertion** - Verify invalid assertions are rejected:
|
|
- Missing `source_hash`
|
|
- Invalid confidence (out of range)
|
|
- Negative confidence
|
|
|
|
## Running Individual Tests
|
|
|
|
```bash
|
|
# Run a specific test
|
|
STEMEDB_URL=http://localhost:3000 go test -tags=integration -v -run TestIntegration_Health
|
|
|
|
# Run tests matching a pattern
|
|
STEMEDB_URL=http://localhost:3000 go test -tags=integration -v -run Query
|
|
```
|
|
|
|
## Test Isolation
|
|
|
|
Each test uses `uniqueSubject()` to generate unique subject IDs based on timestamps:
|
|
|
|
```go
|
|
subject := uniqueSubject("IntegrationTest_Entity")
|
|
// => "IntegrationTest_Entity_1738425600000000000"
|
|
```
|
|
|
|
This ensures tests don't interfere with each other, even when running in parallel.
|
|
|
|
## CI/CD Integration
|
|
|
|
Integration tests are **skipped by default** (build tag `integration`) so they don't break CI when no server is running.
|
|
|
|
To run integration tests in CI:
|
|
|
|
```yaml
|
|
# GitHub Actions example
|
|
- name: Start StemeDB server
|
|
run: cargo run --bin stemedb-api &
|
|
working-directory: /path/to/stemedb
|
|
|
|
- name: Wait for server
|
|
run: |
|
|
for i in {1..30}; do
|
|
curl -f http://localhost:3000/v1/health && break
|
|
sleep 1
|
|
done
|
|
|
|
- name: Run integration tests
|
|
run: STEMEDB_URL=http://localhost:3000 go test -tags=integration -v ./...
|
|
working-directory: sdk/go/steme
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Tests skip with "STEMEDB_URL not set"
|
|
|
|
Set the environment variable:
|
|
|
|
```bash
|
|
export STEMEDB_URL=http://localhost:3000
|
|
go test -tags=integration -v
|
|
```
|
|
|
|
### Tests fail with connection errors
|
|
|
|
Verify the server is running:
|
|
|
|
```bash
|
|
curl http://localhost:3000/v1/health
|
|
```
|
|
|
|
Should return:
|
|
|
|
```json
|
|
{
|
|
"status": "healthy",
|
|
"version": "0.1.0",
|
|
"assertions_count": 0
|
|
}
|
|
```
|
|
|
|
### Tests fail after server restart
|
|
|
|
The server uses persistent storage. If tests fail due to stale data, clear the data directory:
|
|
|
|
```bash
|
|
# Check server logs for data directory location
|
|
rm -rf /path/to/data/stemedb_*
|
|
```
|
|
|
|
## Adding New Tests
|
|
|
|
1. **Use the build tag:**
|
|
|
|
```go
|
|
//go:build integration
|
|
|
|
package steme
|
|
```
|
|
|
|
2. **Get a test client:**
|
|
|
|
```go
|
|
client := getTestClient(t) // Auto-skips if STEMEDB_URL not set
|
|
```
|
|
|
|
3. **Use unique subjects:**
|
|
|
|
```go
|
|
subject := uniqueSubject("YourTest_Name")
|
|
```
|
|
|
|
4. **Add indexing delay after writes:**
|
|
|
|
```go
|
|
_, err := client.Assert(ctx, assertion)
|
|
time.Sleep(100 * time.Millisecond) // Allow indexing to complete
|
|
```
|
|
|
|
5. **Check for errors:**
|
|
|
|
```go
|
|
if err != nil {
|
|
t.Fatalf("Assert() failed: %v", err)
|
|
}
|
|
```
|
|
|
|
## Example Test
|
|
|
|
```go
|
|
func TestIntegration_MyFeature(t *testing.T) {
|
|
client := getTestClient(t)
|
|
ctx := context.Background()
|
|
|
|
subject := uniqueSubject("MyFeature_Test")
|
|
|
|
// Create assertion
|
|
assertion := NewAssertion(subject, "has_value").
|
|
WithText("test").
|
|
WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
|
|
Build()
|
|
|
|
hash, err := client.Assert(ctx, assertion)
|
|
if err != nil {
|
|
t.Fatalf("Assert() failed: %v", err)
|
|
}
|
|
|
|
t.Logf("Created assertion: %s", hash)
|
|
|
|
// Small delay for indexing
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
// Query it back
|
|
result, err := client.Query(ctx, NewQuery().WithSubject(subject).Build())
|
|
if err != nil {
|
|
t.Fatalf("Query() failed: %v", err)
|
|
}
|
|
|
|
if result.TotalCount == 0 {
|
|
t.Fatal("Expected results, got none")
|
|
}
|
|
}
|
|
```
|