Files
basicmachines-co-basic-memory/tests
phernandez 2bd6552e84 fix(mcp): address PR review feedback (P2 + multi-project search)
Two follow-up fixes on the same branch:

1. **Purge sqlite-vec embeddings during project delete** (Codex P2)
   sqlite-vec stores vectors in a vec0 virtual table keyed by chunk rowid
   with no cascade. The previous purge removed search_vector_chunks but
   left the embeddings behind; `_run_vector_query` then keeps returning
   stale vectors that crowd live results.

   ProjectRepository.delete now deletes embeddings first (using the same
   rowid-IN-chunks pattern as SQLiteSearchRepository.delete_project_vector_rows),
   then the chunk rows. Both deletes are skipped if the underlying table
   is absent on a given install. New test test_remove_project_purges_vector_embeddings
   covers the happy path and skips cleanly when the embeddings table
   isn't initialized.

2. **Fix search_all_projects=True on local installs**
   `_search_all_projects` recurses into search_notes with both project=
   and project_id= set. project_id (external UUID) routes through the
   cloud v2 API path, which 401s on local installs because there's no
   JWT to present — so the inner calls silently failed and the merged
   result list stayed empty.

   The fan-out now mirrors get_project_client's cloud_available composite
   (factory mode OR explicit --cloud OR has_cloud_credentials). When that
   composite is false we forward project= only and take the name-routed
   local-ASGI path. Cloud disambiguation still works because the project
   name in project_ref is already the workspace/project qualified_name.

   The existing cloud-style fan-out tests now go through a cloud_routing
   fixture that pins the three signals; a new local_routing test confirms
   project_id is dropped when no cloud route is available.

Signed-off-by: phernandez <paul@basicmachines.co>
2026-05-16 15:11:39 -05:00
..
2026-04-23 10:12:46 -05:00

Dual-Backend Testing

Basic Memory tests run against both SQLite and Postgres backends to ensure compatibility.

Quick Start

# Run tests against SQLite only (default, no setup needed)
pytest

# Run tests against Postgres only (requires docker-compose)
docker-compose -f docker-compose-postgres.yml up -d
pytest -m postgres

# Run tests against BOTH backends
docker-compose -f docker-compose-postgres.yml up -d
pytest --run-all-backends  # Not yet implemented - run both commands above

How It Works

Parametrized Backend Fixture

The db_backend fixture is parametrized to run tests against both sqlite and postgres:

@pytest.fixture(
    params=[
        pytest.param("sqlite", id="sqlite"),
        pytest.param("postgres", id="postgres", marks=pytest.mark.postgres),
    ]
)
def db_backend(request) -> Literal["sqlite", "postgres"]:
    return request.param

Backend-Specific Engine Factories

Each backend has its own engine factory implementation:

  • sqlite_engine_factory - Uses in-memory SQLite (fast, isolated)
  • postgres_engine_factory - Uses Postgres test database (realistic, requires Docker)

The main engine_factory fixture delegates to the appropriate implementation based on db_backend.

Configuration

The app_config fixture automatically configures the correct backend:

# SQLite config
database_backend = DatabaseBackend.SQLITE
database_url = None  # Uses default SQLite path

# Postgres config
database_backend = DatabaseBackend.POSTGRES
database_url = "postgresql+asyncpg://basic_memory_user:dev_password@localhost:5433/basic_memory_test"

Running Postgres Tests

1. Start Postgres Docker Container

docker-compose -f docker-compose-postgres.yml up -d

This starts:

  • Postgres 17 on port 5433 (not 5432 to avoid conflicts)
  • Test database: basic_memory_test
  • Credentials: basic_memory_user / dev_password

2. Run Postgres Tests

# Run only Postgres tests
pytest -m postgres

# Run specific test with Postgres
pytest tests/test_entity_repository.py::test_create -m postgres

# Skip Postgres tests (default behavior)
pytest -m "not postgres"

3. Stop Docker Container

docker-compose -f docker-compose-postgres.yml down

Test Isolation

SQLite Tests

  • Each test gets a fresh in-memory database
  • Automatic cleanup (database destroyed after test)
  • No setup required

Postgres Tests

  • Database is cleaned before each test (drop all tables, recreate)
  • Tests share the same Postgres instance but get isolated schemas
  • Requires Docker Compose to be running

Markers

  • postgres - Marks tests that run against Postgres backend
  • Use -m postgres to run only Postgres tests
  • Use -m "not postgres" to skip Postgres tests (default)

CI Integration

GitHub Actions

Use service containers for Postgres (no Docker Compose needed):

jobs:
  test:
    runs-on: ubuntu-latest

    # Postgres service container
    services:
      postgres:
        image: postgres:17
        env:
          POSTGRES_DB: basic_memory_test
          POSTGRES_USER: basic_memory_user
          POSTGRES_PASSWORD: dev_password
        ports:
          - 5433:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - name: Run SQLite tests
        run: pytest -m "not postgres"

      - name: Run Postgres tests
        run: pytest -m postgres

Troubleshooting

Postgres tests fail with "connection refused"

Make sure Docker Compose is running:

docker-compose -f docker-compose-postgres.yml ps
docker-compose -f docker-compose-postgres.yml logs postgres

Port 5433 already in use

Either:

  • Stop the conflicting service
  • Change the port in docker-compose-postgres.yml and tests/conftest.py

Tests hang or timeout

Check Postgres health:

docker-compose -f docker-compose-postgres.yml exec postgres pg_isready -U basic_memory_user

Future Enhancements

  • Add --run-all-backends CLI flag to run both backends in sequence
  • Implement test fixtures for backend-specific features (e.g., Postgres full-text search vs SQLite FTS5)
  • Add performance comparison benchmarks between backends