Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 68 additions & 8 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,27 @@ on:
branches: [ master ]
pull_request:
branches: [ master ]
workflow_dispatch:

permissions:
contents: read

jobs:
test:
build-test:
name: build-test (py ${{ matrix.python-version }})
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
python-version: [3.7, 3.12]
python-version: ["3.8", "3.10", "3.12"]

steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

Expand All @@ -40,17 +42,75 @@ jobs:
- name: Run build tests
run: |
python -m pip install pytest
python -m pytest tests/ci/build-test/
python -m pytest tests/ci/build-test/ -v

unit-test:
name: unit-test (py ${{ matrix.python-version }})
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.12"]

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies (with test extras)
run: |
python -V
python -m pip install --upgrade pip setuptools
python -m pip install -e .[test]

- name: Run unit tests (no network)
run: |
python -m pytest tests/unit/ -v

e2e-test:
# Manual-only: hits a real SLS endpoint via LOG_TEST_* secrets.
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Install dependencies (with test extras)
run: |
python -V
python -m pip install --upgrade pip setuptools
python -m pip install -e .[test]

- name: Run e2e tests
env:
LOG_TEST_ENDPOINT: ${{ secrets.LOG_TEST_ENDPOINT }}
LOG_TEST_ACCESS_KEY_ID: ${{ secrets.LOG_TEST_ACCESS_KEY_ID }}
LOG_TEST_ACCESS_KEY_SECRET: ${{ secrets.LOG_TEST_ACCESS_KEY_SECRET }}
LOG_TEST_PROJECT: ${{ secrets.LOG_TEST_PROJECT }}
LOG_TEST_LOGSTORE: ${{ secrets.LOG_TEST_LOGSTORE }}
run: |
python -m pytest tests/e2e/ -v -m e2e

typing:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4

- name: Set up Python 3.12
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: '3.12'

Expand Down
59 changes: 49 additions & 10 deletions .github/workflows/py2-build.yaml
Original file line number Diff line number Diff line change
@@ -1,28 +1,46 @@


name: Py2-build-test
name: Legacy-python-build-test

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
workflow_dispatch:

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-20.04
build-test:
name: build-test (py ${{ matrix.python-version }})
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
include:
- python-version: "2.7"
image: python:2.7.18-buster
- python-version: "3.6"
image: python:3.6.15-buster
- python-version: "3.7"
image: python:3.7.17-bullseye

container:
image: python:2.7.18-buster
image: ${{ matrix.image }}

steps:
- name: Checkout repository
uses: actions/checkout@v2

uses: actions/checkout@v4

- name: Install dependencies
run: |
python -V
python -m pip install --upgrade pip setuptools
if python -c "import sys; sys.exit(0 if sys.version_info < (3,) else 1)"; then
python -m pip install "pip<21" "setuptools<45"
else
python -m pip install --upgrade pip setuptools
fi
python -m pip install .

- name: Show dependencies
Expand All @@ -32,4 +50,25 @@ jobs:
- name: Run build tests
run: |
python -m pip install pytest
python -m pytest tests/ci/build-test/
python -m pytest tests/ci/build-test/ -v

unit-test-py37:
name: unit-test (py 3.7, container)
runs-on: ubuntu-latest

container:
image: python:3.7.17-bullseye

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install dependencies (with test extras)
run: |
python -V
python -m pip install --upgrade pip setuptools
python -m pip install -e .[test]

- name: Run unit tests (no network)
run: |
python -m pytest tests/unit/ -v
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ cscope.out
cscope.po.out
tags
.venv-stubtest/
.venv-test/
.pytest_cache/
__pycache__/
120 changes: 120 additions & 0 deletions TESTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Testing

The Python SDK separates fast, hermetic unit tests from end-to-end tests that
require a real SLS endpoint.

## Layout

```
tests/
_helpers/ shared env + mock infrastructure
unit/ no network, runs in CI on every push
e2e/ hits a real SLS endpoint, gated by env vars
ci/build-test/ Python 2/3 compile + protobuf smoke check (py2-build.yaml)
sample*.py runnable sample scripts (NOT collected by pytest)
consumer_group_examples/ topic-grouped example scripts
export_examples/
jupyter_magic_test/
rebuild_index_examples/
es_migration/ migration_manager example scripts
```

`pyproject.toml` sets `testpaths = ["tests/unit", "tests/e2e"]`, so the sample
and example scripts are ignored by `pytest` collection. They are run directly
(e.g. via `tox.ini` for `tests/sample.py` and `tests/sample_consumer.py`) or
referenced from the README and tutorial docs.

## Running unit tests

```sh
pip install -e .[test]
pytest tests/unit/ -v
```

Unit tests must not touch the network. The repo includes
`tests/unit/test_logclient_mock.py` as a worked example using `responses`.

## Running e2e tests

```sh
export LOG_TEST_ENDPOINT=cn-hangzhou.log.aliyuncs.com
export LOG_TEST_ACCESS_KEY_ID=...
export LOG_TEST_ACCESS_KEY_SECRET=...
export LOG_TEST_PROJECT=...
export LOG_TEST_LOGSTORE=...

pytest tests/e2e/ -v -m e2e
```

If any of the required vars are missing, every e2e test is auto-skipped.

The conftest also reads legacy variable names so older shell setups keep
working:

| Canonical (preferred) | Legacy aliases (also accepted) |
|-------------------------------|--------------------------------------------------------------------------------------------------|
| `LOG_TEST_ENDPOINT` | `ALIYUN_LOG_SAMPLE_ENDPOINT`, `TEST_ENDPOINT`, `SLS_ENDPOINT` |
| `LOG_TEST_ACCESS_KEY_ID` | `ALIYUN_LOG_SAMPLE_ACCESSID`, `TEST_ACCESS_KEY_ID`, `SLS_AK_ID` |
| `LOG_TEST_ACCESS_KEY_SECRET` | `ALIYUN_LOG_SAMPLE_ACCESSKEY`, `TEST_ACCESS_KEY_SECRET`, `SLS_AK_KEY` |
| `LOG_TEST_PROJECT` | `ALIYUN_LOG_SAMPLE_PROJECT`, `TEST_PROJECT`, `SLS_PROJECT` |
| `LOG_TEST_LOGSTORE` | `ALIYUN_LOG_SAMPLE_LOGSTORE`, `TEST_LOGSTORE`, `SLS_LOGSTORE` |

## Writing new tests

### Unit tests with mocked SLS responses

Use `responses` plus the helpers in `tests/_helpers/fakes.py`:

```python
import re

import pytest
import responses

from aliyun.log import GetLogsRequest, LogException
from tests._helpers.fakes import error_response, make_client, mock_sls_response


@responses.activate
def test_my_query():
client = make_client(endpoint="cn-mock.example.com", project="mock-proj")
mock_sls_response(
responses,
"POST",
re.compile(r"https?://mock-proj\.cn-mock\.example\.com.*?/logstores/store-1/logs.*"),
status=200,
body={"meta": {"count": 0, "progress": "Complete"}, "data": []},
)
req = GetLogsRequest("mock-proj", "store-1", 1700000000, 1700000100, query="*")
assert client.get_logs(req).get_count() == 0


@responses.activate
def test_error_path():
client = make_client()
mock_sls_response(
responses, "POST",
re.compile(r"https?://.*?/logstores/.*"),
status=400,
body=error_response("ParameterInvalid", "bad"),
)
with pytest.raises(LogException):
client.get_logs(GetLogsRequest("p", "s", 0, 1, "*"))
```

### E2E tests against a real endpoint

```python
import pytest

pytestmark = pytest.mark.e2e


def test_round_trip(e2e_client, sls_env):
# `e2e_client` is an aliyun.log.LogClient built from LOG_TEST_* env vars.
# `sls_env` exposes endpoint/project/logstore/etc.
e2e_client.list_project(size=1)
```

The autouse env check skips the whole test cleanly when env vars are missing,
so contributors without SLS credentials can still run `pytest tests/unit/`.
6 changes: 6 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[tool.pytest.ini_options]
testpaths = ["tests/unit", "tests/e2e"]
markers = [
"e2e: requires real SLS endpoint (LOG_TEST_* env vars)",
]
filterwarnings = ["ignore::DeprecationWarning"]
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@

test_requirements = [
'pytest',
'pytest-mock',
'responses',
'lz4',
'virtualenv',
'zstandard'
Expand Down
Empty file added tests/_helpers/__init__.py
Empty file.
Loading
Loading