184 lines
4.9 KiB
Go
184 lines
4.9 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"termtap.dev/internal/model"
|
|
)
|
|
|
|
type staticErrListener struct {
|
|
err error
|
|
}
|
|
|
|
func (l *staticErrListener) Accept() (net.Conn, error) { return nil, l.err }
|
|
func (l *staticErrListener) Close() error { return nil }
|
|
func (l *staticErrListener) Addr() net.Addr {
|
|
return &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 8080}
|
|
}
|
|
|
|
func TestStartProxy_NilGuards(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
StartProxy(nil, make(chan model.Event, 1))
|
|
StartProxy(&model.ProxyServer{}, make(chan model.Event, 1))
|
|
StartProxy(&model.ProxyServer{Server: &http.Server{}}, make(chan model.Event, 1))
|
|
}
|
|
|
|
func TestStartProxy_EmitsStartingAndWarnWhenUntrustedCA(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
listenErr := errors.New("accept failed")
|
|
var ln net.Listener = &staticErrListener{err: listenErr}
|
|
|
|
ch := make(chan model.Event, 8)
|
|
ps := &model.ProxyServer{
|
|
Listener: &ln,
|
|
Server: &http.Server{Handler: http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})},
|
|
CAReady: true,
|
|
CATrusted: false,
|
|
CACreated: true,
|
|
CACertPath: "/tmp/test-ca.pem",
|
|
}
|
|
|
|
StartProxy(ps, ch)
|
|
|
|
events := drainEvents(t, ch, 3, time.Second)
|
|
if !hasType(events, model.EventTypeProxyStarting) {
|
|
t.Fatalf("missing %s event", model.EventTypeProxyStarting)
|
|
}
|
|
if !hasType(events, model.EventTypeWarn) {
|
|
t.Fatalf("missing %s event", model.EventTypeWarn)
|
|
}
|
|
if !containsBody(events, "generated HTTPS interception CA") {
|
|
t.Fatalf("expected generated-CA warning body, got events: %#v", events)
|
|
}
|
|
if !hasType(events, model.EventTypeFatal) {
|
|
t.Fatalf("missing %s event", model.EventTypeFatal)
|
|
}
|
|
}
|
|
|
|
func TestStartProxy_WarnBodyForExistingUntrustedCA(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
listenErr := errors.New("accept failed")
|
|
var ln net.Listener = &staticErrListener{err: listenErr}
|
|
|
|
ch := make(chan model.Event, 8)
|
|
ps := &model.ProxyServer{
|
|
Listener: &ln,
|
|
Server: &http.Server{Handler: http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})},
|
|
CAReady: true,
|
|
CATrusted: false,
|
|
CACreated: false,
|
|
CACertPath: "/tmp/test-ca.pem",
|
|
}
|
|
|
|
StartProxy(ps, ch)
|
|
|
|
events := drainEvents(t, ch, 3, time.Second)
|
|
if !containsBody(events, "HTTPS interception CA available at") {
|
|
t.Fatalf("expected existing-CA warning body, got events: %#v", events)
|
|
}
|
|
}
|
|
|
|
func TestStartProxy_NoCAWarningWhenNotReady(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
listenErr := errors.New("accept failed")
|
|
var ln net.Listener = &staticErrListener{err: listenErr}
|
|
|
|
ch := make(chan model.Event, 8)
|
|
ps := &model.ProxyServer{
|
|
Listener: &ln,
|
|
Server: &http.Server{Handler: http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})},
|
|
CAReady: false,
|
|
CATrusted: false,
|
|
}
|
|
|
|
StartProxy(ps, ch)
|
|
|
|
events := drainEvents(t, ch, 2, time.Second)
|
|
if !hasType(events, model.EventTypeProxyStarting) {
|
|
t.Fatalf("missing %s event", model.EventTypeProxyStarting)
|
|
}
|
|
if hasType(events, model.EventTypeWarn) {
|
|
t.Fatalf("unexpected %s event when CA is not ready: %#v", model.EventTypeWarn, events)
|
|
}
|
|
if !hasType(events, model.EventTypeFatal) {
|
|
t.Fatalf("missing %s event", model.EventTypeFatal)
|
|
}
|
|
}
|
|
|
|
func TestStartProxy_NoCAWarningWhenTrusted(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
listenErr := errors.New("accept failed")
|
|
var ln net.Listener = &staticErrListener{err: listenErr}
|
|
|
|
ch := make(chan model.Event, 8)
|
|
ps := &model.ProxyServer{
|
|
Listener: &ln,
|
|
Server: &http.Server{Handler: http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})},
|
|
CAReady: true,
|
|
CATrusted: true,
|
|
}
|
|
|
|
StartProxy(ps, ch)
|
|
|
|
events := drainEvents(t, ch, 2, time.Second)
|
|
if !hasType(events, model.EventTypeProxyStarting) {
|
|
t.Fatalf("missing %s event", model.EventTypeProxyStarting)
|
|
}
|
|
if hasType(events, model.EventTypeWarn) {
|
|
t.Fatalf("unexpected %s event when CA is already trusted: %#v", model.EventTypeWarn, events)
|
|
}
|
|
if !hasType(events, model.EventTypeFatal) {
|
|
t.Fatalf("missing %s event", model.EventTypeFatal)
|
|
}
|
|
}
|
|
|
|
func TestStartProxy_SwallowsErrServerClosed(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatalf("listen error = %v", err)
|
|
}
|
|
t.Cleanup(func() { _ = ln.Close() })
|
|
|
|
ch := make(chan model.Event, 8)
|
|
server := &http.Server{Handler: http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})}
|
|
ps := &model.ProxyServer{Listener: &ln, Server: server}
|
|
|
|
done := make(chan struct{})
|
|
go func() {
|
|
StartProxy(ps, ch)
|
|
close(done)
|
|
}()
|
|
|
|
shutdownCtx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
if err := server.Shutdown(shutdownCtx); err != nil {
|
|
t.Fatalf("shutdown error = %v", err)
|
|
}
|
|
|
|
select {
|
|
case <-done:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("timeout waiting for StartProxy to return")
|
|
}
|
|
|
|
events := drainEvents(t, ch, 1, time.Second)
|
|
if !hasType(events, model.EventTypeProxyStarting) {
|
|
t.Fatalf("missing %s event", model.EventTypeProxyStarting)
|
|
}
|
|
if hasType(events, model.EventTypeFatal) {
|
|
t.Fatalf("unexpected %s event", model.EventTypeFatal)
|
|
}
|
|
}
|