Files
gost/tests/e2e
ginuerzh 94ccb5c706 tests(e2e): a quic tunnel must survive aborted downloads
Data the peer already sent stays unread in the stream and keeps
occupying the connection level receive window of the quic session. When
the local app aborts a download, that data is never consumed, so the
credit is only returned when the read side of the stream is cancelled.

Without the cancellation the window drains. The initial stream window is
512 KB and the initial connection window is 768 KB, so a few aborted
downloads are enough to exhaust it. Once that happens the peer cannot
send data on any stream of the session: new requests still reach the
server, the server still acknowledges, there is no idle timeout and no
error in the log, and the client waits forever for a response. Only a
new session clears it.

The test aborts three downloads of a body that is much larger than the
flow control window, and then checks that the tunnel still forwards. The
client reads very slowly, so the data piles up in the connection that
carries it.

Verified against go-gost/x#135: with the fix reverted the test fails
with a timeout on the request that follows the aborted downloads, which
is the reported symptom.
2026-10-21 10:42:56 +00:00
..

End-to-End Tests

Integration tests that spin up real gost instances inside Docker containers and verify protocol behavior over the network.

Prerequisites

  • Docker (running daemon)
  • Go toolchain (for compiling the gost binary under test)

Running

From the repository root:

# Run all e2e tests
go test ./tests/e2e/ -v -timeout 10m

# Run a specific test suite
go test ./tests/e2e/ -v -run TestShadowsocksSuite -timeout 5m
go test ./tests/e2e/ -v -run TestParallelSelectorSuite -timeout 5m

# Use a pre-built gost binary (skips compilation)
go test ./tests/e2e/ -v -gost-bin /path/to/gost

Architecture

tests/e2e/
├── Dockerfile                  # Shared base image (Alpine + curl, python3, etc.)
├── main_test.go                # TestMain: compiles gost, creates Docker network
├── utils.go                    # Helpers: container lifecycle, config rendering
├── scripts/
│   ├── tcp_echo.py             # HTTP echo server (responds with "hello-gost")
│   └── udp_echo.py             # UDP echo server (reflects payloads)
├── testdata/                   # config files or data files for running cases
├── shadowsocks_test.go         # Shadowsocks protocol tests
└── selector_test.go            # Node selector tests (round-robin, fifo, backup, parallel)

How it works

  1. TestMain (main_test.go) compiles the gost binary from ../../cmd/gost (unless -gost-bin is provided) and creates a shared Docker network for all containers.
  2. Each test suite starts echo server containers (TCP/UDP), then launches separate gost containers for server and client roles.
  3. Client configs use Go template syntax ({{.ServerAddr}}) so the server address is injected at runtime.
  4. Tests verify end-to-end connectivity by sending traffic through the gost proxy chain and checking that the echo server responds correctly.

Tips

  • Increase -timeout for CI or slow networks. Container image builds on first run take extra time.
  • Use -gost-bin to avoid recompiling when iterating on tests locally.
  • Add -v to see container log output on failure.
  • RunGostContainer will wait for exposedPorts automatically, but it's not reliable for udp ports. So, you should check the readiness of udp ports inside cases.