Skip to content

feat(tests): run live tests against local tlsfingerprint.com Docker instance#49

Merged
Danny-Dasilva merged 14 commits intomainfrom
pr-43-tlsfingerprint-live
May 7, 2026
Merged

feat(tests): run live tests against local tlsfingerprint.com Docker instance#49
Danny-Dasilva merged 14 commits intomainfrom
pr-43-tlsfingerprint-live

Conversation

@Danny-Dasilva
Copy link
Copy Markdown
Owner

Summary

Adapts the local-Docker live-tests pattern from #43 (smeinecke), but uses Danny-Dasilva/tlsfingerprint.com (the source of tls.peet.ws) instead of the third-party TrackMe image. This keeps the test infrastructure under our control and ensures response-shape parity with the production endpoint we already test against.

Tests fall back to https://tls.peet.ws when TLSFP_URL is unset (CI uses the local container; local dev defaults to production).

Co-authored-by: Stefan Meinecke (test-pattern inspiration from #43)

How it works

  • CI workflow clones Danny-Dasilva/tlsfingerprint.com@master, generates self-signed certs, builds + runs the Docker container with NET_ADMIN/NET_RAW caps, then runs the live-tests pytest matrix against https://localhost.
  • Tests use TLSFP_URL env var (default: https://tls.peet.ws).
  • smeinecke's six commits are preserved with original authorship; the env-var rename and Docker swap are committed as Danny-Dasilva.

Test Plan

  • CI green on local-Docker run
  • Backward-compat: omitting TLSFP_URL falls back to tls.peet.ws and tests still pass
  • uv run ruff check tests/ clean
  • uv run pytest tests/ --collect-only clean (1187 tests collected)

@Danny-Dasilva Danny-Dasilva force-pushed the pr-43-tlsfingerprint-live branch 3 times, most recently from 0ad58bd to cea296d Compare April 28, 2026 13:50
smeinecke and others added 10 commits April 28, 2026 09:55
Replace hardcoded tls.peet.ws URLs with a TRACKME_URL environment variable
(defaulting to https://tls.peet.ws for backward compatibility) so CI and
local tests can run against a local TrackMe Docker instance.

- Add docker/trackme/Dockerfile + config.json: builds TrackMe from GitHub
  (golang:1.24-alpine, no pcap/QUIC, ports 8443/8080)
- Add docker-compose.test.yml: runs local TrackMe
- Add scripts/setup-trackme-certs.sh: generates self-signed certs; prints
  SSL_CERT_FILE hint for no-sudo local testing
- Update all test files (20 files) to read TRACKME_URL from env
- Update blocking-tests.yml and live-tests.yml:
  - Generate self-signed TLS certs; combine with system CA bundle
  - Start TrackMe via docker compose and wait for health
  - Set TRACKME_URL + SSL_CERT_FILE for the test run

Tested locally: 22/22 blocking tests pass against https://localhost:8443
…ale connections

TrackMe closes connections after each request; module-scoped CycleTLS clients
reuse stale connections from the pool, causing "use of closed network connection" errors.
TrackMe closes TCP connections after each request. The Go transport
(loaded as a shared library) caches TLS connections in a global pool;
the next test reuses the already-closed connection, causing "use of
closed network connection". Setting enable_connection_reuse=False per
request forces a fresh roundTripper with empty cachedConnections,
matching how the blocking tests already work.
…ve tests

- test_post_method: TrackMe rejects non-GET via HTTP/2 RST_STREAM causing timeout;
  skip gracefully instead of failing
- test_multiple_clients: explicitly pass enable_connection_reuse=False since these
  clients are created directly in the test body, bypassing the fixture wrapper
Move test files that hit tls.peet.ws / scrapfly.io to the live test suite
by adding `pytestmark = pytest.mark.live`, so they no longer run in the
unit-test workflow (which has no network access to those endpoints).

Expand the live-tests workflow to a Python 3.10–3.13 matrix (ubuntu-only,
since TrackMe requires Docker) matching the breadth of the unit-test matrix.

Affected test files:
- test_async_ja3.py
- test_force_http1.py
- test_frame_headers.py
- test_http2.py
- test_http2_fingerprint.py
- test_integration.py
- test_ja3_fingerprints.py
- test_ja4_fingerprints.py
TrackMe closes the TLS connection after every response. The global Go
transport caches the closed connection; the next test gets
"use of closed network connection".

Fix by injecting enable_connection_reuse=False via setdefault in:
- conftest.py cycletls_client (covers test_integration, test_http2_fingerprint, test_tls13)
- test_force_http1.py client fixture
- test_http2.py cycle fixture
- test_ja3_fingerprints.py cycle_client fixture
- test_ja4_fingerprints.py cycle_client fixture
- test_async_ja3.py: add enable_connection_reuse=False to 5 individual requests
  that were missing it (async tests can't use the wrapper pattern)
Tests now read the local-server endpoint from TLSFP_URL instead of the
TrackMe-specific TRACKME_URL. Default remains https://tls.peet.ws so
local devs without the local server still hit production. The renamed
fixture (trackme_url -> tlsfp_url) and updated docstrings reflect that
the local target is Danny-Dasilva/tlsfingerprint.com (the source of
tls.peet.ws), not the third-party TrackMe image.
…or live tests

Replaces the third-party TrackMe Docker setup that this PR originally added
with the user's own tlsfingerprint.com server (the open-source code that
powers tls.peet.ws). Both blocking-tests and live-tests workflows now:

  1. Check out Danny-Dasilva/tlsfingerprint.com@master into .tlsfingerprint-server/
  2. Generate a self-signed cert via openssl
  3. Patch config.example.json to disable mongo/log_to_db
  4. Build + start the container via the upstream docker-compose.yml
     (NET_ADMIN/NET_RAW caps, ports 80/443/443-udp)
  5. Wait up to 90s for /api/clean to respond
  6. Inject TLSFP_URL=https://localhost and SSL_CERT_FILE so tests trust
     the self-signed cert and target the local server
  7. Tear the container down at the end of the run

Tests still fall back to https://tls.peet.ws when TLSFP_URL is unset, so
local developers without Docker keep working against production. Adds
tests/README.md documenting the local-dev path and test markers.
@Danny-Dasilva Danny-Dasilva force-pushed the pr-43-tlsfingerprint-live branch from 2dca4e1 to 358888e Compare April 28, 2026 13:55
Comment thread .github/workflows/live-tests.yml Outdated
…_blocking too

The previous commit (7ad1723) only patched tests/test_ja4_fingerprints.py.
The blocking test module (tests/test_tlsfingerprint_blocking.py) still
contained 5 exact-equality JA4_r assertions, including
test_ja4r_tls12_fingerprint_exact_match which was the actual CI failure
(production tls.peet.ws emits 't12d128h2' for 12 ciphers + 8 extensions
unpadded; the local tlsfingerprint.com Docker emits the spec-padded
't12d1208h2'; equality fails).

Promote parse_ja4r and assert_ja4r_equivalent helpers from
test_ja4_fingerprints.py into tests/conftest.py as public names so they
can be reused. Both test files now import from conftest. Replace the
five JA4_r equality assertions in test_tlsfingerprint_blocking.py with
structural matcher calls.
The session-wide `enable_connection_reuse=False` wrapper was forcing
fresh TLS handshakes for *every* request, including ones against
httpbin.org. httpbin closes idle HTTP/1.1 connections aggressively, so
disabling reuse there causes "server closed idle connection" / EOF
errors on multi-request flows like:

  - test_http1_with_cookies (set cookie -> get cookie)
  - test_http1_with_redirects (redirect chain)

These two tests fail consistently across all 4 Python versions in the
Live Tests workflow.

Fix: only force `enable_connection_reuse=False` when the URL is the
local tlsfingerprint.com server (TLSFP_URL). Public endpoints
(httpbin.org) keep the default keep-alive behaviour.
@Danny-Dasilva Danny-Dasilva merged commit e12b39b into main May 7, 2026
22 checks passed
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.

3 participants