test(e2e): add hosts module e2e test suite

Verify the static hosts mapping (HostMapper) overrides DNS: a mapped
hostname resolves to the configured IP and reaches the echo server,
while an unmapped hostname fails to resolve.
master
ginuerzh 2026-07-13 17:59:29 +08:00
parent 6746f556b7
commit 9355607ba7
2 changed files with 103 additions and 0 deletions

View File

@ -0,0 +1,85 @@
package e2e
import (
"context"
"fmt"
"io"
"os"
"testing"
"github.com/stretchr/testify/suite"
"github.com/testcontainers/testcontainers-go"
)
// HostsSuite verifies the static hosts mapping (HostMapper), which overrides
// DNS for matched hostnames. A mapped hostname resolves to the configured IP
// and reaches the echo server; an unmapped hostname fails to resolve.
type HostsSuite struct {
suite.Suite
ctx context.Context
echoC testcontainers.Container
echoIP string
gostC testcontainers.Container
}
func (s *HostsSuite) SetupSuite() {
s.ctx = context.Background()
echoC, err := RunEchoContainer(s.ctx, SharedNetworkName)
s.Require().NoError(err)
s.echoC = echoC
echoIP, err := echoC.ContainerIP(s.ctx)
s.Require().NoError(err)
s.echoIP = echoIP
cfg, err := RenderConfig("testdata/hosts/hosts.yaml", ConfigData{ServerAddr: s.echoIP})
s.Require().NoError(err)
defer os.Remove(cfg)
gostC, err := RunGostContainerWithPorts(s.ctx, SharedNetworkName, cfg, "8080/tcp")
s.Require().NoError(err)
s.gostC = gostC
}
func (s *HostsSuite) TearDownSuite() {
if s.gostC != nil {
s.gostC.Terminate(s.ctx)
}
if s.echoC != nil {
s.echoC.Terminate(s.ctx)
}
}
// proxyRequest sends a request to the given hostname through the gost proxy and
// returns the response body. A resolvable host that reaches the echo server
// returns "hello-gost"; an unresolvable host returns an empty body.
func (s *HostsSuite) proxyRequest(host string) string {
cmd := []string{
"curl", "-s",
"-x", "http://127.0.0.1:8080",
fmt.Sprintf("http://%s:5678", host),
}
_, out, err := s.gostC.Exec(s.ctx, cmd)
s.Require().NoError(err)
body, err := io.ReadAll(out)
s.Require().NoError(err)
return string(body)
}
// TestMappedHostname verifies that a hostname in the hosts mapping resolves to
// the configured IP and reaches the echo server.
func (s *HostsSuite) TestMappedHostname() {
s.Require().Contains(s.proxyRequest("echo.internal"), "hello-gost")
}
// TestUnmappedHostname verifies that a hostname absent from the mapping fails to
// resolve, so the request never reaches the echo server.
func (s *HostsSuite) TestUnmappedHostname() {
s.Require().NotContains(s.proxyRequest("nomap.internal"), "hello-gost")
}
func TestHostsSuite(t *testing.T) {
suite.Run(t, new(HostsSuite))
}

View File

@ -0,0 +1,18 @@
# HTTP proxy with a static hosts mapping. The made-up hostname echo.internal is
# mapped to the echo server's real IP, overriding DNS. A request to
# echo.internal resolves via the mapping and reaches the echo server; a request
# to an unmapped hostname fails to resolve and never connects.
services:
- name: proxy
addr: ":8080"
hosts: hosts-0
handler:
type: http
listener:
type: tcp
hosts:
- name: hosts-0
mappings:
- ip: {{.ServerAddr}}
hostname: echo.internal