chore(qol): Test python and shell code samples. WIP - use for local testing only, not ready for CI. (#5154)

- Adds a Docker image, scripts, and configuration that install and run pytest-codeblocks on Python and shell code samples in Markdown.
- Copied from https://github.com/jstirnaman/docs-codeblocks/
- TODO: The script copies files into a tmp directory configured as a bind mount for the Docker container. We might want to replace this with the experimental `watch` feature.
- TODO: Use a secret to configure the token. As is, values subbed from the dotenv file are exposed in your Docker container history.
pull/5157/head
Jason Stirnaman 2023-09-26 14:56:21 -05:00 committed by GitHub
parent e278c0cd42
commit 237b01877b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 308 additions and 0 deletions

47
Dockerfile Normal file
View File

@ -0,0 +1,47 @@
# syntax=docker/dockerfile:1
# If you need more help, visit the Dockerfile reference guide at
# https://docs.docker.com/engine/reference/builder/
ARG PYTHON_VERSION=3.11.5
FROM python:${PYTHON_VERSION}-slim as base
RUN apt-get update && apt-get install -y \
curl \
git \
&& rm -rf /var/lib/apt/lists/*
ARG SOURCE_DIR
ARG TESTS_DIR
# Prevents Python from writing pyc files.
ENV PYTHONDONTWRITEBYTECODE=1
# Keeps Python from buffering stdout and stderr to avoid situations where
# the application crashes without emitting any logs due to buffering.
ENV PYTHONUNBUFFERED=1
COPY $SOURCE_DIR /app/$SOURCE_DIR
WORKDIR /app/$SOURCE_DIR
RUN chmod -R 755 /app/$SOURCE_DIR
COPY $SOURCE_DIR/run-tests.sh /usr/local/bin/run-tests.sh
RUN chmod +x /usr/local/bin/run-tests.sh
# Download dependencies as a separate step to take advantage of Docker's caching.
# Leverage a cache mount to /root/.cache/pip to speed up subsequent builds.
# Leverage a bind mount to requirements.txt to avoid having to copy them into
# into this layer.
RUN --mount=type=cache,target=/root/.cache/pip \
--mount=type=bind,source=${SOURCE_DIR}/requirements.txt,target=requirements.txt \
python -m pip install -r requirements.txt
# RUN --mount=type=cache,target=/root/.cache/node_modules \
# --mount=type=bind,source=package.json,target=package.json \
# npm install
WORKDIR $TESTS_DIR
ENTRYPOINT ["run-tests.sh"]
CMD [""]

17
compose.yaml Normal file
View File

@ -0,0 +1,17 @@
# For examples, see the Awesome Compose repository:
# https://github.com/docker/awesome-compose
services:
test:
image: docs-v2-tests
env_file:
- ./test/.env.influxdbv3
volumes:
- type: bind
source: ./test/tmp
target: /app/test/tmp
build:
context: .
dockerfile: Dockerfile
args:
- SOURCE_DIR=test
- TESTS_DIR=tmp

View File

@ -44,6 +44,8 @@ whichever threshold is met first.
Before writing data points to InfluxDB, sort tags by key in lexicographic order.
_Verify sort results match results from the [Go `bytes.Compare` function](http://golang.org/pkg/bytes/#Compare)._
<!--pytest.mark.skip-->
```sh
# Line protocol example with unsorted tags
measurement,tagC=therefore,tagE=am,tagA=i,tagD=i,tagB=think fieldKey=fieldValue 1562020262

43
test.sh Executable file
View File

@ -0,0 +1,43 @@
#! /bin/bash
# Path: test.sh
# Description:
# This script is used to copy content files for testing and to run tests on tests on those temporary copies.
# The temporary files are shared between the host and the Docker container
# using a bind mount configured in compose.yaml.
#
# Docker compose now has an experimental file watch feature
# (https://docs.docker.com/compose/file-watch/) that is likely preferable to the
# strategy here.
#
# Usage:
# The default behavior is to test all *.md files that have been added or modified in the current branch, effectively:
#
# `git diff --name-only --diff-filter=AM --relative master | grep -E '\.md$' | ./test.sh`
#
# To specify files to test, in your terminal command line, pass a file pattern as the only argument to the script--for example:
#
# sh test.sh ./content/**/*.md
##
paths="$1"
target=./test/tmp
testrun=./test/.test-run.txt
mkdir -p "$target"
cat /dev/null > "$testrun"
rm -rf "$target"/*
# Check if the user provided a path to copy.
if [ -z "$paths" ]; then
echo "No path provided. Running tests for *.md files that have been added or modified in the current branch."
paths=$(git diff --name-only --diff-filter=AM --relative master | \
grep -E '\.md$')
else
paths=$(find "$paths" -type f -name '*.md')
fi
# Log the list of files to be tested and copy them to the test directory.
echo "$paths" >> "$testrun"
echo "$paths" | rsync -arv --files-from=- . "$target"
# Start a new container and run the tests.
docker compose run --no-TTY test

35
test/.dockerignore Normal file
View File

@ -0,0 +1,35 @@
# Include any files or directories that you don't want to be copied to your
# container here (e.g., local build artifacts, temporary files, etc.).
#
# For more help, visit the .dockerignore file reference guide at
# https://docs.docker.com/engine/reference/builder/#dockerignore-file
**/.DS_Store
**/__pycache__
**/.venv
**/.classpath
**/.dockerignore
**/.env
**/.env.influxdbv3
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/bin
**/charts
**/docker-compose*
**/compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

7
test/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
/target
/Cargo.lock
node_modules
.env*
.pytest_cache
.test-run.txt
tmp

105
test/README.md Normal file
View File

@ -0,0 +1,105 @@
Test code blocks in Markdown files.
This project contains the following:
- `test.sh`: The primary entrypoint for running tests.
Copies Markdown files to a temporary directory shared with the `test` Docker image and runs the test container.
- `test/run-tests.sh`: The Docker image entrypoint.
Substitutes placeholders with environment variables in code blocks.
Passes test files to test runners (for example, `pytest --codeblocks` for Python and shell code samples).
- `compose.yaml` and `Dockerfile`: Docker image for the **test** service that installs test dependencies and passes test files to test runners.
## Set configuration values
To set your InfluxDB credentials for testing, create the file `.env.influxdbv3` and add key=value properties for the following:
```text
INFLUX_HOST=https://us-east-1-1.aws.cloud2.influxdata.com
INFLUX_TOKEN=3S3SFrpFbnNR_pZ3Cr6LMN...==
INFLUX_ORG=28d1f2f.........
INFLUX_DATABASE=get-started
```
Storing configuration properties in a `.env` (dotenv) file is generally preferable to using environment variables.
## Build the image
1. Install Docker for your system.
2. Build the Docker image.
```shell
docker compose build test
```
After editing configuration or files used by the image, re-run the preceding build command.
## Run tests
Test code blocks in Markdown files that have changed relative to your git `master` branch:
```sh
sh test.sh
```
Test code blocks in files that match a pattern:
```sh
sh test.sh ./content/**/*.md
```
`test.sh` copies files into `./test/tmp/` for testing and runs the tests in Docker.
### Test runners
_Experimental--work in progress_
[pytest-codeblocks](https://github.com/nschloe/pytest-codeblocks/tree/main) extracts code from python and shell Markdown code blocks and executes assertions for the code.
If you don't assert a value, `--codeblocks` considers a non-zero exit code to be a failure.
_Note_: `pytest --codeblocks` uses Python's `subprocess.run()` to execute shell code.
To assert (and display) the expected output of your code, follow the code block with the `<!--pytest-codeblocks:expected-output-->` comment tag, and then the expected output in a code block--for example:
```python
print("Hello, world!")
```
<!--pytest-codeblocks:expected-output-->
If successful, the output is the following:
```
Hello, world!
```
pytest-codeblocks has features for skipping tests and marking blocks as failed.
To learn more, see the pytest-codeblocks README and tests.
#### Other tools and ideas for testing code blocks
The `codedown` NPM package extracts code from Markdown code blocks for each language and
can pipe the output to a test runner for the language.
`pytest` and `pytest-codeblocks` use the Python `Assertions` module to keep testing overhead low.
Node.js also provides an `Assertions` package.
The `runmd` NPM package runs `javascript` code blocks in Markdown and generates a new Markdown file with the code block output inserted.
## Troubleshoot
### Pytest collected 0 items
Potential reasons:
- See the test discovery options in `pytest.ini`.
- For Python code blocks, use the following delimiter:
```python
# Codeblocks runs this block.
```
`pytest --codeblocks` ignores code blocks that use the following:
```py
# Codeblocks ignores this block.
```

18
test/package.json Normal file
View File

@ -0,0 +1,18 @@
{
"name": "docs-codesamples",
"version": "1.0.0",
"description": "",
"main": "index.js",
"directories": {
"example": "examples"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"runmd": "^1.3.9"
}
}

11
test/pytest.ini Normal file
View File

@ -0,0 +1,11 @@
# pytest configuration file
[pytest]
# Collect tests.
# Collect tests from files matching file name pattern.
python_files = *_test.py *_test_sh.py
# Collect classes with names ending in Test.
python_classes = *Test
# Collect all functions.
python_functions = *
filterwarnings = ignore::pytest.PytestReturnNotNoneWarning

8
test/requirements.txt Normal file
View File

@ -0,0 +1,8 @@
## Code sample dependencies
influxdb3-python
pandas
python-dotenv==1.0.0
## Test dependencies
pytest==7.4.1
pytest-cov==2.12.1
pytest-codeblocks==0.16.1

15
test/run-tests.sh Normal file
View File

@ -0,0 +1,15 @@
#!/bin/bash
for file in "$TEST_DIR"/*; do
if [ -f "$file" ]; then
# Replace placeholder values with environment variables.
sed -i '' 's|https:\/\/{{< influxdb/host >}}|$INFLUX_HOST|g;
s/API_TOKEN/$INFLUX_TOKEN/g;
s/DATABASE_TOKEN/$INFLUX_TOKEN/g;
s/BUCKET_NAME/$INFLUX_DATABASE/g;
s/DATABASE_NAME/$INFLUX_DATABASE/g;' \
$file
fi
done
pytest --codeblocks $TEST_DIR