Files
netbird/client/internal/portforward/pinhole_test.go

117 lines
2.8 KiB
Go

//go:build !js
package portforward
import (
"context"
"errors"
"strings"
"testing"
"github.com/netbirdio/go-nat"
log "github.com/sirupsen/logrus"
"github.com/sirupsen/logrus/hooks/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// mockPinholeNAT is a gateway that also reports an IPv6 pinhole outcome, the
// shape a dual-stack gateway has.
type mockPinholeNAT struct {
*mockNAT
pinholeErr error
}
func (m *mockPinholeNAT) IPv6PinholeError() error {
return m.pinholeErr
}
func TestSetupLogsPinholeOutcome(t *testing.T) {
pinholeErr := errors.New("pcp ipv6: NOT_AUTHORIZED")
tests := []struct {
name string
pinholeErr error
mappingErr error
wantLevel log.Level
wantText string
}{
{
name: "an open pinhole is reported",
wantLevel: log.InfoLevel,
wantText: "IPv6 pinhole open",
},
{
name: "a failed pinhole is reported without failing the mapping",
// The IPv4 mapping is what the caller asked for, so the pinhole
// failure surfaces only in the log.
pinholeErr: pinholeErr,
wantLevel: log.WarnLevel,
wantText: pinholeErr.Error(),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gateway := &mockPinholeNAT{mockNAT: newMockNAT(), pinholeErr: tt.pinholeErr}
hook := stubGatewayDiscovery(t, gateway)
m := NewManager()
m.wgPort = 51820
_, mapping, err := m.setup(context.Background())
require.NoError(t, err)
require.NotNil(t, mapping)
entry := findEntry(hook, tt.wantText)
require.NotNil(t, entry, "no log entry mentioning %q", tt.wantText)
assert.Equal(t, tt.wantLevel, entry.Level)
})
}
t.Run("a failed mapping reports no pinhole outcome", func(t *testing.T) {
// Nothing opened the pinhole, so whatever it currently reports says
// nothing about this attempt.
gateway := &mockPinholeNAT{mockNAT: newMockNAT()}
gateway.addMappingErr = errors.New("gateway refused")
hook := stubGatewayDiscovery(t, gateway)
m := NewManager()
m.wgPort = 51820
_, _, err := m.setup(context.Background())
require.Error(t, err)
assert.Nil(t, findEntry(hook, "IPv6 pinhole"))
})
}
// stubGatewayDiscovery makes discovery return gateway and captures log output.
func stubGatewayDiscovery(t *testing.T, gateway nat.NAT) *test.Hook {
t.Helper()
orig := discoverGateway
discoverGateway = func(context.Context) (nat.NAT, error) { return gateway, nil }
t.Cleanup(func() { discoverGateway = orig })
hook := test.NewGlobal()
origLevel := log.GetLevel()
log.SetLevel(log.DebugLevel)
t.Cleanup(func() {
hook.Reset()
log.SetLevel(origLevel)
})
return hook
}
func findEntry(hook *test.Hook, substr string) *log.Entry {
for _, entry := range hook.AllEntries() {
if strings.Contains(entry.Message, substr) {
return entry
}
}
return nil
}