Skip to content

Commit

Permalink
Tests: Add software tests and CI/GHA configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
amotl committed Oct 30, 2024
1 parent c473674 commit f5875b2
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 0 deletions.
16 changes: 16 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates

version: 2
updates:
- package-ecosystem: "pip" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"

- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
70 changes: 70 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: "Tests"

on:
push:
branches: [ main ]
pull_request: ~
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:

test-cpython:
name: "
CPython ${{ matrix.python-version }}
"
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: ['ubuntu-22.04']
python-version: ['3.7', '3.13']

env:
OS: ${{ matrix.os }}
PYTHON: ${{ matrix.python-version }}

services:
cratedb:
image: crate/crate:nightly
ports:
- 4200:4200

steps:

- name: Acquire sources
uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
architecture: x64
cache: 'pip'
cache-dependency-path: |
pyproject.toml
requirements*.txt
- name: Install uv
uses: astral-sh/setup-uv@v3

- name: Set up project tools
run: uv pip install --system --requirement=requirements.txt --requirement=requirements-dev.txt

- name: Run linters and software tests
run: poe test

# https://github.com/codecov/codecov-action
- name: Upload coverage results to Codecov
uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
files: ./coverage.xml
flags: main
env_vars: OS,PYTHON
name: codecov-umbrella
fail_ci_if_error: false
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
.env
.idea
__pycache__
.coverage*
coverage.xml
129 changes: 129 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
[tool.ruff]
line-length = 100

extend-exclude = [
]

lint.select = [
# Builtins
"A",
# Bugbear
"B",
# comprehensions
"C4",
# Pycodestyle
"E",
# eradicate
"ERA",
# Pyflakes
"F",
# isort
"I",
# pandas-vet
"PD",
# return
"RET",
# Bandit
"S",
# print
"T20",
"W",
# flake8-2020
"YTT",
]

lint.extend-ignore = [
# zip() without an explicit strict= parameter
"B905",
# df is a bad variable name. Be kinder to your future self.
"PD901",
# Unnecessary variable assignment before `return` statement
"RET504",
# Unnecessary `elif` after `return` statement
"RET505",
# Probable insecure usage of temporary file or directory
"S108",
# Possible SQL injection vector through string-based query construction
"S608",
]

lint.per-file-ignores."examples/*" = [
"ERA001", # Found commented-out code
"T201", # Allow `print`
]

# ===================
# Tasks configuration
# ===================
lint.per-file-ignores."tests/*" = [
"S101", # Allow use of `assert`, and `print`.
]

[tool.pytest.ini_options]
addopts = """
-rfEXs -p pytester --strict-markers --verbosity=3
--cov --cov-report=term-missing --cov-report=xml
"""
minversion = "2.0"
log_level = "DEBUG"
log_cli_level = "DEBUG"
log_format = "%(asctime)-15s [%(name)-36s] %(levelname)-8s: %(message)s"
xfail_strict = true


[tool.coverage.paths2]
source = [
".",
"examples",
]

[tool.coverage.run]
branch = false
source = [
".",
"examples",
]
omit = [
"tests/*",
]

[tool.coverage.report]
fail_under = 0
show_missing = true
exclude_lines = [
"# pragma: no cover",
"raise NotImplemented",
]


[tool.poe.tasks]

check = [
"lint",
"test",
]

format = [
{ cmd = "ruff format ." },
# Configure Ruff not to auto-fix (remove!):
# unused imports (F401), unused variables (F841), `print` statements (T201), and commented-out code (ERA001).
{ cmd = "ruff check --fix --ignore=ERA --ignore=F401 --ignore=F841 --ignore=T20 --ignore=ERA001 ." },
{ cmd = "pyproject-fmt --keep-full-version pyproject.toml" },
]

lint = [
{ cmd = "ruff format --check ." },
{ cmd = "ruff check ." },
{ cmd = "validate-pyproject pyproject.toml" },
]


[tool.poe.tasks.test]
cmd = "pytest"
help = "Invoke software tests"

[tool.poe.tasks.test.args.expression]
options = [ "-k" ]

[tool.poe.tasks.test.args.marker]
options = [ "-m" ]
6 changes: 6 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
poethepoet
pyproject-fmt<3
pytest<9
pytest-cov<7
ruff<0.8
validate-pyproject<0.23
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
requests
46 changes: 46 additions & 0 deletions tests/test_examples.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import os
import subprocess
from pathlib import Path

import pytest


@pytest.fixture(autouse=True)
def boot():
"""
Add current working dir to Python module search path.
This is needed to make the interpreter pick up `cratedb.py`
in the top-level directory.
"""
os.environ["PYTHONPATH"] = str(Path.cwd())


def test_example_usage(capfd):
"""
Validate `examples/example_usage.py` runs to completion.
"""
subprocess.check_call(["python", "examples/example_usage.py"])
out, err = capfd.readouterr()
assert "Create table" in out
assert "Drop table" in out


def test_object_examples(capfd):
"""
Validate `examples/object_examples.py` runs to completion.
"""
subprocess.check_call(["python", "examples/object_examples.py"])
out, err = capfd.readouterr()
assert "Create table" in out
assert "Drop table" in out


def test_picow_demo(capfd):
"""
Validate `examples/picow_demo.py` fails, because it needs real hardware.
"""
returncode = subprocess.call(["python", "examples/picow_demo.py"])
assert returncode == 1
out, err = capfd.readouterr()
assert "ModuleNotFoundError: No module named 'machine'" in err

0 comments on commit f5875b2

Please sign in to comment.