diff --git a/.github/workflows/test_lemonade_server.yml b/.github/workflows/test_lemonade_server.yml index b02b981a6..a3ed71404 100644 --- a/.github/workflows/test_lemonade_server.yml +++ b/.github/workflows/test_lemonade_server.yml @@ -7,9 +7,27 @@ on: workflow_call: push: branches: [ main ] + paths: + - 'src/gaia/llm/**' + - 'src/gaia/installer/**' + - 'setup.py' + - '.github/workflows/test_lemonade_server.yml' + - '.github/actions/install-lemonade/**' + - '.github/actions/setup-venv/**' + - 'installer/**' + - 'tests/test_lemonade*.py' pull_request: branches: [ main ] types: [opened, synchronize, reopened, ready_for_review] + paths: + - 'src/gaia/llm/**' + - 'src/gaia/installer/**' + - 'setup.py' + - '.github/workflows/test_lemonade_server.yml' + - '.github/actions/install-lemonade/**' + - '.github/actions/setup-venv/**' + - 'installer/**' + - 'tests/test_lemonade*.py' merge_group: workflow_dispatch: inputs: diff --git a/.github/workflows/test_mcp.yml b/.github/workflows/test_mcp.yml index a330e6018..986b7ddea 100644 --- a/.github/workflows/test_mcp.yml +++ b/.github/workflows/test_mcp.yml @@ -13,6 +13,7 @@ on: branches: [ main ] paths: - 'src/gaia/mcp/**' + - 'src/gaia/cli.py' - 'tests/mcp/**' - 'setup.py' - '.github/workflows/test_mcp.yml' @@ -21,6 +22,7 @@ on: types: [opened, synchronize, reopened, ready_for_review] paths: - 'src/gaia/mcp/**' + - 'src/gaia/cli.py' - 'tests/mcp/**' - 'setup.py' - '.github/workflows/test_mcp.yml' diff --git a/.github/workflows/test_unit.yml b/.github/workflows/test_unit.yml index cafd73051..68dc507c7 100644 --- a/.github/workflows/test_unit.yml +++ b/.github/workflows/test_unit.yml @@ -39,17 +39,21 @@ permissions: jobs: unit-tests: - name: Run Unit Tests + name: Unit Tests (py${{ matrix.python-version }}) runs-on: ubuntu-latest timeout-minutes: 30 if: github.event_name != 'pull_request' || github.event.pull_request.draft == false || contains(github.event.pull_request.labels.*.name, 'ready_for_ci') + strategy: + fail-fast: false + matrix: + python-version: ['3.10', '3.11', '3.12'] steps: - uses: actions/checkout@v6 - name: Set up Python uses: actions/setup-python@v6 with: - python-version: '3.12' + python-version: ${{ matrix.python-version }} - name: Install uv run: curl -LsSf https://astral.sh/uv/install.sh | sh @@ -63,8 +67,8 @@ jobs: # keyring + httpx + respx are required by tests/unit/connections/ # (issue #915). The in-memory keyring backend in tests/conftest.py # avoids the SecretService daemon prerequisite on Linux runners. - uv pip install --system pytest pytest-cov pytest-asyncio pytest-mock pyfakefs \ - keyring httpx respx + uv pip install --system pytest pytest-cov pytest-asyncio pytest-mock pytest-timeout \ + pyfakefs keyring httpx respx uv pip install --system -e ".[api]" - name: Validate packaging integrity @@ -149,3 +153,44 @@ jobs: echo "Integration Tests:" echo " - DatabaseMixin + Agent: Full agent lifecycle with database" echo " - DatabaseAgent: Auto-registered database tools" + + # Experimental macOS smoke test — validates core SDK imports and pure-Python + # unit tests on Darwin. continue-on-error until the full suite is macOS-clean. + unit-tests-macos: + name: Unit Tests (macOS smoke) + runs-on: macos-latest + if: github.event_name != 'pull_request' || github.event.pull_request.draft == false || contains(github.event.pull_request.labels.*.name, 'ready_for_ci') + continue-on-error: true + steps: + - uses: actions/checkout@v6 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: '3.12' + + - name: Install uv + run: curl -LsSf https://astral.sh/uv/install.sh | sh + + - name: Install dependencies + run: | + uv pip install --system pytest pytest-asyncio pytest-mock pytest-timeout \ + pyfakefs keyring httpx respx + uv pip install --system -e ".[api]" + + - name: Validate packaging integrity + run: | + echo "=== Validating Packaging Integrity ===" + echo "Checking setup.py packages, __init__.py files, and entry points" + echo "" + pytest tests/unit/test_packaging.py -v --tb=short + echo "✅ Packaging integrity checks passed" + + - name: Run unit tests (macOS smoke) + env: + GAIA_MEMORY_DISABLED: "1" + run: | + echo "=== macOS Smoke Test ===" + echo "Running unit tests on macOS to catch platform-specific issues" + echo "" + pytest tests/unit/ -x --timeout=60 -v --tb=short diff --git a/tests/unit/test_llamacpp_backend.py b/tests/unit/test_llamacpp_backend.py index 1ab58db2e..ff82a36a3 100644 --- a/tests/unit/test_llamacpp_backend.py +++ b/tests/unit/test_llamacpp_backend.py @@ -587,13 +587,15 @@ def test_no_model_loaded_by_message(self): class TestLlamaServerCorruptDownload: """Verify _is_corrupt_download_error catches llama-server startup failures.""" - def test_llama_server_failed_to_start(self): - """'llama-server failed to start' indicates corrupt model files.""" + def test_llama_server_failed_to_start_is_not_corruption(self): + """'llama-server failed to start' is NOT a corruption signal — Lemonade + emits it for many non-corruption failures (resource limits, ctx_size, + port conflicts), so it must not trigger the delete+redownload path.""" client = LemonadeClient(host="localhost", port=13305) error = Exception( "model_load_error: llama-server failed to start after loading model" ) - assert client._is_corrupt_download_error(error) is True + assert client._is_corrupt_download_error(error) is False def test_incomplete_download(self): client = LemonadeClient(host="localhost", port=13305)