mirror of
https://github.com/pi-hole/pi-hole.git
synced 2026-05-29 11:19:14 +02:00
Merge branch 'development' into april-os
This commit is contained in:
@@ -0,0 +1 @@
|
||||
test/libs/
|
||||
@@ -1,40 +0,0 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- development
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- development
|
||||
schedule:
|
||||
- cron: '32 11 * * 6'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
steps:
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
-
|
||||
name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 #v4.35.2
|
||||
with:
|
||||
languages: 'python'
|
||||
-
|
||||
name: Autobuild
|
||||
uses: github/codeql-action/autobuild@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 #v4.35.2
|
||||
-
|
||||
name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 #v4.35.2
|
||||
@@ -7,11 +7,6 @@ on:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
PYTHONUNBUFFERED: 1
|
||||
PYTHONUTF8: 1
|
||||
|
||||
jobs:
|
||||
smoke-tests:
|
||||
if: github.event.pull_request.draft == false
|
||||
@@ -25,7 +20,7 @@ jobs:
|
||||
- name: Check scripts in repository are executable
|
||||
run: |
|
||||
IFS=$'\n';
|
||||
for f in $(find . -name '*.sh'); do if [[ ! -x $f ]]; then echo "$f is not executable" && FAIL=1; fi ;done
|
||||
for f in $(find . -name '*.sh' -o -name '*.bats'); do if [[ ! -x $f ]]; then echo "$f is not executable" && FAIL=1; fi ;done
|
||||
unset IFS;
|
||||
# If FAIL is 1 then we fail.
|
||||
[[ $FAIL == 1 ]] && exit 1 || echo "Scripts are executable!"
|
||||
@@ -37,7 +32,7 @@ jobs:
|
||||
display-engine: sarif-fmt
|
||||
|
||||
- name: Secret Scanning with TruffleHog
|
||||
uses: trufflesecurity/trufflehog@17456f8c7d042d8c82c9a8ca9e937231f9f42e26 #v3.95.2
|
||||
uses: trufflesecurity/trufflehog@37b77001d0174ebec2fcca2bd83ff83a6d45a3ab #v3.95.3
|
||||
with:
|
||||
extra_args: --results=verified,unknown
|
||||
|
||||
@@ -52,12 +47,6 @@ jobs:
|
||||
- name: Run editorconfig-checker
|
||||
run: editorconfig-checker
|
||||
|
||||
- name: Check python code formatting with black
|
||||
uses: psf/black@c6755bb741b6481d6b3d3bb563c83fa060db96c9 #26.3.1
|
||||
with:
|
||||
src: "./test"
|
||||
options: "--check --diff --color"
|
||||
|
||||
distro-test:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
@@ -85,22 +74,9 @@ jobs:
|
||||
alpine_3_22,
|
||||
alpine_3_23,
|
||||
]
|
||||
env:
|
||||
DISTRO: ${{matrix.distro}}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 #v6.2.0
|
||||
with:
|
||||
python-version: "3.13"
|
||||
|
||||
- name: Install wheel
|
||||
run: pip install wheel
|
||||
|
||||
- name: Install dependencies
|
||||
run: pip install -r test/requirements.txt
|
||||
|
||||
- name: Test with tox
|
||||
run: tox -c test/tox.${DISTRO}.ini
|
||||
- name: Run BATS test suite for ${{ matrix.distro }}
|
||||
run: DISTRO=${{ matrix.distro }} bash test/run.sh
|
||||
|
||||
+1
-9
@@ -1,15 +1,7 @@
|
||||
.DS_Store
|
||||
*.pyc
|
||||
*.swp
|
||||
__pycache__
|
||||
.cache
|
||||
.pytest_cache
|
||||
.tox
|
||||
.eggs
|
||||
*.egg-info
|
||||
.idea/
|
||||
*.iml
|
||||
.vscode/
|
||||
.venv/
|
||||
.fleet/
|
||||
.cache/
|
||||
test/libs/
|
||||
|
||||
@@ -162,7 +162,7 @@ EOM
|
||||
)
|
||||
|
||||
# List of required packages on APK based systems
|
||||
PIHOLE_META_VERSION_APK=0.2
|
||||
PIHOLE_META_VERSION_APK=0.3
|
||||
PIHOLE_META_DEPS_APK=(
|
||||
bash
|
||||
bash-completion
|
||||
@@ -172,6 +172,7 @@ PIHOLE_META_DEPS_APK=(
|
||||
cronie
|
||||
curl
|
||||
dialog
|
||||
gawk
|
||||
git
|
||||
grep
|
||||
iproute2-minimal # piholeARPTable.sh
|
||||
|
||||
+56
-51
@@ -772,18 +772,22 @@ gravity_DownloadBlocklistFromUrl() {
|
||||
# Define the generic error message
|
||||
curlOutputFormat='%{http_code};No message available. Non supported curl version.'
|
||||
|
||||
# Check if the installed curl version supports the "-w %{errormsg}" option (available as of curl 7.75.0)
|
||||
# (https://github.com/pi-hole/pi-hole/pull/6605#discussion_r3112153347)
|
||||
# First we get the current curl version.
|
||||
# Get the current installed curl version.
|
||||
curlVersion=$(curl --version | awk '{print $2;exit}')
|
||||
# After that, we pipe the current version along with the string '7.75' (the minimum version supporting the required option.)
|
||||
# Then we sort the list in natural (version) order and return the first item which will be the lowest version seen.
|
||||
# If it is "7.75" then the current version is greater than or equal to "7.75.0".
|
||||
# Compatibility notes:
|
||||
# Busybox doesn't support some long flags:
|
||||
# - "sort -V" is short form of "sort --version-sort"
|
||||
# - "head -n1" is short form of "head --lines=1"
|
||||
if [[ "$(printf '%s\n' "${curlVersion}" "7.75" | sort -V | head -n1)" == 7.75 ]]; then
|
||||
|
||||
# Check if the installed curl version supports the "-w %{errormsg}" option.
|
||||
# The minimum curl version supporting this option is 7.75.0.
|
||||
# (https://github.com/pi-hole/pi-hole/pull/6605#discussion_r3112153347)
|
||||
#
|
||||
# We use "awk" to compare versions by subtracting 7.75 from the version number.
|
||||
# If the result is greater than or equal to zero, the option is supported.
|
||||
# (see https://github.com/pi-hole/pi-hole/issues/6615)
|
||||
#
|
||||
# Notes:
|
||||
# - Use parameter expansion to get only Major and Minor version parts (containing only one dot).
|
||||
# - The comparison result will be true or false. We use it as exit code.
|
||||
# - awk considers "true=1". We negate the comparison to exit with "0" when a desired version is found.
|
||||
if echo "${curlVersion%.*}" | awk '{exit !($1 - 7.75 >= 0)}'; then
|
||||
# Use the error message returned by curl
|
||||
curlOutputFormat='%{http_code};%{errormsg}'
|
||||
fi
|
||||
@@ -796,48 +800,49 @@ gravity_DownloadBlocklistFromUrl() {
|
||||
# a generic message is returned.
|
||||
curlOutput=$(curl --connect-timeout ${curl_connect_timeout} -s --fail -L ${compression:+${compression}} ${customUpstreamResolver:+${customUpstreamResolver}} "${modifiedOptions[@]}" -w "${curlOutputFormat}" "${url}" -o "${listCurlBuffer}")
|
||||
curlExitCode="$?"
|
||||
|
||||
|
||||
# Retrieve http_code and errormsg values, returned by curl command
|
||||
IFS=";" read -r httpCode curlErrorMsg <<<"$curlOutput"
|
||||
|
||||
case $url in
|
||||
# Did we "download" a local file?
|
||||
"file"*)
|
||||
if [[ -s "${listCurlBuffer}" ]]; then
|
||||
echo -e "${OVER} ${TICK} ${str} Retrieval successful"
|
||||
success=true
|
||||
else
|
||||
echo -e "${OVER} ${CROSS} ${str} Retrieval failed / empty list"
|
||||
fi
|
||||
;;
|
||||
# Did we "download" a remote file?
|
||||
*)
|
||||
# Use the exit code to determine if curl was successful or not.
|
||||
# Use HTTP code only to select the correct error message.
|
||||
if [[ "${curlExitCode}" == "0" ]]; then
|
||||
case "${httpCode}" in
|
||||
"200") echo -e "${OVER} ${TICK} ${str} Retrieval successful" ;;
|
||||
"304") echo -e "${OVER} ${TICK} ${str} No changes detected" ;;
|
||||
*) echo -e "${OVER} ${TICK} ${str} Success (http_code=${COL_CYAN}${httpCode}${COL_NC})" ;;
|
||||
esac
|
||||
success=true
|
||||
else
|
||||
case "${httpCode}" in
|
||||
"403") echo -e "${OVER} ${CROSS} ${str} Forbidden" ;;
|
||||
"404") echo -e "${OVER} ${CROSS} ${str} Not found" ;;
|
||||
"408") echo -e "${OVER} ${CROSS} ${str} Time-out" ;;
|
||||
"451") echo -e "${OVER} ${CROSS} ${str} Unavailable For Legal Reasons" ;;
|
||||
"500") echo -e "${OVER} ${CROSS} ${str} Internal Server Error" ;;
|
||||
"504") echo -e "${OVER} ${CROSS} ${str} Connection Timed Out (Gateway)" ;;
|
||||
"521") echo -e "${OVER} ${CROSS} ${str} Web Server Is Down (Cloudflare)" ;;
|
||||
"522") echo -e "${OVER} ${CROSS} ${str} Connection Timed Out (Cloudflare)" ;;
|
||||
*) echo -e "${OVER} ${CROSS} ${str} Retrieval failed (exit_code=${COL_CYAN}${curlExitCode}${COL_NC} Msg: ${COL_CYAN}${curlErrorMsg}${COL_NC})" ;;
|
||||
esac
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Retrieve http_code and errormsg values, returned by curl command
|
||||
IFS=";" read -r httpCode curlErrorMsg <<<"$curlOutput"
|
||||
|
||||
case $url in
|
||||
# Did we "download" a local file?
|
||||
"file"*)
|
||||
if [[ -s "${listCurlBuffer}" ]]; then
|
||||
echo -e "${OVER} ${TICK} ${str} Retrieval successful"
|
||||
success=true
|
||||
else
|
||||
echo -e "${OVER} ${CROSS} ${str} Retrieval failed / empty list"
|
||||
fi
|
||||
;;
|
||||
# Did we "download" a remote file?
|
||||
*)
|
||||
# Use the exit code to determine if curl was successful or not.
|
||||
# Use HTTP code only to select the correct error message.
|
||||
if [[ "${curlExitCode}" == "0" ]]; then
|
||||
case "${httpCode}" in
|
||||
"200") echo -e "${OVER} ${TICK} ${str} Retrieval successful" ;;
|
||||
"304") echo -e "${OVER} ${TICK} ${str} No changes detected" ;;
|
||||
*) echo -e "${OVER} ${TICK} ${str} Success (http_code=${COL_CYAN}${httpCode}${COL_NC})" ;;
|
||||
esac
|
||||
success=true
|
||||
else
|
||||
case "${httpCode}" in
|
||||
"403") echo -e "${OVER} ${CROSS} ${str} Forbidden" ;;
|
||||
"404") echo -e "${OVER} ${CROSS} ${str} Not found" ;;
|
||||
"408") echo -e "${OVER} ${CROSS} ${str} Time-out" ;;
|
||||
"451") echo -e "${OVER} ${CROSS} ${str} Unavailable For Legal Reasons" ;;
|
||||
"500") echo -e "${OVER} ${CROSS} ${str} Internal Server Error" ;;
|
||||
"504") echo -e "${OVER} ${CROSS} ${str} Connection Timed Out (Gateway)" ;;
|
||||
"521") echo -e "${OVER} ${CROSS} ${str} Web Server Is Down (Cloudflare)" ;;
|
||||
"522") echo -e "${OVER} ${CROSS} ${str} Connection Timed Out (Cloudflare)" ;;
|
||||
*) echo -e "${OVER} ${CROSS} ${str} Retrieval failed (exit_code=${COL_CYAN}${curlExitCode}${COL_NC} Msg: ${COL_CYAN}${curlErrorMsg}${COL_NC})" ;;
|
||||
esac
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
local done="false"
|
||||
# Determine if the blocklist was downloaded and saved correctly
|
||||
if [[ "${success}" == true ]]; then
|
||||
|
||||
+37
-14
@@ -1,25 +1,48 @@
|
||||
# Recommended way to run tests
|
||||
|
||||
Make sure you have Docker and Python w/pip package manager.
|
||||
The test suite is implemented with BATS and runs inside distro-specific Docker containers.
|
||||
|
||||
From command line all you need to do is:
|
||||
## Requirements
|
||||
|
||||
- `pip install tox`
|
||||
- `tox`
|
||||
- Docker (with buildx support)
|
||||
- Bash shell
|
||||
|
||||
Tox handles setting up a virtual environment for python dependencies, installing dependencies, building the docker images used by tests, and finally running tests. It's an easy way to have travis-ci like build behavior locally.
|
||||
## Run tests
|
||||
|
||||
## Alternative py.test method of running tests
|
||||
From the repository root, run:
|
||||
|
||||
You're responsible for setting up your virtual env and dependencies in this situation.
|
||||
|
||||
```
|
||||
py.test -vv -n auto -m "build_stage"
|
||||
py.test -vv -n auto -m "not build_stage"
|
||||
```bash
|
||||
bash test/run.sh --distro debian_12
|
||||
```
|
||||
|
||||
The build_stage tests have to run first to create the docker images, followed by the actual tests which utilize said images. Unless you're changing your dockerfiles you shouldn't have to run the build_stage every time - but it's a good idea to rebuild at least once a day in case the base Docker images or packages change.
|
||||
`test/run.sh` will:
|
||||
|
||||
# How do I debug python?
|
||||
- Build the distro test image from `test/_<distro>.Dockerfile`
|
||||
- Run the mock/function BATS suite in a fresh container
|
||||
- Run the fresh-install BATS suite in a separate fresh container
|
||||
|
||||
Highly recommended: Setup PyCharm on a **Docker enabled** machine. Having a python debugger like PyCharm changes your life if you've never used it :)
|
||||
## Available distros
|
||||
|
||||
If you are unsure which distro names are valid, run:
|
||||
|
||||
```bash
|
||||
bash test/run.sh --help
|
||||
```
|
||||
|
||||
The help output includes the current list of supported distros.
|
||||
|
||||
## Optional: override BATS library versions
|
||||
|
||||
`test/run.sh` accepts optional environment variable overrides when building test images:
|
||||
|
||||
- `BATS_CORE_VER`
|
||||
- `BATS_SUPPORT_VER`
|
||||
- `BATS_ASSERT_VER`
|
||||
- `BATS_MOCK_VER`
|
||||
- `BATS_FILE_VER`
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
BATS_CORE_VER=v1.14.0 DISTRO=debian_12 bash test/run.sh
|
||||
```
|
||||
|
||||
@@ -3,7 +3,7 @@ FROM alpine:3.21
|
||||
ENV GITDIR=/etc/.pihole
|
||||
ENV SCRIPTDIR=/opt/pihole
|
||||
RUN sed -i 's/#\(.*\/community\)/\1/' /etc/apk/repositories
|
||||
RUN apk --no-cache add bash coreutils curl git jq openrc shadow
|
||||
RUN apk --no-cache add bash coreutils curl git jq ncurses openrc shadow
|
||||
|
||||
RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole
|
||||
ADD . $GITDIR
|
||||
@@ -13,6 +13,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
|
||||
RUN true && \
|
||||
chmod +x $SCRIPTDIR/*
|
||||
|
||||
ARG BATS_CORE_VER
|
||||
ARG BATS_SUPPORT_VER
|
||||
ARG BATS_ASSERT_VER
|
||||
ARG BATS_MOCK_VER
|
||||
ARG BATS_FILE_VER
|
||||
RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file
|
||||
|
||||
ENV SKIP_INSTALL=true
|
||||
|
||||
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
|
||||
|
||||
@@ -3,7 +3,7 @@ FROM alpine:3.22
|
||||
ENV GITDIR=/etc/.pihole
|
||||
ENV SCRIPTDIR=/opt/pihole
|
||||
RUN sed -i 's/#\(.*\/community\)/\1/' /etc/apk/repositories
|
||||
RUN apk --no-cache add bash coreutils curl git jq openrc shadow
|
||||
RUN apk --no-cache add bash coreutils curl git jq ncurses openrc shadow
|
||||
|
||||
RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole
|
||||
ADD . $GITDIR
|
||||
@@ -13,6 +13,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
|
||||
RUN true && \
|
||||
chmod +x $SCRIPTDIR/*
|
||||
|
||||
ARG BATS_CORE_VER
|
||||
ARG BATS_SUPPORT_VER
|
||||
ARG BATS_ASSERT_VER
|
||||
ARG BATS_MOCK_VER
|
||||
ARG BATS_FILE_VER
|
||||
RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file
|
||||
|
||||
ENV SKIP_INSTALL=true
|
||||
|
||||
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
|
||||
|
||||
@@ -3,7 +3,7 @@ FROM alpine:3.23
|
||||
ENV GITDIR=/etc/.pihole
|
||||
ENV SCRIPTDIR=/opt/pihole
|
||||
RUN sed -i 's/#\(.*\/community\)/\1/' /etc/apk/repositories
|
||||
RUN apk --no-cache add bash coreutils curl git jq openrc shadow
|
||||
RUN apk --no-cache add bash coreutils curl git jq ncurses openrc shadow
|
||||
|
||||
RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole
|
||||
ADD . $GITDIR
|
||||
@@ -13,6 +13,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
|
||||
RUN true && \
|
||||
chmod +x $SCRIPTDIR/*
|
||||
|
||||
ARG BATS_CORE_VER
|
||||
ARG BATS_SUPPORT_VER
|
||||
ARG BATS_ASSERT_VER
|
||||
ARG BATS_MOCK_VER
|
||||
ARG BATS_FILE_VER
|
||||
RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file
|
||||
|
||||
ENV SKIP_INSTALL=true
|
||||
|
||||
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
|
||||
|
||||
@@ -14,6 +14,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
|
||||
RUN true && \
|
||||
chmod +x $SCRIPTDIR/*
|
||||
|
||||
ARG BATS_CORE_VER
|
||||
ARG BATS_SUPPORT_VER
|
||||
ARG BATS_ASSERT_VER
|
||||
ARG BATS_MOCK_VER
|
||||
ARG BATS_FILE_VER
|
||||
RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file
|
||||
|
||||
ENV SKIP_INSTALL=true
|
||||
|
||||
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
|
||||
|
||||
@@ -14,6 +14,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
|
||||
RUN true && \
|
||||
chmod +x $SCRIPTDIR/*
|
||||
|
||||
ARG BATS_CORE_VER
|
||||
ARG BATS_SUPPORT_VER
|
||||
ARG BATS_ASSERT_VER
|
||||
ARG BATS_MOCK_VER
|
||||
ARG BATS_FILE_VER
|
||||
RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file
|
||||
|
||||
ENV SKIP_INSTALL=true
|
||||
|
||||
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
|
||||
|
||||
@@ -11,6 +11,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
|
||||
RUN true && \
|
||||
chmod +x $SCRIPTDIR/*
|
||||
|
||||
ARG BATS_CORE_VER
|
||||
ARG BATS_SUPPORT_VER
|
||||
ARG BATS_ASSERT_VER
|
||||
ARG BATS_MOCK_VER
|
||||
ARG BATS_FILE_VER
|
||||
RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file
|
||||
|
||||
ENV SKIP_INSTALL=true
|
||||
|
||||
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
|
||||
|
||||
@@ -11,6 +11,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
|
||||
RUN true && \
|
||||
chmod +x $SCRIPTDIR/*
|
||||
|
||||
ARG BATS_CORE_VER
|
||||
ARG BATS_SUPPORT_VER
|
||||
ARG BATS_ASSERT_VER
|
||||
ARG BATS_MOCK_VER
|
||||
ARG BATS_FILE_VER
|
||||
RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file
|
||||
|
||||
ENV SKIP_INSTALL=true
|
||||
|
||||
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
|
||||
|
||||
@@ -11,6 +11,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
|
||||
RUN true && \
|
||||
chmod +x $SCRIPTDIR/*
|
||||
|
||||
ARG BATS_CORE_VER
|
||||
ARG BATS_SUPPORT_VER
|
||||
ARG BATS_ASSERT_VER
|
||||
ARG BATS_MOCK_VER
|
||||
ARG BATS_FILE_VER
|
||||
RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file
|
||||
|
||||
ENV SKIP_INSTALL=true
|
||||
|
||||
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
|
||||
|
||||
@@ -12,6 +12,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
|
||||
RUN true && \
|
||||
chmod +x $SCRIPTDIR/*
|
||||
|
||||
ARG BATS_CORE_VER
|
||||
ARG BATS_SUPPORT_VER
|
||||
ARG BATS_ASSERT_VER
|
||||
ARG BATS_MOCK_VER
|
||||
ARG BATS_FILE_VER
|
||||
RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file
|
||||
|
||||
ENV SKIP_INSTALL=true
|
||||
|
||||
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
|
||||
|
||||
@@ -12,6 +12,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
|
||||
RUN true && \
|
||||
chmod +x $SCRIPTDIR/*
|
||||
|
||||
ARG BATS_CORE_VER
|
||||
ARG BATS_SUPPORT_VER
|
||||
ARG BATS_ASSERT_VER
|
||||
ARG BATS_MOCK_VER
|
||||
ARG BATS_FILE_VER
|
||||
RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file
|
||||
|
||||
ENV SKIP_INSTALL=true
|
||||
|
||||
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
|
||||
|
||||
@@ -12,6 +12,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
|
||||
RUN true && \
|
||||
chmod +x $SCRIPTDIR/*
|
||||
|
||||
ARG BATS_CORE_VER
|
||||
ARG BATS_SUPPORT_VER
|
||||
ARG BATS_ASSERT_VER
|
||||
ARG BATS_MOCK_VER
|
||||
ARG BATS_FILE_VER
|
||||
RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file
|
||||
|
||||
ENV SKIP_INSTALL=true
|
||||
|
||||
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
|
||||
|
||||
@@ -12,6 +12,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
|
||||
RUN true && \
|
||||
chmod +x $SCRIPTDIR/*
|
||||
|
||||
ARG BATS_CORE_VER
|
||||
ARG BATS_SUPPORT_VER
|
||||
ARG BATS_ASSERT_VER
|
||||
ARG BATS_MOCK_VER
|
||||
ARG BATS_FILE_VER
|
||||
RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file
|
||||
|
||||
ENV SKIP_INSTALL=true
|
||||
|
||||
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
|
||||
|
||||
@@ -11,6 +11,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
|
||||
RUN true && \
|
||||
chmod +x $SCRIPTDIR/*
|
||||
|
||||
ARG BATS_CORE_VER
|
||||
ARG BATS_SUPPORT_VER
|
||||
ARG BATS_ASSERT_VER
|
||||
ARG BATS_MOCK_VER
|
||||
ARG BATS_FILE_VER
|
||||
RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file
|
||||
|
||||
ENV SKIP_INSTALL=true
|
||||
|
||||
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
|
||||
|
||||
@@ -12,6 +12,16 @@ ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN true && \
|
||||
chmod +x $SCRIPTDIR/*
|
||||
|
||||
ARG BATS_CORE_VER
|
||||
ARG BATS_SUPPORT_VER
|
||||
ARG BATS_ASSERT_VER
|
||||
ARG BATS_MOCK_VER
|
||||
ARG BATS_FILE_VER
|
||||
RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file
|
||||
|
||||
ENV SKIP_INSTALL=true
|
||||
|
||||
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
|
||||
|
||||
@@ -12,6 +12,16 @@ ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN true && \
|
||||
chmod +x $SCRIPTDIR/*
|
||||
|
||||
ARG BATS_CORE_VER
|
||||
ARG BATS_SUPPORT_VER
|
||||
ARG BATS_ASSERT_VER
|
||||
ARG BATS_MOCK_VER
|
||||
ARG BATS_FILE_VER
|
||||
RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \
|
||||
git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file
|
||||
|
||||
ENV SKIP_INSTALL=true
|
||||
|
||||
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
import pytest
|
||||
import testinfra
|
||||
import testinfra.backend.docker
|
||||
import subprocess
|
||||
from textwrap import dedent
|
||||
|
||||
IMAGE = "pytest_pihole:test_container"
|
||||
tick_box = "[✓]"
|
||||
cross_box = "[✗]"
|
||||
info_box = "[i]"
|
||||
|
||||
|
||||
# Monkeypatch sh to bash, if they ever support non hard code /bin/sh this can go away
|
||||
# https://github.com/pytest-dev/pytest-testinfra/blob/master/testinfra/backend/docker.py
|
||||
def run_bash(self, command, *args, **kwargs):
|
||||
cmd = self.get_command(command, *args)
|
||||
if self.user is not None:
|
||||
out = self.run_local(
|
||||
"docker exec -u %s %s /bin/bash -c %s", self.user, self.name, cmd
|
||||
)
|
||||
else:
|
||||
out = self.run_local("docker exec %s /bin/bash -c %s", self.name, cmd)
|
||||
out.command = self.encode(cmd)
|
||||
return out
|
||||
|
||||
|
||||
testinfra.backend.docker.DockerBackend.run = run_bash
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def host():
|
||||
# run a container
|
||||
docker_id = (
|
||||
subprocess.check_output(["docker", "run", "-t", "-d", "--cap-add=ALL", IMAGE])
|
||||
.decode()
|
||||
.strip()
|
||||
)
|
||||
|
||||
# return a testinfra connection to the container
|
||||
docker_host = testinfra.get_host("docker://" + docker_id)
|
||||
|
||||
yield docker_host
|
||||
# at the end of the test suite, destroy the container
|
||||
subprocess.check_call(["docker", "rm", "-f", docker_id])
|
||||
|
||||
|
||||
# Helper functions
|
||||
def mock_command(script, args, container):
|
||||
"""
|
||||
Allows for setup of commands we don't really want to have to run for real
|
||||
in unit tests
|
||||
"""
|
||||
full_script_path = "/usr/local/bin/{}".format(script)
|
||||
mock_script = dedent(r"""\
|
||||
#!/bin/bash -e
|
||||
echo "\$0 \$@" >> /var/log/{script}
|
||||
case "\$1" in""".format(script=script))
|
||||
for k, v in args.items():
|
||||
case = dedent("""
|
||||
{arg})
|
||||
echo {res}
|
||||
exit {retcode}
|
||||
;;""".format(arg=k, res=v[0], retcode=v[1]))
|
||||
mock_script += case
|
||||
mock_script += dedent("""
|
||||
esac""")
|
||||
container.run(
|
||||
"""
|
||||
cat <<EOF> {script}\n{content}\nEOF
|
||||
chmod +x {script}
|
||||
rm -f /var/log/{scriptlog}""".format(
|
||||
script=full_script_path, content=mock_script, scriptlog=script
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def mock_command_passthrough(script, args, container):
|
||||
"""
|
||||
Per other mock_command* functions, allows intercepting of commands we don't want to run for real
|
||||
in unit tests, however also allows only specific arguments to be mocked. Anything not defined will
|
||||
be passed through to the actual command.
|
||||
|
||||
Example use-case: mocking `git pull` but still allowing `git clone` to work as intended
|
||||
"""
|
||||
orig_script_path = container.check_output("command -v {}".format(script))
|
||||
full_script_path = "/usr/local/bin/{}".format(script)
|
||||
mock_script = dedent(r"""\
|
||||
#!/bin/bash -e
|
||||
echo "\$0 \$@" >> /var/log/{script}
|
||||
case "\$1" in""".format(script=script))
|
||||
for k, v in args.items():
|
||||
case = dedent("""
|
||||
{arg})
|
||||
echo {res}
|
||||
exit {retcode}
|
||||
;;""".format(arg=k, res=v[0], retcode=v[1]))
|
||||
mock_script += case
|
||||
mock_script += dedent(r"""
|
||||
*)
|
||||
{orig_script_path} "\$@"
|
||||
;;""".format(orig_script_path=orig_script_path))
|
||||
mock_script += dedent("""
|
||||
esac""")
|
||||
container.run(
|
||||
"""
|
||||
cat <<EOF> {script}\n{content}\nEOF
|
||||
chmod +x {script}
|
||||
rm -f /var/log/{scriptlog}""".format(
|
||||
script=full_script_path, content=mock_script, scriptlog=script
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def mock_command_run(script, args, container):
|
||||
"""
|
||||
Allows for setup of commands we don't really want to have to run for real
|
||||
in unit tests
|
||||
"""
|
||||
full_script_path = "/usr/local/bin/{}".format(script)
|
||||
mock_script = dedent(r"""\
|
||||
#!/bin/bash -e
|
||||
echo "\$0 \$@" >> /var/log/{script}
|
||||
case "\$1 \$2" in""".format(script=script))
|
||||
for k, v in args.items():
|
||||
case = dedent("""
|
||||
\"{arg}\")
|
||||
echo {res}
|
||||
exit {retcode}
|
||||
;;""".format(arg=k, res=v[0], retcode=v[1]))
|
||||
mock_script += case
|
||||
mock_script += dedent(r"""
|
||||
esac""")
|
||||
container.run(
|
||||
"""
|
||||
cat <<EOF> {script}\n{content}\nEOF
|
||||
chmod +x {script}
|
||||
rm -f /var/log/{scriptlog}""".format(
|
||||
script=full_script_path, content=mock_script, scriptlog=script
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def mock_command_2(script, args, container):
|
||||
"""
|
||||
Allows for setup of commands we don't really want to have to run for real
|
||||
in unit tests
|
||||
"""
|
||||
full_script_path = "/usr/local/bin/{}".format(script)
|
||||
mock_script = dedent(r"""\
|
||||
#!/bin/bash -e
|
||||
echo "\$0 \$@" >> /var/log/{script}
|
||||
case "\$1 \$2" in""".format(script=script))
|
||||
for k, v in args.items():
|
||||
case = dedent("""
|
||||
\"{arg}\")
|
||||
echo \"{res}\"
|
||||
exit {retcode}
|
||||
;;""".format(arg=k, res=v[0], retcode=v[1]))
|
||||
mock_script += case
|
||||
mock_script += dedent(r"""
|
||||
esac""")
|
||||
container.run(
|
||||
"""
|
||||
cat <<EOF> {script}\n{content}\nEOF
|
||||
chmod +x {script}
|
||||
rm -f /var/log/{scriptlog}""".format(
|
||||
script=full_script_path, content=mock_script, scriptlog=script
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def run_script(Pihole, script):
|
||||
result = Pihole.run(script)
|
||||
assert result.rc == 0
|
||||
return result
|
||||
@@ -1,6 +0,0 @@
|
||||
pyyaml == 6.0.3
|
||||
pytest == 9.0.3
|
||||
pytest-xdist == 3.8.0
|
||||
pytest-testinfra == 10.2.2
|
||||
tox == 4.52.1
|
||||
pytest-clarity == 1.0.1
|
||||
Executable
+162
@@ -0,0 +1,162 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "${SCRIPT_DIR}"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# CLI arguments (optional)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
usage() {
|
||||
echo "Usage:"
|
||||
echo " DISTRO=<name> bash test/run.sh"
|
||||
echo " bash test/run.sh --distro <name>"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -d, --distro <name> Distro to test (e.g., debian_12)"
|
||||
echo " -h, --help Show this help"
|
||||
}
|
||||
|
||||
list_distros() {
|
||||
find . -maxdepth 1 -name '_*.Dockerfile' | sed 's|^\./||;s/^_//;s/\.Dockerfile$//' | sort
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-d|--distro)
|
||||
shift
|
||||
if [[ $# -eq 0 ]]; then
|
||||
echo "Error: --distro requires a value"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
DISTRO="$1"
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
echo ""
|
||||
echo "Available distros:"
|
||||
list_distros
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Error: Unknown option '$1'"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Distro selection
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
if [[ -z "${DISTRO:-}" ]]; then
|
||||
echo "Error: DISTRO is required."
|
||||
echo "Example: DISTRO=debian_12 bash test/run.sh"
|
||||
echo "or: bash test/run.sh --distro debian_12"
|
||||
echo ""
|
||||
echo "Available distros:"
|
||||
list_distros
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DOCKERFILE="_${DISTRO}.Dockerfile"
|
||||
if [[ ! -f "${DOCKERFILE}" ]]; then
|
||||
echo "Error: Unknown distro '${DISTRO}'. Available distros:"
|
||||
list_distros | sed 's/^/ /'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Determine distro family to select which test files to run.
|
||||
# rhel: CentOS/Fedora — includes SELinux tests
|
||||
# alpine: Alpine Linux
|
||||
# debian: Debian/Ubuntu (default)
|
||||
distro_family() {
|
||||
case "$1" in
|
||||
centos_* | fedora_*) echo "rhel" ;;
|
||||
alpine_*) echo "alpine" ;;
|
||||
*) echo "debian" ;;
|
||||
esac
|
||||
}
|
||||
DISTRO_FAMILY=$(distro_family "${DISTRO}")
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Suite definitions
|
||||
#
|
||||
# Suite 1 — mock/function tests: run together in one container. Each test
|
||||
# cleans up its own state via setup()/teardown(); no full install occurs.
|
||||
#
|
||||
# Suite 2 — fresh install: runs alone in its own container so the installer
|
||||
# can mutate the filesystem freely without needing any teardown.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
SUITE_1=(
|
||||
test_automated_install.bats
|
||||
test_installer_ftl.bats
|
||||
test_network.bats
|
||||
test_utils.bats
|
||||
)
|
||||
[[ "${DISTRO_FAMILY}" == "rhel" ]] && SUITE_1+=(test_selinux.bats)
|
||||
|
||||
SUITE_2=(test_fresh_install.bats)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# BATS library versions — single source of truth, passed to Docker as build
|
||||
# args so the Dockerfiles themselves stay version-agnostic. Override any of
|
||||
# these by setting the corresponding environment variable before calling this
|
||||
# script, e.g. BATS_CORE_VER=v1.14.0 bash test/run.sh --distro debian_12
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
BATS_CORE_VER="${BATS_CORE_VER:-v1.13.0}"
|
||||
BATS_SUPPORT_VER="${BATS_SUPPORT_VER:-v0.3.0}"
|
||||
BATS_ASSERT_VER="${BATS_ASSERT_VER:-v2.2.4}"
|
||||
BATS_MOCK_VER="${BATS_MOCK_VER:-v1.2.5}"
|
||||
BATS_FILE_VER="${BATS_FILE_VER:-v0.4.0}"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Build the test image (once, shared by both suites)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
IMAGE_TAG="pihole_test:${DISTRO}"
|
||||
|
||||
docker buildx build \
|
||||
--load \
|
||||
--progress plain \
|
||||
--build-arg "BATS_CORE_VER=${BATS_CORE_VER}" \
|
||||
--build-arg "BATS_SUPPORT_VER=${BATS_SUPPORT_VER}" \
|
||||
--build-arg "BATS_ASSERT_VER=${BATS_ASSERT_VER}" \
|
||||
--build-arg "BATS_MOCK_VER=${BATS_MOCK_VER}" \
|
||||
--build-arg "BATS_FILE_VER=${BATS_FILE_VER}" \
|
||||
-f "${DOCKERFILE}" \
|
||||
-t "${IMAGE_TAG}" \
|
||||
../
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# run_suite <label> <file>...
|
||||
# Spin up a fresh container and run the named BATS files inside it.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
run_suite() {
|
||||
local label="$1"; shift
|
||||
local files=("$@")
|
||||
|
||||
printf '\n=== Suite: %s ===\n' "${label}"
|
||||
|
||||
docker run --rm -t "${IMAGE_TAG}" \
|
||||
bash -euo pipefail -c '
|
||||
cd /etc/.pihole/test
|
||||
exec libs/bats/bin/bats -p --print-output-on-failure "$@"
|
||||
' bash "${files[@]}"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Run both suites; collect exit codes so both always run even if one fails.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
rc=0
|
||||
run_suite "mock and function tests" "${SUITE_1[@]}" || rc=$?
|
||||
run_suite "fresh install" "${SUITE_2[@]}" || rc=$?
|
||||
exit ${rc}
|
||||
@@ -1,7 +0,0 @@
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
py_modules=[],
|
||||
setup_requires=["pytest-runner"],
|
||||
tests_require=["pytest"],
|
||||
)
|
||||
@@ -1,472 +0,0 @@
|
||||
import pytest
|
||||
from textwrap import dedent
|
||||
import re
|
||||
from .conftest import (
|
||||
tick_box,
|
||||
info_box,
|
||||
cross_box,
|
||||
mock_command,
|
||||
mock_command_2,
|
||||
mock_command_passthrough,
|
||||
)
|
||||
|
||||
FTL_BRANCH = "development"
|
||||
|
||||
|
||||
def test_supported_package_manager(host):
|
||||
"""
|
||||
confirm installer exits when no supported package manager found
|
||||
"""
|
||||
# break supported package managers
|
||||
host.run("rm -rf /usr/bin/apt-get")
|
||||
host.run("rm -rf /usr/bin/rpm")
|
||||
host.run("rm -rf /sbin/apk")
|
||||
package_manager_detect = host.run("""
|
||||
source /opt/pihole/basic-install.sh
|
||||
package_manager_detect
|
||||
""")
|
||||
expected_stdout = cross_box + " No supported package manager found"
|
||||
assert expected_stdout in package_manager_detect.stdout
|
||||
# assert package_manager_detect.rc == 1
|
||||
|
||||
|
||||
def test_selinux_not_detected(host):
|
||||
"""
|
||||
confirms installer continues when SELinux configuration file does not exist
|
||||
"""
|
||||
check_selinux = host.run("""
|
||||
rm -f /etc/selinux/config
|
||||
source /opt/pihole/basic-install.sh
|
||||
checkSelinux
|
||||
""")
|
||||
expected_stdout = info_box + " SELinux not detected"
|
||||
assert expected_stdout in check_selinux.stdout
|
||||
assert check_selinux.rc == 0
|
||||
|
||||
|
||||
def get_directories_recursive(host, directory):
|
||||
if directory is None:
|
||||
return directory
|
||||
# returns all non-hidden subdirs of 'directory'
|
||||
dirs_raw = host.run("find {} -type d -not -path '*/.*'".format(directory))
|
||||
dirs = list(filter(bool, dirs_raw.stdout.splitlines()))
|
||||
return dirs
|
||||
|
||||
|
||||
def test_installPihole_fresh_install_readableFiles(host):
|
||||
"""
|
||||
confirms all necessary files are readable by pihole user
|
||||
"""
|
||||
# dialog returns Cancel for user prompt
|
||||
mock_command("dialog", {"*": ("", "0")}, host)
|
||||
# mock git pull
|
||||
mock_command_passthrough("git", {"pull": ("", "0")}, host)
|
||||
# mock systemctl to not start FTL
|
||||
mock_command_2(
|
||||
"systemctl",
|
||||
{
|
||||
"enable pihole-FTL": ("", "0"),
|
||||
"restart pihole-FTL": ("", "0"),
|
||||
"start pihole-FTL": ("", "0"),
|
||||
"*": ('echo "systemctl call with $@"', "0"),
|
||||
},
|
||||
host,
|
||||
)
|
||||
mock_command_2(
|
||||
"rc-service",
|
||||
{
|
||||
"rc-service pihole-FTL enable": ("", "0"),
|
||||
"rc-service pihole-FTL restart": ("", "0"),
|
||||
"rc-service pihole-FTL start": ("", "0"),
|
||||
"*": ('echo "rc-service call with $@"', "0"),
|
||||
},
|
||||
host,
|
||||
)
|
||||
# try to install man
|
||||
host.run("command -v apt-get > /dev/null && apt-get install -qq man")
|
||||
host.run("command -v dnf > /dev/null && dnf install -y man")
|
||||
host.run("command -v yum > /dev/null && yum install -y man")
|
||||
host.run("command -v apk > /dev/null && apk add mandoc man-pages")
|
||||
# Workaround to get FTLv6 installed until it reaches master branch
|
||||
host.run('echo "' + FTL_BRANCH + '" > /etc/pihole/ftlbranch')
|
||||
install = host.run("""
|
||||
export TERM=xterm
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
umask 0027
|
||||
runUnattended=true
|
||||
source /opt/pihole/basic-install.sh > /dev/null
|
||||
runUnattended=true
|
||||
main
|
||||
/opt/pihole/pihole-FTL-prestart.sh
|
||||
""")
|
||||
assert 0 == install.rc
|
||||
maninstalled = True
|
||||
if (info_box + " man not installed") in install.stdout:
|
||||
maninstalled = False
|
||||
if (info_box + " man pages not installed") in install.stdout:
|
||||
maninstalled = False
|
||||
piholeuser = "pihole"
|
||||
exit_status_success = 0
|
||||
test_cmd = 'su -s /bin/bash -c "test -{0} {1}" -p {2}'
|
||||
# check files in /etc/pihole for read, write and execute permission
|
||||
check_etc = test_cmd.format("r", "/etc/pihole", piholeuser)
|
||||
actual_rc = host.run(check_etc).rc
|
||||
assert exit_status_success == actual_rc
|
||||
check_etc = test_cmd.format("x", "/etc/pihole", piholeuser)
|
||||
actual_rc = host.run(check_etc).rc
|
||||
assert exit_status_success == actual_rc
|
||||
# readable and writable dhcp.leases
|
||||
check_leases = test_cmd.format("r", "/etc/pihole/dhcp.leases", piholeuser)
|
||||
actual_rc = host.run(check_leases).rc
|
||||
assert exit_status_success == actual_rc
|
||||
check_leases = test_cmd.format("w", "/etc/pihole/dhcp.leases", piholeuser)
|
||||
actual_rc = host.run(check_leases).rc
|
||||
# readable install.log
|
||||
check_install = test_cmd.format("r", "/etc/pihole/install.log", piholeuser)
|
||||
actual_rc = host.run(check_install).rc
|
||||
assert exit_status_success == actual_rc
|
||||
# readable versions
|
||||
check_localversion = test_cmd.format("r", "/etc/pihole/versions", piholeuser)
|
||||
actual_rc = host.run(check_localversion).rc
|
||||
assert exit_status_success == actual_rc
|
||||
# readable macvendor.db
|
||||
check_macvendor = test_cmd.format("r", "/etc/pihole/macvendor.db", piholeuser)
|
||||
actual_rc = host.run(check_macvendor).rc
|
||||
assert exit_status_success == actual_rc
|
||||
# check readable and executable /etc/init.d/pihole-FTL
|
||||
check_init = test_cmd.format("x", "/etc/init.d/pihole-FTL", piholeuser)
|
||||
actual_rc = host.run(check_init).rc
|
||||
assert exit_status_success == actual_rc
|
||||
check_init = test_cmd.format("r", "/etc/init.d/pihole-FTL", piholeuser)
|
||||
actual_rc = host.run(check_init).rc
|
||||
assert exit_status_success == actual_rc
|
||||
# check readable and executable manpages
|
||||
if maninstalled is True:
|
||||
check_man = test_cmd.format("x", "/usr/local/share/man", piholeuser)
|
||||
actual_rc = host.run(check_man).rc
|
||||
assert exit_status_success == actual_rc
|
||||
check_man = test_cmd.format("r", "/usr/local/share/man", piholeuser)
|
||||
actual_rc = host.run(check_man).rc
|
||||
assert exit_status_success == actual_rc
|
||||
check_man = test_cmd.format("x", "/usr/local/share/man/man8", piholeuser)
|
||||
actual_rc = host.run(check_man).rc
|
||||
assert exit_status_success == actual_rc
|
||||
check_man = test_cmd.format("r", "/usr/local/share/man/man8", piholeuser)
|
||||
actual_rc = host.run(check_man).rc
|
||||
assert exit_status_success == actual_rc
|
||||
check_man = test_cmd.format(
|
||||
"r", "/usr/local/share/man/man8/pihole.8", piholeuser
|
||||
)
|
||||
actual_rc = host.run(check_man).rc
|
||||
assert exit_status_success == actual_rc
|
||||
# check not readable cron file
|
||||
check_sudo = test_cmd.format("x", "/etc/cron.d/", piholeuser)
|
||||
actual_rc = host.run(check_sudo).rc
|
||||
assert exit_status_success == actual_rc
|
||||
check_sudo = test_cmd.format("r", "/etc/cron.d/", piholeuser)
|
||||
actual_rc = host.run(check_sudo).rc
|
||||
assert exit_status_success == actual_rc
|
||||
check_sudo = test_cmd.format("r", "/etc/cron.d/pihole", piholeuser)
|
||||
actual_rc = host.run(check_sudo).rc
|
||||
assert exit_status_success == actual_rc
|
||||
directories = get_directories_recursive(host, "/etc/.pihole/")
|
||||
for directory in directories:
|
||||
check_pihole = test_cmd.format("r", directory, piholeuser)
|
||||
actual_rc = host.run(check_pihole).rc
|
||||
check_pihole = test_cmd.format("x", directory, piholeuser)
|
||||
actual_rc = host.run(check_pihole).rc
|
||||
findfiles = 'find "{}" -maxdepth 1 -type f -exec echo {{}} \\;;'
|
||||
filelist = host.run(findfiles.format(directory))
|
||||
files = list(filter(bool, filelist.stdout.splitlines()))
|
||||
for file in files:
|
||||
check_pihole = test_cmd.format("r", file, piholeuser)
|
||||
actual_rc = host.run(check_pihole).rc
|
||||
|
||||
|
||||
def test_update_package_cache_success_no_errors(host):
|
||||
"""
|
||||
confirms package cache was updated without any errors
|
||||
"""
|
||||
updateCache = host.run("""
|
||||
source /opt/pihole/basic-install.sh
|
||||
package_manager_detect
|
||||
update_package_cache
|
||||
""")
|
||||
expected_stdout = tick_box + " Update local cache of available packages"
|
||||
assert expected_stdout in updateCache.stdout
|
||||
assert "error" not in updateCache.stdout.lower()
|
||||
|
||||
|
||||
def test_update_package_cache_failure_no_errors(host):
|
||||
"""
|
||||
confirms package cache was not updated
|
||||
"""
|
||||
mock_command("apt-get", {"update": ("", "1")}, host)
|
||||
updateCache = host.run("""
|
||||
source /opt/pihole/basic-install.sh
|
||||
package_manager_detect
|
||||
update_package_cache
|
||||
""")
|
||||
expected_stdout = cross_box + " Update local cache of available packages"
|
||||
assert expected_stdout in updateCache.stdout
|
||||
assert "Error: Unable to update package cache." in updateCache.stdout
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"arch,detected_string,supported",
|
||||
[
|
||||
("aarch64", "AArch64 (64 Bit ARM)", True),
|
||||
("armv6", "ARMv6", True),
|
||||
("armv7l", "ARMv7 (or newer)", True),
|
||||
("armv7", "ARMv7 (or newer)", True),
|
||||
("armv8a", "ARMv7 (or newer)", True),
|
||||
("x86_64", "x86_64", True),
|
||||
("riscv64", "riscv64", True),
|
||||
("mips", "mips", False),
|
||||
],
|
||||
)
|
||||
def test_FTL_detect_no_errors(host, arch, detected_string, supported):
|
||||
"""
|
||||
confirms only correct package is downloaded for FTL engine
|
||||
"""
|
||||
# mock uname to return passed platform
|
||||
mock_command("uname", {"-m": (arch, "0")}, host)
|
||||
# mock readelf to respond with passed CPU architecture
|
||||
mock_command_2(
|
||||
"readelf",
|
||||
{
|
||||
"-A /bin/sh": ("Tag_CPU_arch: " + arch, "0"),
|
||||
"-A /usr/bin/sh": ("Tag_CPU_arch: " + arch, "0"),
|
||||
"-A /usr/sbin/sh": ("Tag_CPU_arch: " + arch, "0"),
|
||||
},
|
||||
host,
|
||||
)
|
||||
host.run('echo "' + FTL_BRANCH + '" > /etc/pihole/ftlbranch')
|
||||
detectPlatform = host.run("""
|
||||
source /opt/pihole/basic-install.sh
|
||||
create_pihole_user
|
||||
funcOutput=$(get_binary_name)
|
||||
binary="pihole-FTL${funcOutput##*pihole-FTL}"
|
||||
theRest="${funcOutput%pihole-FTL*}"
|
||||
FTLdetect "${binary}" "${theRest}"
|
||||
""")
|
||||
if supported:
|
||||
expected_stdout = info_box + " FTL Checks..."
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
expected_stdout = tick_box + " Detected " + detected_string + " architecture"
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
expected_stdout = tick_box + " Downloading and Installing FTL"
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
else:
|
||||
expected_stdout = (
|
||||
"Not able to detect architecture (unknown: " + detected_string + ")"
|
||||
)
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
|
||||
|
||||
def test_FTL_development_binary_installed_and_responsive_no_errors(host):
|
||||
"""
|
||||
confirms FTL development binary is copied and functional in installed location
|
||||
"""
|
||||
host.run('echo "' + FTL_BRANCH + '" > /etc/pihole/ftlbranch')
|
||||
host.run("""
|
||||
source /opt/pihole/basic-install.sh
|
||||
create_pihole_user
|
||||
funcOutput=$(get_binary_name)
|
||||
binary="pihole-FTL${funcOutput##*pihole-FTL}"
|
||||
theRest="${funcOutput%pihole-FTL*}"
|
||||
FTLdetect "${binary}" "${theRest}"
|
||||
""")
|
||||
version_check = host.run("""
|
||||
VERSION=$(pihole-FTL version)
|
||||
echo ${VERSION:0:1}
|
||||
""")
|
||||
expected_stdout = "v"
|
||||
assert expected_stdout in version_check.stdout
|
||||
|
||||
|
||||
def test_IPv6_only_link_local(host):
|
||||
"""
|
||||
confirms IPv6 blocking is disabled for Link-local address
|
||||
"""
|
||||
# mock ip -6 address to return Link-local address
|
||||
mock_command_2(
|
||||
"ip",
|
||||
{"-6 address": ("inet6 fe80::d210:52fa:fe00:7ad7/64 scope link", "0")},
|
||||
host,
|
||||
)
|
||||
detectPlatform = host.run("""
|
||||
source /opt/pihole/basic-install.sh
|
||||
find_IPv6_information
|
||||
""")
|
||||
expected_stdout = "Unable to find IPv6 ULA/GUA address"
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
|
||||
|
||||
def test_IPv6_only_ULA(host):
|
||||
"""
|
||||
confirms IPv6 blocking is enabled for ULA addresses
|
||||
"""
|
||||
# mock ip -6 address to return ULA address
|
||||
mock_command_2(
|
||||
"ip",
|
||||
{
|
||||
"-6 address": (
|
||||
"inet6 fda2:2001:5555:0:d210:52fa:fe00:7ad7/64 scope global",
|
||||
"0",
|
||||
)
|
||||
},
|
||||
host,
|
||||
)
|
||||
detectPlatform = host.run("""
|
||||
source /opt/pihole/basic-install.sh
|
||||
find_IPv6_information
|
||||
""")
|
||||
expected_stdout = "Found IPv6 ULA address"
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
|
||||
|
||||
def test_IPv6_only_GUA(host):
|
||||
"""
|
||||
confirms IPv6 blocking is enabled for GUA addresses
|
||||
"""
|
||||
# mock ip -6 address to return GUA address
|
||||
mock_command_2(
|
||||
"ip",
|
||||
{
|
||||
"-6 address": (
|
||||
"inet6 2003:12:1e43:301:d210:52fa:fe00:7ad7/64 scope global",
|
||||
"0",
|
||||
)
|
||||
},
|
||||
host,
|
||||
)
|
||||
detectPlatform = host.run("""
|
||||
source /opt/pihole/basic-install.sh
|
||||
find_IPv6_information
|
||||
""")
|
||||
expected_stdout = "Found IPv6 GUA address"
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
|
||||
|
||||
def test_IPv6_GUA_ULA_test(host):
|
||||
"""
|
||||
confirms IPv6 blocking is enabled for GUA and ULA addresses
|
||||
"""
|
||||
# mock ip -6 address to return GUA and ULA addresses
|
||||
mock_command_2(
|
||||
"ip",
|
||||
{
|
||||
"-6 address": (
|
||||
"inet6 2003:12:1e43:301:d210:52fa:fe00:7ad7/64 scope global\n"
|
||||
"inet6 fda2:2001:5555:0:d210:52fa:fe00:7ad7/64 scope global",
|
||||
"0",
|
||||
)
|
||||
},
|
||||
host,
|
||||
)
|
||||
detectPlatform = host.run("""
|
||||
source /opt/pihole/basic-install.sh
|
||||
find_IPv6_information
|
||||
""")
|
||||
expected_stdout = "Found IPv6 ULA address"
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
|
||||
|
||||
def test_IPv6_ULA_GUA_test(host):
|
||||
"""
|
||||
confirms IPv6 blocking is enabled for GUA and ULA addresses
|
||||
"""
|
||||
# mock ip -6 address to return ULA and GUA addresses
|
||||
mock_command_2(
|
||||
"ip",
|
||||
{
|
||||
"-6 address": (
|
||||
"inet6 fda2:2001:5555:0:d210:52fa:fe00:7ad7/64 scope global\n"
|
||||
"inet6 2003:12:1e43:301:d210:52fa:fe00:7ad7/64 scope global",
|
||||
"0",
|
||||
)
|
||||
},
|
||||
host,
|
||||
)
|
||||
detectPlatform = host.run("""
|
||||
source /opt/pihole/basic-install.sh
|
||||
find_IPv6_information
|
||||
""")
|
||||
expected_stdout = "Found IPv6 ULA address"
|
||||
assert expected_stdout in detectPlatform.stdout
|
||||
|
||||
|
||||
def test_validate_ip(host):
|
||||
"""
|
||||
Tests valid_ip for various IP addresses
|
||||
"""
|
||||
|
||||
def test_address(addr, success=True):
|
||||
output = host.run("""
|
||||
source /opt/pihole/basic-install.sh
|
||||
valid_ip "{addr}"
|
||||
""".format(addr=addr))
|
||||
|
||||
assert output.rc == 0 if success else 1
|
||||
|
||||
test_address("192.168.1.1")
|
||||
test_address("127.0.0.1")
|
||||
test_address("255.255.255.255")
|
||||
test_address("255.255.255.256", False)
|
||||
test_address("255.255.256.255", False)
|
||||
test_address("255.256.255.255", False)
|
||||
test_address("256.255.255.255", False)
|
||||
test_address("1092.168.1.1", False)
|
||||
test_address("not an IP", False)
|
||||
test_address("8.8.8.8#", False)
|
||||
test_address("8.8.8.8#0")
|
||||
test_address("8.8.8.8#1")
|
||||
test_address("8.8.8.8#42")
|
||||
test_address("8.8.8.8#888")
|
||||
test_address("8.8.8.8#1337")
|
||||
test_address("8.8.8.8#65535")
|
||||
test_address("8.8.8.8#65536", False)
|
||||
test_address("8.8.8.8#-1", False)
|
||||
test_address("00.0.0.0", False)
|
||||
test_address("010.0.0.0", False)
|
||||
test_address("001.0.0.0", False)
|
||||
test_address("0.0.0.0#00", False)
|
||||
test_address("0.0.0.0#01", False)
|
||||
test_address("0.0.0.0#001", False)
|
||||
test_address("0.0.0.0#0001", False)
|
||||
test_address("0.0.0.0#00001", False)
|
||||
|
||||
|
||||
def test_package_manager_has_pihole_deps(host):
|
||||
"""Confirms OS is able to install the required packages for Pi-hole"""
|
||||
mock_command("dialog", {"*": ("", "0")}, host)
|
||||
output = host.run("""
|
||||
source /opt/pihole/basic-install.sh
|
||||
package_manager_detect
|
||||
update_package_cache
|
||||
build_dependency_package
|
||||
install_dependent_packages
|
||||
""")
|
||||
|
||||
assert "No package" not in output.stdout
|
||||
assert output.rc == 0
|
||||
|
||||
|
||||
def test_meta_package_uninstall(host):
|
||||
"""Confirms OS is able to install and uninstall the Pi-hole meta package"""
|
||||
mock_command("dialog", {"*": ("", "0")}, host)
|
||||
install = host.run("""
|
||||
source /opt/pihole/basic-install.sh
|
||||
package_manager_detect
|
||||
update_package_cache
|
||||
build_dependency_package
|
||||
install_dependent_packages
|
||||
""")
|
||||
assert install.rc == 0
|
||||
|
||||
uninstall = host.run("""
|
||||
source /opt/pihole/uninstall.sh
|
||||
removeMetaPackage
|
||||
""")
|
||||
assert uninstall.rc == 0
|
||||
@@ -1,50 +0,0 @@
|
||||
def test_key_val_replacement_works(host):
|
||||
"""Confirms addOrEditKeyValPair either adds or replaces a key value pair in a given file"""
|
||||
host.run("""
|
||||
source /opt/pihole/utils.sh
|
||||
touch ./testoutput
|
||||
addOrEditKeyValPair "./testoutput" "KEY_ONE" "value1"
|
||||
addOrEditKeyValPair "./testoutput" "KEY_TWO" "value2"
|
||||
addOrEditKeyValPair "./testoutput" "KEY_ONE" "value3"
|
||||
addOrEditKeyValPair "./testoutput" "KEY_FOUR" "value4"
|
||||
""")
|
||||
output = host.run("""
|
||||
cat ./testoutput
|
||||
""")
|
||||
expected_stdout = "KEY_ONE=value3\nKEY_TWO=value2\nKEY_FOUR=value4\n"
|
||||
assert expected_stdout == output.stdout
|
||||
|
||||
|
||||
def test_getFTLPID_default(host):
|
||||
"""Confirms getFTLPID returns the default value if FTL is not running"""
|
||||
output = host.run("""
|
||||
source /opt/pihole/utils.sh
|
||||
getFTLPID
|
||||
""")
|
||||
expected_stdout = "-1\n"
|
||||
assert expected_stdout == output.stdout
|
||||
|
||||
|
||||
def test_setFTLConfigValue_getFTLConfigValue(host):
|
||||
"""
|
||||
Confirms getFTLConfigValue works (also assumes setFTLConfigValue works)
|
||||
Requires FTL to be installed, so we do that first
|
||||
(taken from test_FTL_development_binary_installed_and_responsive_no_errors)
|
||||
"""
|
||||
host.run("""
|
||||
source /opt/pihole/basic-install.sh
|
||||
create_pihole_user
|
||||
funcOutput=$(get_binary_name)
|
||||
echo "development" > /etc/pihole/ftlbranch
|
||||
binary="pihole-FTL${funcOutput##*pihole-FTL}"
|
||||
theRest="${funcOutput%pihole-FTL*}"
|
||||
FTLdetect "${binary}" "${theRest}"
|
||||
""")
|
||||
|
||||
output = host.run("""
|
||||
source /opt/pihole/utils.sh
|
||||
setFTLConfigValue "dns.upstreams" '["9.9.9.9"]' > /dev/null
|
||||
getFTLConfigValue "dns.upstreams"
|
||||
""")
|
||||
|
||||
assert "[ 9.9.9.9 ]" in output.stdout
|
||||
Executable
+95
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env bats
|
||||
# Core installer tests — package manager, cache, dependencies
|
||||
|
||||
load 'libs/bats-support/load'
|
||||
load 'libs/bats-assert/load'
|
||||
load 'libs/bats-mock/stub'
|
||||
|
||||
TICK="[✓]"
|
||||
CROSS="[✗]"
|
||||
INFO="[i]"
|
||||
|
||||
@test "installer exits when no supported package manager found" {
|
||||
[[ -e /usr/bin/apt-get ]] && mv /usr/bin/apt-get /usr/bin/apt-get.disabled
|
||||
[[ -e /usr/bin/rpm ]] && mv /usr/bin/rpm /usr/bin/rpm.disabled
|
||||
[[ -e /sbin/apk ]] && mv /sbin/apk /sbin/apk.disabled
|
||||
|
||||
run bash -c "
|
||||
source /opt/pihole/basic-install.sh
|
||||
package_manager_detect
|
||||
"
|
||||
|
||||
assert_output --partial "${CROSS} No supported package manager found"
|
||||
assert_failure
|
||||
|
||||
# Restore package managers for other tests
|
||||
[[ -e /usr/bin/apt-get.disabled ]] && mv -f /usr/bin/apt-get.disabled /usr/bin/apt-get || true
|
||||
[[ -e /usr/bin/rpm.disabled ]] && mv -f /usr/bin/rpm.disabled /usr/bin/rpm || true
|
||||
[[ -e /sbin/apk.disabled ]] && mv -f /sbin/apk.disabled /sbin/apk || true
|
||||
}
|
||||
|
||||
@test "installer continues when SELinux config file does not exist" {
|
||||
run bash -c "
|
||||
rm -f /etc/selinux/config
|
||||
source /opt/pihole/basic-install.sh
|
||||
checkSelinux
|
||||
"
|
||||
assert_output --partial "${INFO} SELinux not detected"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "package cache update succeeds without errors" {
|
||||
run bash -c "
|
||||
source /opt/pihole/basic-install.sh
|
||||
package_manager_detect
|
||||
update_package_cache
|
||||
"
|
||||
assert_output --partial "${TICK} Update local cache of available packages"
|
||||
refute_output --partial "error"
|
||||
}
|
||||
|
||||
@test "package cache update reports failure correctly" {
|
||||
stub apt-get "update : return 1"
|
||||
|
||||
run bash -c "
|
||||
source /opt/pihole/basic-install.sh
|
||||
package_manager_detect
|
||||
update_package_cache
|
||||
"
|
||||
assert_output --partial "${CROSS} Update local cache of available packages"
|
||||
assert_output --partial "Error: Unable to update package cache."
|
||||
|
||||
unstub apt-get 2>/dev/null || true
|
||||
}
|
||||
|
||||
@test "OS can install required Pi-hole dependency packages" {
|
||||
run bash -c "
|
||||
source /opt/pihole/basic-install.sh
|
||||
package_manager_detect
|
||||
update_package_cache
|
||||
build_dependency_package
|
||||
install_dependent_packages
|
||||
"
|
||||
refute_output --partial "No package"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "OS can install and uninstall the Pi-hole meta package" {
|
||||
run bash -c "
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
source /opt/pihole/basic-install.sh
|
||||
package_manager_detect
|
||||
update_package_cache
|
||||
build_dependency_package
|
||||
install_dependent_packages
|
||||
"
|
||||
assert_success
|
||||
|
||||
run bash -c "
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
source /opt/pihole/basic-install.sh
|
||||
package_manager_detect
|
||||
eval \"\${PKG_REMOVE}\" pihole-meta
|
||||
"
|
||||
assert_success
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
from .conftest import (
|
||||
tick_box,
|
||||
cross_box,
|
||||
mock_command,
|
||||
)
|
||||
|
||||
|
||||
def mock_selinux_config(state, host):
|
||||
"""
|
||||
Creates a mock SELinux config file with expected content
|
||||
"""
|
||||
# validate state string
|
||||
valid_states = ["enforcing", "permissive", "disabled"]
|
||||
assert state in valid_states
|
||||
# getenforce returns the running state of SELinux
|
||||
mock_command("getenforce", {"*": (state.capitalize(), "0")}, host)
|
||||
# create mock configuration with desired content
|
||||
host.run("""
|
||||
mkdir /etc/selinux
|
||||
echo "SELINUX={state}" > /etc/selinux/config
|
||||
""".format(state=state.lower()))
|
||||
|
||||
|
||||
def test_selinux_enforcing_exit(host):
|
||||
"""
|
||||
confirms installer prompts to exit when SELinux is Enforcing by default
|
||||
"""
|
||||
mock_selinux_config("enforcing", host)
|
||||
check_selinux = host.run("""
|
||||
source /opt/pihole/basic-install.sh
|
||||
checkSelinux
|
||||
""")
|
||||
expected_stdout = cross_box + " Current SELinux: enforcing"
|
||||
assert expected_stdout in check_selinux.stdout
|
||||
expected_stdout = "SELinux Enforcing detected, exiting installer"
|
||||
assert expected_stdout in check_selinux.stdout
|
||||
assert check_selinux.rc == 1
|
||||
|
||||
|
||||
def test_selinux_permissive(host):
|
||||
"""
|
||||
confirms installer continues when SELinux is Permissive
|
||||
"""
|
||||
mock_selinux_config("permissive", host)
|
||||
check_selinux = host.run("""
|
||||
source /opt/pihole/basic-install.sh
|
||||
checkSelinux
|
||||
""")
|
||||
expected_stdout = tick_box + " Current SELinux: permissive"
|
||||
assert expected_stdout in check_selinux.stdout
|
||||
assert check_selinux.rc == 0
|
||||
|
||||
|
||||
def test_selinux_disabled(host):
|
||||
"""
|
||||
confirms installer continues when SELinux is Disabled
|
||||
"""
|
||||
mock_selinux_config("disabled", host)
|
||||
check_selinux = host.run("""
|
||||
source /opt/pihole/basic-install.sh
|
||||
checkSelinux
|
||||
""")
|
||||
expected_stdout = tick_box + " Current SELinux: disabled"
|
||||
assert expected_stdout in check_selinux.stdout
|
||||
assert check_selinux.rc == 0
|
||||
Executable
+123
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env bats
|
||||
# Full-install test — runs in a dedicated container so no teardown is needed.
|
||||
# Verifies that all files written by the installer are readable by the pihole user.
|
||||
|
||||
load 'libs/bats-support/load'
|
||||
load 'libs/bats-assert/load'
|
||||
load 'libs/bats-file/load'
|
||||
load 'libs/bats-mock/stub'
|
||||
|
||||
INFO="[i]"
|
||||
FTL_BRANCH="development"
|
||||
|
||||
@test "fresh install: all necessary files are readable by pihole user" {
|
||||
# bats-mock prepends $BATS_MOCK_BINDIR to PATH at load time but only
|
||||
# creates the directory on the first stub call. We write scripts directly
|
||||
# so create it ourselves.
|
||||
mkdir -p "${BATS_MOCK_BINDIR}"
|
||||
|
||||
# dialog — suppress any TUI calls; with a TTY allocated an uncaught dialog
|
||||
# invocation would block the test waiting for input
|
||||
printf '#!/bin/bash\nexit 0\n' > "${BATS_MOCK_BINDIR}/dialog"
|
||||
chmod +x "${BATS_MOCK_BINDIR}/dialog"
|
||||
|
||||
# git — let every subcommand run for real except 'pull', which we suppress
|
||||
# so the test has no dependency on outbound network access
|
||||
local real_git
|
||||
real_git="$(type -P git)"
|
||||
cat > "${BATS_MOCK_BINDIR}/git" <<EOF
|
||||
#!/bin/bash
|
||||
case "\$1" in
|
||||
pull) exit 0 ;;
|
||||
*) exec "${real_git}" "\$@" ;;
|
||||
esac
|
||||
EOF
|
||||
chmod +x "${BATS_MOCK_BINDIR}/git"
|
||||
|
||||
# systemctl / rc-service — accept any service-management call silently
|
||||
printf '#!/bin/bash\nexit 0\n' > "${BATS_MOCK_BINDIR}/systemctl"
|
||||
chmod +x "${BATS_MOCK_BINDIR}/systemctl"
|
||||
printf '#!/bin/bash\nexit 0\n' > "${BATS_MOCK_BINDIR}/rc-service"
|
||||
chmod +x "${BATS_MOCK_BINDIR}/rc-service"
|
||||
|
||||
command -v apt-get > /dev/null && apt-get install -qq man || true
|
||||
command -v dnf > /dev/null && dnf install -y man || true
|
||||
command -v yum > /dev/null && yum install -y man || true
|
||||
command -v apk > /dev/null && apk add mandoc man-pages || true
|
||||
|
||||
echo "${FTL_BRANCH}" > /etc/pihole/ftlbranch
|
||||
|
||||
run bash -c "
|
||||
export TERM=xterm
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
umask 0027
|
||||
source /opt/pihole/basic-install.sh > /dev/null
|
||||
runUnattended=true
|
||||
main
|
||||
/opt/pihole/pihole-FTL-prestart.sh
|
||||
"
|
||||
assert_success
|
||||
|
||||
local maninstalled=true
|
||||
if [[ "${output}" == *"${INFO} man not installed"* ]] || [[ "${output}" == *"${INFO} man pages not installed"* ]]; then
|
||||
maninstalled=false
|
||||
fi
|
||||
|
||||
# Verify files exist before checking user-level read permission.
|
||||
assert_dir_exists /etc/pihole
|
||||
assert_file_exists /etc/pihole/dhcp.leases
|
||||
assert_file_exists /etc/pihole/install.log
|
||||
assert_file_exists /etc/pihole/versions
|
||||
assert_file_exists /etc/pihole/macvendor.db
|
||||
assert_file_exists /etc/init.d/pihole-FTL
|
||||
|
||||
if [[ "${maninstalled}" == "true" ]]; then
|
||||
assert_dir_exists /usr/local/share/man
|
||||
assert_dir_exists /usr/local/share/man/man8
|
||||
assert_file_exists /usr/local/share/man/man8/pihole.8
|
||||
fi
|
||||
|
||||
assert_file_exists /etc/cron.d/pihole
|
||||
|
||||
# Verify the pihole user can actually read the files (bats-file checks as
|
||||
# the current process user; _check_perm runs the test as the pihole user).
|
||||
local piholeuser="pihole"
|
||||
_check_perm() { su -s /bin/bash -c "test -${1} ${2}" -p ${piholeuser}; }
|
||||
|
||||
run _check_perm r /etc/pihole; assert_success
|
||||
run _check_perm x /etc/pihole; assert_success
|
||||
run _check_perm r /etc/pihole/dhcp.leases; assert_success
|
||||
run _check_perm r /etc/pihole/install.log; assert_success
|
||||
run _check_perm r /etc/pihole/versions; assert_success
|
||||
run _check_perm r /etc/pihole/macvendor.db; assert_success
|
||||
run _check_perm x /etc/init.d/pihole-FTL; assert_success
|
||||
run _check_perm r /etc/init.d/pihole-FTL; assert_success
|
||||
|
||||
if [[ "${maninstalled}" == "true" ]]; then
|
||||
run _check_perm x /usr/local/share/man; assert_success
|
||||
run _check_perm r /usr/local/share/man; assert_success
|
||||
run _check_perm x /usr/local/share/man/man8; assert_success
|
||||
run _check_perm r /usr/local/share/man/man8; assert_success
|
||||
run _check_perm r /usr/local/share/man/man8/pihole.8; assert_success
|
||||
fi
|
||||
|
||||
run _check_perm x /etc/cron.d/; assert_success
|
||||
run _check_perm r /etc/cron.d/; assert_success
|
||||
run _check_perm r /etc/cron.d/pihole; assert_success
|
||||
|
||||
local dirs
|
||||
dirs=$(find /etc/.pihole/ -type d -not -path '*/.*' 2>/dev/null || true)
|
||||
while IFS= read -r dir; do
|
||||
[[ -z "${dir}" ]] && continue
|
||||
assert_dir_exists "${dir}"
|
||||
run _check_perm r "${dir}"; assert_success
|
||||
run _check_perm x "${dir}"; assert_success
|
||||
local files
|
||||
files=$(find "${dir}" -maxdepth 1 -type f -exec echo {} \; 2>/dev/null || true)
|
||||
while IFS= read -r file; do
|
||||
[[ -z "${file}" ]] && continue
|
||||
assert_file_exists "${file}"
|
||||
run _check_perm r "${file}"; assert_success
|
||||
done <<< "${files}"
|
||||
done <<< "${dirs}"
|
||||
}
|
||||
Executable
+102
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env bats
|
||||
# Installer tests for FTL architecture detection and binary installation
|
||||
|
||||
load 'libs/bats-support/load'
|
||||
load 'libs/bats-assert/load'
|
||||
load 'libs/bats-mock/stub'
|
||||
|
||||
TICK="[✓]"
|
||||
INFO="[i]"
|
||||
|
||||
FTL_BRANCH="development"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Installer FTL architecture detection — one @test per arch
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
_test_ftl_arch() {
|
||||
local arch="$1" detected_string="$2" supported="$3"
|
||||
|
||||
# Resolve the sh binary path the installer will interrogate so we stub
|
||||
# exactly the call that will be made, rather than all possible paths.
|
||||
local sh_path
|
||||
sh_path="$(command -v sh)"
|
||||
|
||||
stub uname "-m : echo '${arch}'"
|
||||
stub readelf "-A ${sh_path} : echo 'Tag_CPU_arch: ${arch}'"
|
||||
echo "${FTL_BRANCH}" > /etc/pihole/ftlbranch
|
||||
|
||||
run bash -c "
|
||||
source /opt/pihole/basic-install.sh
|
||||
create_pihole_user
|
||||
funcOutput=\$(get_binary_name)
|
||||
binary=\"pihole-FTL\${funcOutput##*pihole-FTL}\"
|
||||
theRest=\"\${funcOutput%pihole-FTL*}\"
|
||||
FTLdetect \"\${binary}\" \"\${theRest}\"
|
||||
"
|
||||
|
||||
if [[ "${supported}" == "true" ]]; then
|
||||
assert_output --partial "${INFO} FTL Checks..."
|
||||
assert_output --partial "${TICK} Detected ${detected_string} architecture"
|
||||
|
||||
if [[ "${output}" != *"Downloading and Installing FTL"* && "${output}" != *"Local binary up-to-date. No need to download!"* ]]; then
|
||||
echo "Expected either download or up-to-date path, got:" >&2
|
||||
echo "${output}" >&2
|
||||
false
|
||||
fi
|
||||
else
|
||||
assert_output --partial "Not able to detect architecture (unknown: ${detected_string})"
|
||||
fi
|
||||
|
||||
unstub uname 2>/dev/null || true
|
||||
unstub readelf 2>/dev/null || true
|
||||
}
|
||||
|
||||
@test "installer detects aarch64 architecture for FTL" {
|
||||
_test_ftl_arch "aarch64" "AArch64 (64 Bit ARM)" "true"
|
||||
}
|
||||
|
||||
@test "installer detects ARMv6 architecture for FTL" {
|
||||
_test_ftl_arch "armv6" "ARMv6" "true"
|
||||
}
|
||||
|
||||
@test "installer detects ARMv7l architecture for FTL" {
|
||||
_test_ftl_arch "armv7l" "ARMv7 (or newer)" "true"
|
||||
}
|
||||
|
||||
@test "installer detects ARMv7 architecture for FTL" {
|
||||
_test_ftl_arch "armv7" "ARMv7 (or newer)" "true"
|
||||
}
|
||||
|
||||
@test "installer detects ARMv8a architecture for FTL" {
|
||||
_test_ftl_arch "armv8a" "ARMv7 (or newer)" "true"
|
||||
}
|
||||
|
||||
@test "installer detects x86_64 architecture for FTL" {
|
||||
_test_ftl_arch "x86_64" "x86_64" "true"
|
||||
}
|
||||
|
||||
@test "installer detects riscv64 architecture for FTL" {
|
||||
_test_ftl_arch "riscv64" "riscv64" "true"
|
||||
}
|
||||
|
||||
@test "installer reports unsupported architecture for FTL" {
|
||||
_test_ftl_arch "mips" "mips" "false"
|
||||
}
|
||||
|
||||
@test "installer provides a responsive FTL development binary" {
|
||||
echo "${FTL_BRANCH}" > /etc/pihole/ftlbranch
|
||||
bash -c "
|
||||
source /opt/pihole/basic-install.sh
|
||||
create_pihole_user
|
||||
funcOutput=\$(get_binary_name)
|
||||
binary=\"pihole-FTL\${funcOutput##*pihole-FTL}\"
|
||||
theRest=\"\${funcOutput%pihole-FTL*}\"
|
||||
FTLdetect \"\${binary}\" \"\${theRest}\"
|
||||
"
|
||||
run bash -c '
|
||||
VERSION=$(pihole-FTL version)
|
||||
echo "${VERSION:0:1}"
|
||||
'
|
||||
assert_output --partial "v"
|
||||
}
|
||||
Executable
+107
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env bats
|
||||
# Network detection tests — IPv6 address detection and IP validation
|
||||
|
||||
load 'libs/bats-support/load'
|
||||
load 'libs/bats-assert/load'
|
||||
load 'libs/bats-mock/stub'
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# IPv6 detection
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "IPv6 link-local only: blocking disabled" {
|
||||
stub ip "-6 address : echo 'inet6 fe80::d210:52fa:fe00:7ad7/64 scope link'"
|
||||
run bash -c "
|
||||
source /opt/pihole/basic-install.sh
|
||||
find_IPv6_information
|
||||
"
|
||||
assert_output --partial "Unable to find IPv6 ULA/GUA address"
|
||||
|
||||
unstub ip 2>/dev/null || true
|
||||
}
|
||||
|
||||
@test "IPv6 ULA only: blocking enabled" {
|
||||
stub ip "-6 address : echo 'inet6 fda2:2001:5555:0:d210:52fa:fe00:7ad7/64 scope global'"
|
||||
run bash -c "
|
||||
source /opt/pihole/basic-install.sh
|
||||
find_IPv6_information
|
||||
"
|
||||
assert_output --partial "Found IPv6 ULA address"
|
||||
|
||||
unstub ip 2>/dev/null || true
|
||||
}
|
||||
|
||||
@test "IPv6 GUA only: blocking enabled" {
|
||||
stub ip "-6 address : echo 'inet6 2003:12:1e43:301:d210:52fa:fe00:7ad7/64 scope global'"
|
||||
run bash -c "
|
||||
source /opt/pihole/basic-install.sh
|
||||
find_IPv6_information
|
||||
"
|
||||
assert_output --partial "Found IPv6 GUA address"
|
||||
|
||||
unstub ip 2>/dev/null || true
|
||||
}
|
||||
|
||||
@test "IPv6 GUA + ULA: ULA takes precedence" {
|
||||
stub ip "-6 address : printf 'inet6 2003:12:1e43:301:d210:52fa:fe00:7ad7/64 scope global\ninet6 fda2:2001:5555:0:d210:52fa:fe00:7ad7/64 scope global\n'"
|
||||
run bash -c "
|
||||
source /opt/pihole/basic-install.sh
|
||||
find_IPv6_information
|
||||
"
|
||||
assert_output --partial "Found IPv6 ULA address"
|
||||
|
||||
unstub ip 2>/dev/null || true
|
||||
}
|
||||
|
||||
@test "IPv6 ULA + GUA: ULA takes precedence" {
|
||||
stub ip "-6 address : printf 'inet6 fda2:2001:5555:0:d210:52fa:fe00:7ad7/64 scope global\ninet6 2003:12:1e43:301:d210:52fa:fe00:7ad7/64 scope global\n'"
|
||||
run bash -c "
|
||||
source /opt/pihole/basic-install.sh
|
||||
find_IPv6_information
|
||||
"
|
||||
assert_output --partial "Found IPv6 ULA address"
|
||||
|
||||
unstub ip 2>/dev/null || true
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# IP address validation
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "valid_ip accepts and rejects addresses correctly" {
|
||||
_valid() {
|
||||
run bash -c "source /opt/pihole/basic-install.sh; valid_ip '${1}'"
|
||||
assert_success
|
||||
}
|
||||
_invalid() {
|
||||
run bash -c "source /opt/pihole/basic-install.sh; valid_ip '${1}'"
|
||||
assert_failure
|
||||
}
|
||||
|
||||
_valid "192.168.1.1"
|
||||
_valid "127.0.0.1"
|
||||
_valid "255.255.255.255"
|
||||
_invalid "255.255.255.256"
|
||||
_invalid "255.255.256.255"
|
||||
_invalid "255.256.255.255"
|
||||
_invalid "256.255.255.255"
|
||||
_invalid "1092.168.1.1"
|
||||
_invalid "not an IP"
|
||||
_invalid "8.8.8.8#"
|
||||
_valid "8.8.8.8#0"
|
||||
_valid "8.8.8.8#1"
|
||||
_valid "8.8.8.8#42"
|
||||
_valid "8.8.8.8#888"
|
||||
_valid "8.8.8.8#1337"
|
||||
_valid "8.8.8.8#65535"
|
||||
_invalid "8.8.8.8#65536"
|
||||
_invalid "8.8.8.8#-1"
|
||||
_invalid "00.0.0.0"
|
||||
_invalid "010.0.0.0"
|
||||
_invalid "001.0.0.0"
|
||||
_invalid "0.0.0.0#00"
|
||||
_invalid "0.0.0.0#01"
|
||||
_invalid "0.0.0.0#001"
|
||||
_invalid "0.0.0.0#0001"
|
||||
_invalid "0.0.0.0#00001"
|
||||
}
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bats
|
||||
# Tests for SELinux handling in basic-install.sh.
|
||||
# Only runs on rhel family (CentOS/Fedora) — selected by run.sh.
|
||||
|
||||
load 'libs/bats-support/load'
|
||||
load 'libs/bats-assert/load'
|
||||
load 'libs/bats-mock/stub'
|
||||
|
||||
TICK="[✓]"
|
||||
CROSS="[✗]"
|
||||
|
||||
_mock_selinux_config() {
|
||||
local state="$1" # enforcing, permissive, or disabled
|
||||
local capitalized
|
||||
capitalized=$(echo "${state}" | awk '{print toupper(substr($0,1,1)) substr($0,2)}')
|
||||
stub getenforce ": echo '${capitalized}'"
|
||||
mkdir -p /etc/selinux
|
||||
echo "SELINUX=${state}" > /etc/selinux/config
|
||||
}
|
||||
|
||||
@test "SELinux enforcing: installer exits with error" {
|
||||
_mock_selinux_config "enforcing"
|
||||
run bash -c "
|
||||
source /opt/pihole/basic-install.sh
|
||||
checkSelinux
|
||||
"
|
||||
assert_output --partial "${CROSS} Current SELinux: enforcing"
|
||||
assert_output --partial "SELinux Enforcing detected, exiting installer"
|
||||
assert_failure
|
||||
|
||||
unstub getenforce 2>/dev/null || true
|
||||
}
|
||||
|
||||
@test "SELinux permissive: installer continues" {
|
||||
_mock_selinux_config "permissive"
|
||||
run bash -c "
|
||||
source /opt/pihole/basic-install.sh
|
||||
checkSelinux
|
||||
"
|
||||
assert_output --partial "${TICK} Current SELinux: permissive"
|
||||
assert_success
|
||||
|
||||
unstub getenforce 2>/dev/null || true
|
||||
}
|
||||
|
||||
@test "SELinux disabled: installer continues" {
|
||||
_mock_selinux_config "disabled"
|
||||
run bash -c "
|
||||
source /opt/pihole/basic-install.sh
|
||||
checkSelinux
|
||||
"
|
||||
assert_output --partial "${TICK} Current SELinux: disabled"
|
||||
assert_success
|
||||
|
||||
unstub getenforce 2>/dev/null || true
|
||||
}
|
||||
Executable
+59
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env bats
|
||||
# Tests for utils.sh
|
||||
|
||||
load 'libs/bats-support/load'
|
||||
load 'libs/bats-assert/load'
|
||||
load 'libs/bats-file/load'
|
||||
|
||||
setup() {
|
||||
TEST_TEMP_DIR="$(temp_make)"
|
||||
}
|
||||
|
||||
teardown() {
|
||||
temp_del "${TEST_TEMP_DIR}"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "addOrEditKeyValPair adds and replaces key-value pairs correctly" {
|
||||
local outfile="${TEST_TEMP_DIR}/testoutput"
|
||||
bash -c "
|
||||
source /opt/pihole/utils.sh
|
||||
addOrEditKeyValPair '${outfile}' 'KEY_ONE' 'value1'
|
||||
addOrEditKeyValPair '${outfile}' 'KEY_TWO' 'value2'
|
||||
addOrEditKeyValPair '${outfile}' 'KEY_ONE' 'value3'
|
||||
addOrEditKeyValPair '${outfile}' 'KEY_FOUR' 'value4'
|
||||
"
|
||||
assert_file_exists "${outfile}"
|
||||
assert_file_contains "${outfile}" "KEY_ONE=value3"
|
||||
assert_file_contains "${outfile}" "KEY_TWO=value2"
|
||||
assert_file_contains "${outfile}" "KEY_FOUR=value4"
|
||||
assert_file_not_contains "${outfile}" "KEY_ONE=value1"
|
||||
}
|
||||
|
||||
@test "getFTLPID returns -1 when FTL is not running" {
|
||||
run bash -c "
|
||||
source /opt/pihole/utils.sh
|
||||
getFTLPID
|
||||
"
|
||||
assert_output "-1"
|
||||
}
|
||||
|
||||
@test "setFTLConfigValue and getFTLConfigValue round-trip" {
|
||||
# FTL must be installed for this test
|
||||
bash -c "
|
||||
source /opt/pihole/basic-install.sh
|
||||
create_pihole_user
|
||||
funcOutput=\$(get_binary_name)
|
||||
echo 'development' > /etc/pihole/ftlbranch
|
||||
binary=\"pihole-FTL\${funcOutput##*pihole-FTL}\"
|
||||
theRest=\"\${funcOutput%pihole-FTL*}\"
|
||||
FTLdetect \"\${binary}\" \"\${theRest}\"
|
||||
"
|
||||
run bash -c "
|
||||
source /opt/pihole/utils.sh
|
||||
setFTLConfigValue 'dns.upstreams' '[\"9.9.9.9\"]' > /dev/null
|
||||
getFTLConfigValue 'dns.upstreams'
|
||||
"
|
||||
assert_output --partial "[ 9.9.9.9 ]"
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
[tox]
|
||||
envlist = py3
|
||||
|
||||
[testenv:py3]
|
||||
allowlist_externals = docker
|
||||
deps = -rrequirements.txt
|
||||
setenv =
|
||||
COLUMNS=120
|
||||
commands = docker buildx build --load --progress plain -f _alpine_3_21.Dockerfile -t pytest_pihole:test_container ../
|
||||
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py
|
||||
@@ -1,10 +0,0 @@
|
||||
[tox]
|
||||
envlist = py3
|
||||
|
||||
[testenv:py3]
|
||||
allowlist_externals = docker
|
||||
deps = -rrequirements.txt
|
||||
setenv =
|
||||
COLUMNS=120
|
||||
commands = docker buildx build --load --progress plain -f _alpine_3_22.Dockerfile -t pytest_pihole:test_container ../
|
||||
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py
|
||||
@@ -1,10 +0,0 @@
|
||||
[tox]
|
||||
envlist = py3
|
||||
|
||||
[testenv:py3]
|
||||
allowlist_externals = docker
|
||||
deps = -rrequirements.txt
|
||||
setenv =
|
||||
COLUMNS=120
|
||||
commands = docker buildx build --load --progress plain -f _alpine_3_23.Dockerfile -t pytest_pihole:test_container ../
|
||||
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py
|
||||
@@ -1,10 +0,0 @@
|
||||
[tox]
|
||||
envlist = py3
|
||||
|
||||
[testenv:py3]
|
||||
allowlist_externals = docker
|
||||
deps = -rrequirements.txt
|
||||
setenv =
|
||||
COLUMNS=120
|
||||
commands = docker buildx build --load --progress plain -f _centos_10.Dockerfile -t pytest_pihole:test_container ../
|
||||
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py ./test_centos_fedora_common_support.py
|
||||
@@ -1,10 +0,0 @@
|
||||
[tox]
|
||||
envlist = py3
|
||||
|
||||
[testenv:py3]
|
||||
allowlist_externals = docker
|
||||
deps = -rrequirements.txt
|
||||
setenv =
|
||||
COLUMNS=120
|
||||
commands = docker buildx build --load --progress plain -f _centos_9.Dockerfile -t pytest_pihole:test_container ../
|
||||
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py ./test_centos_fedora_common_support.py
|
||||
@@ -1,10 +0,0 @@
|
||||
[tox]
|
||||
envlist = py3
|
||||
|
||||
[testenv:py3]
|
||||
allowlist_externals = docker
|
||||
deps = -rrequirements.txt
|
||||
setenv =
|
||||
COLUMNS=120
|
||||
commands = docker buildx build --load --progress plain -f _debian_11.Dockerfile -t pytest_pihole:test_container ../
|
||||
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py
|
||||
@@ -1,10 +0,0 @@
|
||||
[tox]
|
||||
envlist = py3
|
||||
|
||||
[testenv:py3]
|
||||
allowlist_externals = docker
|
||||
deps = -rrequirements.txt
|
||||
setenv =
|
||||
COLUMNS=120
|
||||
commands = docker buildx build --load --progress plain -f _debian_12.Dockerfile -t pytest_pihole:test_container ../
|
||||
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py
|
||||
@@ -1,10 +0,0 @@
|
||||
[tox]
|
||||
envlist = py3
|
||||
|
||||
[testenv:py3]
|
||||
allowlist_externals = docker
|
||||
deps = -rrequirements.txt
|
||||
setenv =
|
||||
COLUMNS=120
|
||||
commands = docker buildx build --load --progress plain -f _debian_13.Dockerfile -t pytest_pihole:test_container ../
|
||||
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py
|
||||
@@ -1,10 +0,0 @@
|
||||
[tox]
|
||||
envlist = py3
|
||||
|
||||
[testenv]
|
||||
allowlist_externals = docker
|
||||
deps = -rrequirements.txt
|
||||
setenv =
|
||||
COLUMNS=120
|
||||
commands = docker buildx build --load --progress plain -f _fedora_40.Dockerfile -t pytest_pihole:test_container ../
|
||||
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py ./test_centos_fedora_common_support.py
|
||||
@@ -1,10 +0,0 @@
|
||||
[tox]
|
||||
envlist = py3
|
||||
|
||||
[testenv]
|
||||
allowlist_externals = docker
|
||||
deps = -rrequirements.txt
|
||||
setenv =
|
||||
COLUMNS=120
|
||||
commands = docker buildx build --load --progress plain -f _fedora_41.Dockerfile -t pytest_pihole:test_container ../
|
||||
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py ./test_centos_fedora_common_support.py
|
||||
@@ -1,10 +0,0 @@
|
||||
[tox]
|
||||
envlist = py3
|
||||
|
||||
[testenv]
|
||||
allowlist_externals = docker
|
||||
deps = -rrequirements.txt
|
||||
setenv =
|
||||
COLUMNS=120
|
||||
commands = docker buildx build --load --progress plain -f _fedora_42.Dockerfile -t pytest_pihole:test_container ../
|
||||
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py ./test_centos_fedora_common_support.py
|
||||
@@ -1,10 +0,0 @@
|
||||
[tox]
|
||||
envlist = py3
|
||||
|
||||
[testenv]
|
||||
allowlist_externals = docker
|
||||
deps = -rrequirements.txt
|
||||
setenv =
|
||||
COLUMNS=120
|
||||
commands = docker buildx build --load --progress plain -f _fedora_43.Dockerfile -t pytest_pihole:test_container ../
|
||||
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py ./test_centos_fedora_common_support.py
|
||||
@@ -1,10 +0,0 @@
|
||||
[tox]
|
||||
envlist = py3
|
||||
|
||||
[testenv:py3]
|
||||
allowlist_externals = docker
|
||||
deps = -rrequirements.txt
|
||||
setenv =
|
||||
COLUMNS=120
|
||||
commands = docker buildx build --load --progress plain -f _ubuntu_20.Dockerfile -t pytest_pihole:test_container ../
|
||||
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py
|
||||
@@ -1,10 +0,0 @@
|
||||
[tox]
|
||||
envlist = py3
|
||||
|
||||
[testenv:py3]
|
||||
allowlist_externals = docker
|
||||
deps = -rrequirements.txt
|
||||
setenv =
|
||||
COLUMNS=120
|
||||
commands = docker buildx build --load --progress plain -f _ubuntu_22.Dockerfile -t pytest_pihole:test_container ../
|
||||
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py
|
||||
@@ -1,10 +0,0 @@
|
||||
[tox]
|
||||
envlist = py3
|
||||
|
||||
[testenv:py3]
|
||||
allowlist_externals = docker
|
||||
deps = -rrequirements.txt
|
||||
setenv =
|
||||
COLUMNS=120
|
||||
commands = docker buildx build --load --progress plain -f _ubuntu_24.Dockerfile -t pytest_pihole:test_container ../
|
||||
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py
|
||||
Reference in New Issue
Block a user