Skip to content

Commit 4dcf75e

Browse files
PromoFauxclaude
andcommitted
ci: replace Python test suite with BATS and consolidate workflows
Replace the pytest/testinfra/tox test stack with BATS, aligning with the approach used in the FTL repository. - Merge build-and-test.yml into build-and-publish.yml; the combined lint+test job now runs on pull_request via a single bash test/run.sh call, removing the need for Python/tox in CI - Replace Python test files with test/run.sh and test/test_suite.bats - test/run.sh handles image build, BATS install, container lifecycle, and cleanup via trap in one place - Containers consolidated from 6 to 2 (CONTAINER_DEFAULT and CONTAINER_CUSTOM), removing tests that belong to FTL's own suite - Tests now focus on Docker-specific behaviour: entrypoint, signal handling, UID/GID mapping, cron setup, and password assignment Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Adam Warner <me@adamwarner.co.uk>
1 parent 9b0e152 commit 4dcf75e

12 files changed

Lines changed: 222 additions & 279 deletions

.github/workflows/build-and-publish.yml

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,39 @@ env:
1616
components_branch: ${{ github.event_name == 'release' && 'master' || 'development' }}
1717

1818
jobs:
19+
test:
20+
if: github.event_name == 'pull_request'
21+
runs-on: ${{ matrix.runner }}
22+
strategy:
23+
fail-fast: false
24+
matrix:
25+
include:
26+
- platform: linux/amd64
27+
runner: ubuntu-latest
28+
- platform: linux/386
29+
runner: ubuntu-latest
30+
- platform: linux/arm/v6
31+
runner: ubuntu-24.04-arm
32+
- platform: linux/arm/v7
33+
runner: ubuntu-24.04-arm
34+
- platform: linux/arm64
35+
runner: ubuntu-24.04-arm
36+
- platform: linux/riscv64
37+
runner: ubuntu-24.04-arm
38+
env:
39+
CI_ARCH: ${{ matrix.platform }}
40+
steps:
41+
- name: Checkout Repo
42+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
43+
44+
- name: Set up QEMU
45+
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a #v4.0.0
46+
47+
- name: Test
48+
run: CIPLATFORM=${{ env.CI_ARCH }} bash test/run.sh
49+
1950
build-prepare:
51+
if: github.event_name != 'pull_request'
2052
runs-on: ubuntu-24.04
2153
outputs:
2254
components_branch: ${{ env.components_branch }}
@@ -25,6 +57,7 @@ jobs:
2557
- run: echo "Exposing env vars for reusable workflow"
2658

2759
build:
60+
if: github.event_name != 'pull_request'
2861
uses: docker/github-builder/.github/workflows/build.yml@v1
2962
needs:
3063
- build-prepare
@@ -45,7 +78,7 @@ jobs:
4578
WEB_BRANCH=${{ needs.build-prepare.outputs.components_branch }}
4679
PADD_BRANCH=${{ needs.build-prepare.outputs.components_branch }}
4780
platforms: linux/amd64,linux/386,linux/arm/v6,linux/arm/v7,linux/arm64,linux/riscv64
48-
push: ${{ github.event_name != 'pull_request' }}
81+
push: true
4982
set-meta-labels: true
5083
meta-images: |
5184
pihole/pihole

.github/workflows/build-and-test.yml

Lines changed: 0 additions & 54 deletions
This file was deleted.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ etc-pihole/
1515
var-log/
1616
.vscode/
1717
.pytest_cache/
18+
test/libs/
1819

1920
# WIP/test stuff
2021
doco.yml

test/TESTING.md

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
# Prerequisites
22

3-
Make sure you have `docker`, `python` and `tox` installed.
3+
Make sure you have `docker` and `git` installed.
44

55
# Running tests locally
66

7-
`tox -c test/tox.ini`
7+
```sh
8+
bash test/run.sh
9+
```
810

9-
Should result in:
11+
This will:
1012

11-
- An image named `pihole:CI_container` being built
12-
- Tests being ran to confirm the image doesn't have any regressions
13+
- Build an image named `pihole:test`
14+
- Start a set of containers (one per configuration under test)
15+
- Run the BATS test suite against those containers
16+
- Remove all test containers on exit
17+
18+
To test a specific platform via emulation, set `CIPLATFORM`:
19+
20+
```sh
21+
CIPLATFORM=linux/arm64 bash test/run.sh
22+
```

test/requirements.txt

Lines changed: 0 additions & 6 deletions
This file was deleted.

test/run.sh

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Run from the test/ directory regardless of where the script is called from
5+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6+
cd "$SCRIPT_DIR"
7+
8+
# ---- Build the image --------------------------------------------------------
9+
10+
PLATFORM_ARGS=()
11+
[ -n "${CIPLATFORM:-}" ] && PLATFORM_ARGS=(--platform "${CIPLATFORM}")
12+
13+
docker buildx build \
14+
--load \
15+
"${PLATFORM_ARGS[@]}" \
16+
--progress plain \
17+
-f ../src/Dockerfile \
18+
-t pihole:test \
19+
../src/
20+
21+
# ---- Install BATS -----------------------------------------------------------
22+
23+
if [ -z "${BATS:-}" ]; then
24+
mkdir -p libs
25+
if [ ! -d libs/bats ]; then
26+
git clone --depth=1 --quiet https://github.com/bats-core/bats-core libs/bats
27+
fi
28+
BATS=libs/bats/bin/bats
29+
fi
30+
31+
# ---- Start containers -------------------------------------------------------
32+
33+
# Cleanup all test containers on exit (success or failure)
34+
CONTAINERS=()
35+
cleanup() {
36+
if [ ${#CONTAINERS[@]} -gt 0 ]; then
37+
docker rm -f "${CONTAINERS[@]}" > /dev/null 2>&1 || true
38+
fi
39+
}
40+
trap cleanup EXIT
41+
42+
start_container() {
43+
local id
44+
id=$(docker run -d -t "${PLATFORM_ARGS[@]}" -e TZ="Europe/London" "$@" pihole:test)
45+
CONTAINERS+=("$id")
46+
echo "$id"
47+
}
48+
49+
CONTAINER_DEFAULT=$(start_container)
50+
CONTAINER_CUSTOM=$(start_container \
51+
-e PIHOLE_UID=456 \
52+
-e PIHOLE_GID=456 \
53+
-e FTLCONF_webserver_api_password=1234567890)
54+
55+
export CONTAINER_DEFAULT CONTAINER_CUSTOM CIPLATFORM
56+
57+
# ---- Wait for containers to be ready ----------------------------------------
58+
59+
wait_for_ftl() {
60+
local container="$1"
61+
local timeout=60
62+
local elapsed=0
63+
printf "Waiting for FTL in %.12s... " "${container}"
64+
until docker logs "${container}" 2>&1 | grep -q "########## FTL started"; do
65+
sleep 1
66+
elapsed=$(( elapsed + 1 ))
67+
if (( elapsed >= timeout )); then
68+
echo "TIMEOUT"
69+
echo "--- Container logs ---"
70+
docker logs "${container}"
71+
return 1
72+
fi
73+
done
74+
echo "ready (${elapsed}s)"
75+
}
76+
77+
for container in "$CONTAINER_DEFAULT" "$CONTAINER_CUSTOM"; do
78+
wait_for_ftl "$container"
79+
done
80+
81+
# ---- Run BATS ---------------------------------------------------------------
82+
83+
"$BATS" -p test_suite.bats

test/test_suite.bats

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#!/usr/bin/env bats
2+
3+
# Containers are started by run.sh and their IDs exported as environment
4+
# variables. All tests (except the shutdown test) share these containers,
5+
# so each configuration is only booted once per test run.
6+
#
7+
# CONTAINER_DEFAULT - no extra env vars
8+
# CONTAINER_CUSTOM - PIHOLE_UID=456, PIHOLE_GID=456, FTLCONF_webserver_api_password=1234567890
9+
10+
# ---- FTL binary -------------------------------------------------------------
11+
12+
@test "FTL reports version" {
13+
run docker exec "$CONTAINER_DEFAULT" pihole-FTL -vv
14+
[ "$status" -eq 0 ]
15+
[[ "$output" == *"Version:"* ]]
16+
}
17+
18+
@test "FTL reports correct architecture" {
19+
[ -n "${CIPLATFORM:-}" ] || skip "CIPLATFORM not set, running locally"
20+
run docker exec "$CONTAINER_DEFAULT" pihole-FTL -vv
21+
[ "$status" -eq 0 ]
22+
[[ "$output" == *"Architecture:"* ]]
23+
[[ "$output" == *"$CIPLATFORM"* ]]
24+
}
25+
26+
@test "FTL starts up and shuts down cleanly" {
27+
# This test needs its own container because it stops it
28+
local platform_args=()
29+
[ -n "${CIPLATFORM:-}" ] && platform_args=(--platform "$CIPLATFORM")
30+
31+
local container
32+
container=$(docker run -d -t "${platform_args[@]}" -e TZ="Europe/London" pihole:test)
33+
34+
# Wait for FTL to start
35+
local timeout=60
36+
local elapsed=0
37+
until docker logs "$container" 2>&1 | grep -q "########## FTL started"; do
38+
sleep 1
39+
elapsed=$(( elapsed + 1 ))
40+
if (( elapsed >= timeout )); then
41+
docker rm -f "$container"
42+
echo "FTL did not start within ${timeout}s"
43+
return 1
44+
fi
45+
done
46+
47+
# Stop gracefully (SIGTERM), then capture logs before removing
48+
docker stop "$container"
49+
local logs
50+
logs=$(docker logs "$container" 2>&1)
51+
docker rm "$container"
52+
53+
[[ "$logs" == *"INFO: ########## FTL terminated after"* ]]
54+
[[ "$logs" == *"(code 0)"* ]]
55+
}
56+
57+
# ---- Container configuration ------------------------------------------------
58+
59+
@test "Cron file is valid" {
60+
run docker exec "$CONTAINER_DEFAULT" bash -c \
61+
"/usr/bin/crontab /crontab.txt 2>&1; crond -d 8 -L /cron.log 2>&1; cat /cron.log"
62+
[[ "$output" != *"parse error"* ]]
63+
}
64+
65+
@test "Custom PIHOLE_UID is applied to pihole user" {
66+
run docker exec "$CONTAINER_CUSTOM" id -u pihole
67+
[ "$status" -eq 0 ]
68+
[ "$output" = "456" ]
69+
}
70+
71+
@test "Custom PIHOLE_GID is applied to pihole group" {
72+
run docker exec "$CONTAINER_CUSTOM" id -g pihole
73+
[ "$status" -eq 0 ]
74+
[ "$output" = "456" ]
75+
}
76+
77+
# ---- Web password setup -----------------------------------------------------
78+
79+
@test "Random password is assigned on fresh start" {
80+
run docker logs "$CONTAINER_DEFAULT"
81+
[ "$status" -eq 0 ]
82+
[[ "$output" == *"assigning random password:"* ]]
83+
}
84+
85+
@test "Password defined by environment variable is used" {
86+
run docker exec "$CONTAINER_CUSTOM" bash -c ". bash_functions.sh; setup_web_password"
87+
[ "$status" -eq 0 ]
88+
[[ "$output" == *"Assigning password defined by Environment Variable"* ]]
89+
}

test/tests/__init__.py

Whitespace-only changes.

test/tests/conftest.py

Lines changed: 0 additions & 60 deletions
This file was deleted.

0 commit comments

Comments
 (0)