Files
openobserve/tests/db-testing/README.md
T

190 lines
5.8 KiB
Markdown

# Database Validation Tests
This directory contains database validation tests for OpenObserve. These tests ingest data via the API and then validate the database state by directly querying the metadata database.
## Purpose
These tests are separate from:
- **Rust integration tests** (`tests/integration_test.rs`) - To avoid increasing PR CI time
- **API tests** (`tests/api-testing/`) - Different concern: DB state validation vs API endpoint testing
## Structure
```
tests/db-testing/
├── README.md # This file
├── pyproject.toml # Python project configuration (auto-generated in CI)
├── requirements.lock # Locked dependencies (managed by rye)
└── tests/
├── conftest.py # Pytest fixtures and configuration
└── test_*.py # Test files
```
## Dependencies
- `pytest` - Test framework
- `requests` - HTTP client for API calls
- `psycopg2-binary` - PostgreSQL database driver
- `python-dotenv` - Environment variable management
## Running Tests Locally
### Prerequisites
1. Install [rye](https://rye-up.com/):
```bash
curl -sSf https://rye.astral.sh/get | bash
```
2. Build OpenObserve:
```bash
cargo build --features mimalloc
```
3. Start a PostgreSQL instance:
```bash
docker run -d \
--name postgres-test \
-e POSTGRES_PASSWORD=password \
-p 5432:5432 \
postgres:17.5-alpine3.22
```
### Run Tests
1. Start OpenObserve with Postgres backend:
```bash
export ZO_META_STORE=postgres
export ZO_META_POSTGRES_DSN=postgres://postgres:password@localhost:5432/postgres
export ZO_ROOT_USER_EMAIL=root@example.com
export ZO_ROOT_USER_PASSWORD=Complexpass#123
target/debug/openobserve
```
2. In another terminal, run the tests:
```bash
cd tests/db-testing
rye sync # Install dependencies
rye run pytest -v
```
## Writing Tests
### Test Structure
Each test should follow this pattern:
1. **Ingest data** via the OpenObserve API
2. **Query the database** directly to validate state
3. **Assert** that the database state matches expectations
### Example Test
```python
def test_my_validation(ingest_test_data, db_cursor, test_org, test_stream):
"""Test description."""
# 1. Ingest test data
test_data = [{"timestamp": "...", "field": "value"}]
ingest_test_data(test_data)
# 2. Query database
db_cursor.execute("""
SELECT * FROM meta
WHERE org_id = %s
AND key2 = %s
""", (test_org, test_stream))
results = db_cursor.fetchall()
# 3. Validate
assert len(results) > 0, "Expected data not found"
```
### Available Fixtures
- `db_connection` - PostgreSQL connection (session-scoped)
- `db_cursor` - Database cursor for queries
- `ingest_test_data` - Function to ingest data via API
- `query_api` - Function to query OpenObserve search API
- `openobserve_base_url` - OpenObserve API base URL
- `auth_credentials` - Authentication credentials tuple
- `api` - Call `/api/{org}/...` (or `/api/v2/...` with `v2=True`) as root
- `destination` - An http template + webhook destination for alert tests
- `test_org` - Test organization name (default: "default")
- `test_stream` - Test stream name (default: "db_test_stream")
### Postgres-only drift checks
Cloud runs on Postgres; SQLite (local CI) accepts any value in any column, so type drift only shows up here.
- **Entity vs schema** (`test_entity_column_types.py`): parses every SeaORM entity under `src/infra/src/table` and checks each column's Postgres type and nullability against the migrated schema. No server data needed. `ENTITY_DIRS` adds entity roots and `BUILD_FEATURES` resolves `#[cfg(feature)]` fields (o2-enterprise reuses it for cloud builds).
- **API round-trips** (`test_alert_roundtrip.py`, `test_object_roundtrips.py`): create an object with every optional field filled, read it back, update it. Empty optional fields are stored as NULL, which skips sqlx's type check, so only filled values exercise every column.
## CI Integration
Tests run automatically in the `db-testing.yml` GitHub Actions workflow on:
- Push to `main` branch
- Pull requests to any branch
The workflow:
1. Starts a PostgreSQL service
2. Builds OpenObserve
3. Configures OpenObserve to use Postgres
4. Runs pytest tests
5. Uploads logs on failure
## Database Schema Reference
The `meta` table structure (adjust based on your actual schema):
```sql
CREATE TABLE meta (
id SERIAL PRIMARY KEY,
org_id VARCHAR(100),
module VARCHAR(100),
key1 VARCHAR(256),
key2 VARCHAR(256),
start_dt BIGINT,
value TEXT,
-- ... other columns
);
```
Common modules:
- `schema` - Stream schemas
- `stream_settings` - Stream configuration
- `file_list` - File metadata
- `organization` - Organization settings
- `user` - User data
## Tips
1. **Use transactions** in tests when you need to clean up (though fixtures handle most cleanup)
2. **Wait for ingestion** - Data ingestion is async, use `wait_for_ingestion()` helper
3. **Print debug info** - Use `print()` statements to debug queries during development
4. **Test isolation** - Each test should be independent and not rely on other tests
5. **Database state** - Tests query a shared database; use unique stream names if needed
## Troubleshooting
### Tests fail with "connection refused"
- Ensure PostgreSQL is running and accessible
- Check `ZO_META_POSTGRES_DSN` environment variable
### Tests fail with "table not found"
- OpenObserve may not have initialized the database schema
- Check OpenObserve logs for migration errors
### Tests timeout
- Increase wait times in `wait_for_ingestion()`
- Check if OpenObserve is running and healthy
## Future Enhancements
- [ ] Add SQLite backend testing
- [ ] Add tests for data compaction
- [ ] Add tests for schema evolution
- [ ] Add tests for multi-tenancy
- [ ] Add performance/load tests
- [ ] Add tests for backup/restore operations