Files
yetone a8d798e2cb gateway, provider tests: the tests stay off the network (testenv.Offline in their TestMains) — after 555f5706 (#1016) routing reads a plan key's quota windows, and a test whose provider has a vendor's real base URL and a made-up key would ask the vendor in the background. Routing's own read (keyAllowance, allowances) was already stubbed in routing_test.go's init, and a survey with a dial guard found no plan-key dial, but it found others: every plugin host's first start fetched models.dev's catalog (65 dials in the gateway's plugin tests), and provider tests asked api.github.com (TestSubscriptionUsageEachAccount, TestSubscriptionUsageServesStale), chatgpt.com (TestCopilotQuotaWithoutEditorsSignIn), server.codeium.com (TestDevinSignedInOnlyInMagpie) and decide.test (TestModelTest) with made-up accounts; each passed whether the vendor answered or not. internal/testenv's Offline wraps http.DefaultTransport's DialContext (so http.DefaultClient and every transport cloned from it later): localhost, *.localhost, loopback and unspecified addresses are dialled, anything else is refused before it is looked up, said once per address on stderr ("testenv: a test asked models.dev:443, refused: tests stay off the network"). internal/gateway's TestMain and internal/provider's (kiro_main_test.go, in RunIn's setup) call it before the tests run. TestLocal (the address table) and TestOffline (a loopback httptest server answers through DefaultClient; models.dev is refused through it and through a Clone made after the call) pass -race -count=3. go test -race -tags nogui -timeout 30m ./internal/testenv ./internal/provider ./internal/gateway passes under a temp HOME (gateway 516 s). The gateway and provider test binaries were then run under sandbox-exec with every non-loopback network-outbound denied, with the kernel's Sandbox reports streamed: both PASS, Offline refused only the six addresses above, and the kernel reported no denial, so no transport outside DefaultTransport reaches the network either. go vet, gofmt -l, linux and windows (nogui) builds pass. Tests only.
2026-10-06 21:08:57 +08:00

63 lines
2.0 KiB
Go

package testenv
import (
"context"
"fmt"
"net"
"net/http"
"os"
"strings"
"sync"
)
// Offline keeps the package's tests off the network: http.DefaultTransport
// (and so http.DefaultClient, and every transport cloned from it after the
// call) dials this computer only — localhost, a name under .localhost, a
// loopback or unspecified address — and refuses anything else before it is
// looked up, saying so on stderr once per address. A test that wants a
// vendor serves it on loopback and points the request there (an
// httptest.Server, a Transport rewriting the host). Without it the tests
// of internal/gateway and internal/provider asked the real models.dev (its
// catalog, fetched as each plugin host started), api.github.com,
// chatgpt.com and Devin's server.codeium.com with made-up accounts, and a
// plan key with a vendor's real base URL would have its windows read for
// routing (#1016). A package calls it from its TestMain, before m.Run.
func Offline() {
t, ok := http.DefaultTransport.(*http.Transport)
if !ok {
return
}
dial := t.DialContext
if dial == nil {
dial = (&net.Dialer{}).DialContext
}
var mu sync.Mutex
said := map[string]bool{}
t.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
if local(addr) {
return dial(ctx, network, addr)
}
mu.Lock()
if !said[addr] {
said[addr] = true
fmt.Fprintf(os.Stderr, "testenv: a test asked %s, refused: tests stay off the network\n", addr)
}
mu.Unlock()
return nil, fmt.Errorf("testenv: %s is not this computer; tests stay off the network", addr)
}
}
// local says addr, a host:port, is this computer, without looking it up.
func local(addr string) bool {
host, _, err := net.SplitHostPort(addr)
if err != nil {
host = addr
}
host = strings.ToLower(strings.TrimSuffix(host, "."))
if host == "localhost" || strings.HasSuffix(host, ".localhost") {
return true
}
ip := net.ParseIP(strings.Trim(host, "[]"))
return ip != nil && (ip.IsLoopback() || ip.IsUnspecified())
}