# Testing PAC files in CI with pactester

Source: https://findproxyforurl.net/articles/test-pac-files-in-ci-with-pactester/ · updated 2026-10-06

A PAC file is code that routes every request in your organisation, yet it is usually edited by hand and deployed without a test. A small regression test with pactester catches the classic "one line changed, half the traffic goes direct" before it reaches users.

## 1. Write down what the PAC should do

A plain text file, `pac-tests.txt`, with one URL and the expected answer per line:

```text
http://intranet/                        DIRECT
https://wiki.corp.example/              DIRECT
http://10.1.2.3/                        DIRECT
http://evilcorp.example/                PROXY proxy1.corp.example:8080; PROXY proxy2.corp.example:8080
https://www.example.com/                PROXY proxy1.corp.example:8080; PROXY proxy2.corp.example:8080
```

Include the cases that break PAC files in practice: look-alike domains, IP literals, plain host names,
and a URL that matches no rule, which tests the default route. The
[testing guide](https://findproxyforurl.net/articles/how-to-test-a-pac-file/#which-urls-to-test) has a fuller list.

## 2. Run it with pactester

`pactester -p <file> -u <url>` prints the proxy string for one URL, and `-c <ip>` sets what
`myIpAddress()` returns. With `-f <file>` it reads one URL per line and prints `url : result`. Our
own lab runs pactester this way (source: lab, 2026-10-04).

```sh
#!/bin/sh
# pac-test.sh - fail when proxy.pac routes a test URL differently than expected
pac=${1:-proxy.pac}; tests=${2:-pac-tests.txt}; fail=0
while read -r url expected; do
  case "$url" in ''|'#'*) continue ;; esac
  got=$(pactester -p "$pac" -u "$url" -c 10.1.2.100)
  if [ "$got" != "$expected" ]; then
    echo "FAIL $url"; echo "  expected: $expected"; echo "  got:      $got"; fail=1
  fi
done < "$tests"
exit $fail
```

## 3. Or with the pacparser Python module

```python
import pacparser, sys

pacparser.init()
pacparser.setmyip("10.1.2.100")
pacparser.parse_pac_file("proxy.pac")
fail = 0
for line in open("pac-tests.txt"):
    if not line.strip() or line.startswith("#"):
        continue
    url, expected = line.split(None, 1)
    host = url.split("/")[2].split(":")[0]
    got = pacparser.find_proxy(url, host)
    if got != expected.strip():
        print(f"FAIL {url}\n  expected: {expected.strip()}\n  got:      {got}")
        fail = 1
pacparser.cleanup()
sys.exit(fail)
```

`init`, `setmyip`, `find_proxy` and `cleanup` are the calls our parity tests use against the real
library (source: code, 2026-10-04). Install it with `pip install pacparser`.

## 4. Know what pactester does not see

pactester is one engine, pacparser. In the browsers:

- Chrome removes the path and query of `https://` URLs before calling the PAC, and Firefox removes
  them for every scheme. pactester passes the full URL (source: code, verified 2026-10-04).
- pactester does not lower-case `host` (source: code, verified 2026-10-04), so lower-case it in the
  PAC.
- pactester resolves names with real DNS. Make sure CI's resolver gives the answers you expect, or
  keep DNS functions out of the PAC ([DNS in PAC files](https://findproxyforurl.net/articles/dns-in-pac-files/)).

The full list is in the [engine differences](https://findproxyforurl.net/engines/differences/) table. For the cross-engine view
and the mistake checks, run the file through the [online PAC file tester](https://findproxyforurl.net/check/) as well. Do that
before the commit; CI is the safety net after it.


## Frequently asked questions

### How do I test a PAC file automatically?

Keep a file of test URLs with the expected proxy string for each, run every URL through pactester (from pacparser) or the pacparser Python module in your CI pipeline, and fail the build when an answer differs.

### Does pactester behave like Chrome or Firefox?

Not exactly. pactester passes the full URL to the script, while Chrome removes the path and query of https URLs and Firefox removes them for every scheme. pactester also does not lower-case host. Keep URL-path rules out of the PAC, or test them in the browsers as well.

