Skip to content

Add CI with unit and integration tests for the install and management scripts - #13

Open
fivetran-paulharper wants to merge 13 commits into
mainfrom
paulh/ci-tests
Open

fivetran-paulharper wants to merge 13 commits into
mainfrom
paulh/ci-tests

Conversation

@fivetran-paulharper

@fivetran-paulharper fivetran-paulharper commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds a GitHub Actions workflow that tests the install and management scripts on every pull request, on both Linux and Windows. Until now the Windows scripts in particular had only been run by hand.

What runs in CI

Job Runner What it does
shellcheck ubuntu-latest Lints the shell scripts.
bats ubuntu-latest tests/bash/manager.bats: 11 black-box tests of proxy-agent-manager.sh with stubbed docker and curl.
pester windows-2022, windows-2025 tests/pwsh/Manager.Tests.ps1: the same shape for proxy-agent-manager.ps1, in both Linux-container and Windows-container mode, with docker, Invoke-RestMethod and Get-CimInstance mocked. Runs under Windows PowerShell 5.1, which is what customers use.
integration-linux ubuntu-latest tests/integration/linux.sh: runs install.sh and the manager against a real Docker daemon.
integration-windows windows-2022, windows-2025 tests/integration/windows.ps1: same for install.ps1 with Windows containers. Each runner tests its own LTSC image (ltsc2022, ltsc2025).

The integration tests build a stub agent image (tests/stub/) that only writes the heartbeat file the health check reads, and start a small local server (tests/integration/registry_server.py) that stands in for the registry tag listing and the management script download. They install from a fixture config.json, then assert the running image, health, version file, memory limit and config permissions, and exercise rollback via the version file, upgrade, a no-op upgrade, restart and stop. No Fivetran credentials or network access to Fivetran are needed.

Script changes

The scripts read three environment variables so the tests can point them at the stub image and local server:

  • PROXY_AGENT_IMAGE (management scripts): image repository.
  • PROXY_AGENT_REGISTRY_TAGS_URL (installers and management scripts): registry tag listing URL.
  • PROXY_AGENT_SCRIPT_URL (installers): management script download URL. Without this the installer under test would fetch the manager from main instead of the branch.

Behavior is unchanged when they are unset. They are documented in a new README "Development" section along with how to run the tests locally.

Not covered

  • ltsc2019 Windows containers: GitHub retired the windows-2019 runner.
  • Linux containers on Windows (Docker Desktop with WSL 2): hosted Windows runners have no nested virtualization.

Relationship to #12

This branch is off main, not stacked on #12. The tests assert tag and version-file prefixes rather than exact formats where the two branches differ, so they should pass on either and neither PR should block the other.

Testing

  • bats: 11/11 pass locally (via the bats/bats Docker image).
  • tests/integration/linux.sh: passes locally against Docker.
  • Pester and the Windows integration test have not run anywhere yet. This PR's own CI run is their first execution; expect to iterate on the Windows jobs.

🤖 Generated with Claude Code

fivetran-paulharper and others added 6 commits October 30, 2026 06:29
… scripts

A GitHub Actions workflow runs shellcheck, bats unit tests for
proxy-agent-manager.sh, Pester unit tests for proxy-agent-manager.ps1
under Windows PowerShell 5.1, and integration tests that run the
installer and management script against a real Docker daemon: Linux
containers on Ubuntu, Windows containers on Windows Server 2022 and
2025.

The unit tests run the scripts as black boxes with docker and the
registry lookup stubbed. The integration tests use a stub agent image
that only writes the heartbeat file, plus a small local server that
stands in for the registry tag listing and the management script
download, so they need no Fivetran credentials.

To make that possible the scripts read three environment variables,
PROXY_AGENT_IMAGE, PROXY_AGENT_REGISTRY_TAGS_URL and
PROXY_AGENT_SCRIPT_URL, which override the image repository, registry
URL and management script URL. Without them behavior is unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Pester 5 does not allow BeforeEach/AfterEach at file root, so the tests
now live in one Describe with a Context per container mode.

Get-LatestVersion in proxy-agent-manager.ps1 derived the registry host
from the image name before checking PROXY_AGENT_REGISTRY_TAGS_URL, so an
image name without a slash threw from Substring. The override is now
checked first.

shellcheck runs at warning severity and the one info-level finding in
install.sh is rewritten as an if. PSScriptAnalyzer is added as a lint
job, with Write-Host and ShouldProcess rules excluded via
PSScriptAnalyzerSettings.psd1.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The Pester tests replace docker, Invoke-RestMethod and Get-CimInstance
with plain functions defined in BeforeAll instead of Pester mocks, which
did not expose the call arguments to the stub.

The Windows integration test checks for the stopped container with
docker ps rather than docker inspect, whose stderr became a terminating
error under ErrorActionPreference Stop.

install.ps1 and proxy-agent-manager.ps1 get a UTF-8 BOM, which Windows
PowerShell 5.1 needs to read their non-ASCII comments correctly, and the
remaining PSScriptAnalyzer findings are either fixed or excluded with a
reason in PSScriptAnalyzerSettings.psd1.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Functions are dynamically scoped in PowerShell, so inside the docker
stub called from the manager script $script: resolved to the manager's
scope and every stub returned nothing. The stubs now share state through
$global:ProxyAgentStub, which both the tests and the invoked script see.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Piping the array of recorded calls through Where-Object unrolled each
call into strings, so Get-DockerRunArgs returned 'run'. A foreach loop
returns the argument list intact. The deliberate global stub state is
suppressed for PSAvoidGlobalVars in the test file.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Piping the helper straight into Should kept the comma wrapper as an
extra array level. Returning the arguments plainly works both piped and
assigned.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Comment thread tests/bash/manager.bats Resolved
Comment thread tests/bash/manager.bats Resolved
…gistry

The bats docker and curl stubs move from heredocs into tests/bash/stubs
so the test file reads as tests. The image repository constants in the
bats and Pester tests are renamed and commented to make clear they only
build expected strings, and the integration test headers state that the
only network access is pulling the stub's base image.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Comment thread tests/bash/stubs/curl Resolved
fivetran-paulharper and others added 6 commits October 30, 2026 07:16
The curl stub echoes STUB_CURL_RESPONSE so tests can return any body.
The Windows integration job first starts the Docker service if needed
and waits for the daemon, which was not running on one windows-2025
runner, and the test's cleanup no longer turns its own errors into the
reported failure.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The workflow gets read-only token permissions, cancels superseded runs
of the same branch, sets timeouts on every job and pins Pester to 5.x.
The Linux integration test fails fast if the stub registry dies on bind
and no longer greps pipeline output under pipefail; the Windows one
quotes paths passed to Start-Process. Both unit suites now assert the
registry URL requested, including the PROXY_AGENT_REGISTRY_TAGS_URL
override, and the Windows health check test decodes the command rather
than matching its prefix. README lists the local prerequisites.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ly PRs

The Windows integration test fails fast if the stub registry exits
during the readiness loop, the Docker-daemon wait step keeps retrying
instead of stopping on docker's first stderr line, runs on main are no
longer cancelled by a later push, and the Pester default-URL test clears
PROXY_AGENT_REGISTRY_TAGS_URL before running.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The stub exits unless started with `-i <config path>` pointing at a
readable file, so a regression in how the management scripts pass the
config to the agent fails the integration tests instead of passing
against a stub that ignored its arguments.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The local server now answers POST /proxy-agent/configure with the
fixture config when the Basic credential matches and 401 otherwise, and
both installers are pointed at it through FIVETRAN_API_URL. The
integration tests install with a TOKEN, check that a wrong TOKEN fails
with HTTP 401 without writing a config, and install from a legacy
WebSocket config, which the installer migrates through the same call.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
PSScriptAnalyzer flagged the parameter as unused; the stub needs it to
accept the manager's call.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant