Skip to content
274 changes: 274 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
#
# Copyright 2026 ABSA Group Limited
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

name: Test

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

permissions:
contents: read

jobs:
test:
name: Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
Comment on lines +37 to +40

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even the referenced code clearly says actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd, nothing to fix here


# Action uses bash 4+ features (mapfile, associative arrays).
# macOS runners ship bash 3.2 — install a modern version via Homebrew.
- name: Install bash (macOS)
if: runner.os == 'macOS'
run: brew install bash

- name: Generate test fixtures
shell: bash
run: |
set -euo pipefail

FIXTURES_DIR="tests/fixtures"
CA_DIR="$(pwd)/$FIXTURES_DIR/ca"
mkdir -p "$CA_DIR/newcerts"
touch "$CA_DIR/index.txt"
echo "1000" > "$CA_DIR/serial"

cat > "$CA_DIR/openssl.cnf" << EOF
[ca]
default_ca = test_ca
[test_ca]
dir = ${CA_DIR}
database = \$dir/index.txt
new_certs_dir = \$dir/newcerts
serial = \$dir/serial
certificate = \$dir/ca.pem
private_key = \$dir/ca.key
default_md = sha256
policy = policy_any
unique_subject = no
[policy_any]
commonName = supplied
[req]
distinguished_name = req_dn
prompt = no
[req_dn]
EOF

openssl req -x509 -newkey rsa:2048 -keyout "$CA_DIR/ca.key" -out "$CA_DIR/ca.pem" \
-days 7300 -nodes -subj "/CN=Test CA" 2>/dev/null

sign_cert() {
local out="$1" cn="$2" startdate="$3" enddate="$4"
local tmpkey tmpcsr
tmpkey=$(mktemp)
tmpcsr=$(mktemp)
openssl req -newkey rsa:2048 -keyout "$tmpkey" -out "$tmpcsr" \
-nodes -subj "/CN=$cn" 2>/dev/null
openssl ca -batch -config "$CA_DIR/openssl.cnf" \
-startdate "$startdate" -enddate "$enddate" \
-in "$tmpcsr" -out "$out" -notext 2>/dev/null
rm -f "$tmpkey" "$tmpcsr"
}

YEAR=$(date -u +%Y)
YY_2AGO=$(printf "%02d" $(( (YEAR - 2) % 100 )))
YY_1AGO=$(printf "%02d" $(( (YEAR - 1) % 100 )))
YY_1AHEAD=$(printf "%02d" $(( (YEAR + 1) % 100 )))
YY_2AHEAD=$(printf "%02d" $(( (YEAR + 2) % 100 )))

openssl req -x509 -newkey rsa:2048 -keyout /dev/null \
-out "$FIXTURES_DIR/valid.pem" -days 365 -nodes \
-subj "/CN=valid.example.com" 2>/dev/null

openssl req -x509 -newkey rsa:2048 -keyout /dev/null \
-out "$FIXTURES_DIR/expiring.pem" -days 10 -nodes \
-subj "/CN=expiring.example.com" 2>/dev/null

sign_cert "$FIXTURES_DIR/expired.pem" "expired.example.com" \
"${YY_2AGO}0101000000Z" "${YY_1AGO}0101000000Z"

sign_cert "$FIXTURES_DIR/not_yet_valid.pem" "notyetvalid.example.com" \
"${YY_1AHEAD}0101000000Z" "${YY_2AHEAD}0101000000Z"

sign_cert "$FIXTURES_DIR/multi_expired.pem" "multi.example.com" \
"${YY_2AGO}0101000000Z" "${YY_1AGO}0101000000Z"

openssl req -x509 -newkey rsa:2048 -keyout /dev/null \
-out "$FIXTURES_DIR/multi_valid.pem" -days 365 -nodes \
-subj "/CN=multi.example.com" 2>/dev/null

# Expiring-soon cert sharing a subject with multi_valid.pem, used to
# prove fail_on_warn does not fail when a newer valid replacement exists.
openssl req -x509 -newkey rsa:2048 -keyout /dev/null \
-out "$FIXTURES_DIR/multi_expiring.pem" -days 10 -nodes \
-subj "/CN=multi.example.com" 2>/dev/null

echo "Fixtures generated:"
ls -la "$FIXTURES_DIR"/*.pem

- name: 'Test: valid certificate'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks awesome overrall, one thing i spotted is that the case for fail_on_warn test case is not added, another one is that we do not test if given certs are valid JSON.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added those test cases, it should be complete now

shell: bash
run: |
SUMMARY=$(mktemp)
INPUT_CERTIFICATES='["tests/fixtures/valid.pem"]' \
INPUT_WARNING_DAYS=30 \
GITHUB_STEP_SUMMARY="$SUMMARY" \
bash validate.sh
grep -q "✅ Valid" "$SUMMARY"
rm -f "$SUMMARY"

- name: 'Test: expiring-soon certificate'
shell: bash
run: |
SUMMARY=$(mktemp)
INPUT_CERTIFICATES='["tests/fixtures/expiring.pem"]' \
INPUT_WARNING_DAYS=30 \
GITHUB_STEP_SUMMARY="$SUMMARY" \
bash validate.sh
grep -q "Expiring Soon" "$SUMMARY"
rm -f "$SUMMARY"

- name: 'Test: expired certificate'
shell: bash
run: |
SUMMARY=$(mktemp)
EXIT=0
INPUT_CERTIFICATES='["tests/fixtures/expired.pem"]' \
INPUT_WARNING_DAYS=30 \
GITHUB_STEP_SUMMARY="$SUMMARY" \
bash validate.sh || EXIT=$?
if [[ "$EXIT" -ne 1 ]]; then
echo "::error::Expected exit code 1 for expired cert, got $EXIT"
exit 1
fi
grep -q "Expired" "$SUMMARY"
rm -f "$SUMMARY"

- name: 'Test: not-yet-valid certificate'
shell: bash
run: |
SUMMARY=$(mktemp)
EXIT=0
INPUT_CERTIFICATES='["tests/fixtures/not_yet_valid.pem"]' \
INPUT_WARNING_DAYS=30 \
GITHUB_STEP_SUMMARY="$SUMMARY" \
bash validate.sh || EXIT=$?
if [[ "$EXIT" -ne 1 ]]; then
echo "::error::Expected exit code 1 for not-yet-valid cert, got $EXIT"
exit 1
fi
grep -q "Failed" "$SUMMARY"
rm -f "$SUMMARY"

- name: 'Test: missing file'
shell: bash
run: |
SUMMARY=$(mktemp)
EXIT=0
INPUT_CERTIFICATES='["/nonexistent/cert.pem"]' \
INPUT_WARNING_DAYS=30 \
GITHUB_STEP_SUMMARY="$SUMMARY" \
bash validate.sh || EXIT=$?
if [[ "$EXIT" -ne 1 ]]; then
echo "::error::Expected exit code 1 for missing file, got $EXIT"
exit 1
fi
grep -q "Failed" "$SUMMARY"
rm -f "$SUMMARY"

- name: 'Test: multi-cert with valid replacement'
shell: bash
run: |
SUMMARY=$(mktemp)
INPUT_CERTIFICATES='["tests/fixtures/multi_expired.pem", "tests/fixtures/multi_valid.pem"]' \
INPUT_WARNING_DAYS=30 \
GITHUB_STEP_SUMMARY="$SUMMARY" \
bash validate.sh
grep -q "newer replacement exists" "$SUMMARY"
rm -f "$SUMMARY"

- name: 'Test: fail_on_warn fails on expiring-soon certificate'
shell: bash
run: |
SUMMARY=$(mktemp)
EXIT=0
INPUT_CERTIFICATES='["tests/fixtures/expiring.pem"]' \
INPUT_WARNING_DAYS=30 \
INPUT_FAIL_ON_WARN=true \
GITHUB_STEP_SUMMARY="$SUMMARY" \
bash validate.sh || EXIT=$?
if [[ "$EXIT" -ne 1 ]]; then
echo "::error::Expected exit code 1 for fail_on_warn=true with expiring cert, got $EXIT"
exit 1
fi
grep -q "Expiring Soon" "$SUMMARY"
rm -f "$SUMMARY"

- name: 'Test: fail_on_warn does not fail when expiring cert has valid replacement'
shell: bash
run: |
SUMMARY=$(mktemp)
INPUT_CERTIFICATES='["tests/fixtures/multi_expiring.pem", "tests/fixtures/multi_valid.pem"]' \
INPUT_WARNING_DAYS=30 \
INPUT_FAIL_ON_WARN=true \
GITHUB_STEP_SUMMARY="$SUMMARY" \
bash validate.sh
# Must NOT fail (newer valid replacement exists, so WARN_COUNT stays 0),
# and the summary status must reflect the nearing-expiry cert.
grep -q "Expiring Soon (newer replacement exists)" "$SUMMARY"
rm -f "$SUMMARY"

- name: 'Test: invalid JSON input'
shell: bash
run: |
OUTPUT=$(mktemp)
EXIT=0
INPUT_CERTIFICATES='not-valid-json' \
INPUT_WARNING_DAYS=30 \
bash validate.sh > "$OUTPUT" 2>&1 || EXIT=$?
if [[ "$EXIT" -ne 1 ]]; then
echo "::error::Expected exit code 1 for invalid JSON input, got $EXIT"
cat "$OUTPUT"
exit 1
fi
grep -q "must be a valid JSON array" "$OUTPUT"
rm -f "$OUTPUT"

- name: 'Test: valid JSON but not an array'
shell: bash
run: |
OUTPUT=$(mktemp)
EXIT=0
INPUT_CERTIFICATES='{"cert": "tests/fixtures/valid.pem"}' \
INPUT_WARNING_DAYS=30 \
bash validate.sh > "$OUTPUT" 2>&1 || EXIT=$?
if [[ "$EXIT" -ne 1 ]]; then
echo "::error::Expected exit code 1 for non-array JSON input, got $EXIT"
cat "$OUTPUT"
exit 1
fi
grep -q "must be a valid JSON array" "$OUTPUT"
rm -f "$OUTPUT"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ Thumbs.db

# Local test scripts
run_locally.sh

# Generated test fixtures
tests/fixtures/
Loading
Loading