mirror of
https://github.com/juanfont/headscale.git
synced 2026-01-23 02:24:10 +00:00
Disable the thelper linter which triggers on inline test closures in table-driven tests. These closures are intentionally not standalone helpers and don't benefit from t.Helper(). Also remove explanatory comments from nolint directives throughout the codebase as they add noise without providing significant value.
347 lines
7.8 KiB
Go
347 lines
7.8 KiB
Go
package mapper
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/juanfont/headscale/hscontrol/state"
|
|
"github.com/juanfont/headscale/hscontrol/types"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"tailscale.com/tailcfg"
|
|
)
|
|
|
|
func TestMapResponseBuilder_Basic(t *testing.T) {
|
|
cfg := &types.Config{
|
|
BaseDomain: "example.com",
|
|
LogTail: types.LogTailConfig{
|
|
Enabled: true,
|
|
},
|
|
}
|
|
|
|
mockState := &state.State{}
|
|
m := &mapper{
|
|
cfg: cfg,
|
|
state: mockState,
|
|
}
|
|
|
|
nodeID := types.NodeID(1)
|
|
|
|
builder := m.NewMapResponseBuilder(nodeID)
|
|
|
|
// Test basic builder creation
|
|
assert.NotNil(t, builder)
|
|
assert.Equal(t, nodeID, builder.nodeID)
|
|
assert.NotNil(t, builder.resp)
|
|
assert.False(t, builder.resp.KeepAlive)
|
|
assert.NotNil(t, builder.resp.ControlTime)
|
|
assert.WithinDuration(t, time.Now(), *builder.resp.ControlTime, time.Second)
|
|
}
|
|
|
|
func TestMapResponseBuilder_WithCapabilityVersion(t *testing.T) {
|
|
cfg := &types.Config{}
|
|
mockState := &state.State{}
|
|
m := &mapper{
|
|
cfg: cfg,
|
|
state: mockState,
|
|
}
|
|
|
|
nodeID := types.NodeID(1)
|
|
capVer := tailcfg.CapabilityVersion(42)
|
|
|
|
builder := m.NewMapResponseBuilder(nodeID).
|
|
WithCapabilityVersion(capVer)
|
|
|
|
assert.Equal(t, capVer, builder.capVer)
|
|
assert.False(t, builder.hasErrors())
|
|
}
|
|
|
|
func TestMapResponseBuilder_WithDomain(t *testing.T) {
|
|
domain := "test.example.com"
|
|
cfg := &types.Config{
|
|
ServerURL: "https://test.example.com",
|
|
BaseDomain: domain,
|
|
}
|
|
|
|
mockState := &state.State{}
|
|
m := &mapper{
|
|
cfg: cfg,
|
|
state: mockState,
|
|
}
|
|
|
|
nodeID := types.NodeID(1)
|
|
|
|
builder := m.NewMapResponseBuilder(nodeID).
|
|
WithDomain()
|
|
|
|
assert.Equal(t, domain, builder.resp.Domain)
|
|
assert.False(t, builder.hasErrors())
|
|
}
|
|
|
|
func TestMapResponseBuilder_WithCollectServicesDisabled(t *testing.T) {
|
|
cfg := &types.Config{}
|
|
mockState := &state.State{}
|
|
m := &mapper{
|
|
cfg: cfg,
|
|
state: mockState,
|
|
}
|
|
|
|
nodeID := types.NodeID(1)
|
|
|
|
builder := m.NewMapResponseBuilder(nodeID).
|
|
WithCollectServicesDisabled()
|
|
|
|
value, isSet := builder.resp.CollectServices.Get()
|
|
assert.True(t, isSet)
|
|
assert.False(t, value)
|
|
assert.False(t, builder.hasErrors())
|
|
}
|
|
|
|
func TestMapResponseBuilder_WithDebugConfig(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
logTailEnabled bool
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "LogTail enabled",
|
|
logTailEnabled: true,
|
|
expected: false, // DisableLogTail should be false when LogTail is enabled
|
|
},
|
|
{
|
|
name: "LogTail disabled",
|
|
logTailEnabled: false,
|
|
expected: true, // DisableLogTail should be true when LogTail is disabled
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
cfg := &types.Config{
|
|
LogTail: types.LogTailConfig{
|
|
Enabled: tt.logTailEnabled,
|
|
},
|
|
}
|
|
mockState := &state.State{}
|
|
m := &mapper{
|
|
cfg: cfg,
|
|
state: mockState,
|
|
}
|
|
|
|
nodeID := types.NodeID(1)
|
|
|
|
builder := m.NewMapResponseBuilder(nodeID).
|
|
WithDebugConfig()
|
|
|
|
require.NotNil(t, builder.resp.Debug)
|
|
assert.Equal(t, tt.expected, builder.resp.Debug.DisableLogTail)
|
|
assert.False(t, builder.hasErrors())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMapResponseBuilder_WithPeerChangedPatch(t *testing.T) {
|
|
cfg := &types.Config{}
|
|
mockState := &state.State{}
|
|
m := &mapper{
|
|
cfg: cfg,
|
|
state: mockState,
|
|
}
|
|
|
|
nodeID := types.NodeID(1)
|
|
changes := []*tailcfg.PeerChange{
|
|
{
|
|
NodeID: 123,
|
|
DERPRegion: 1,
|
|
},
|
|
{
|
|
NodeID: 456,
|
|
DERPRegion: 2,
|
|
},
|
|
}
|
|
|
|
builder := m.NewMapResponseBuilder(nodeID).
|
|
WithPeerChangedPatch(changes)
|
|
|
|
assert.Equal(t, changes, builder.resp.PeersChangedPatch)
|
|
assert.False(t, builder.hasErrors())
|
|
}
|
|
|
|
func TestMapResponseBuilder_WithPeersRemoved(t *testing.T) {
|
|
cfg := &types.Config{}
|
|
mockState := &state.State{}
|
|
m := &mapper{
|
|
cfg: cfg,
|
|
state: mockState,
|
|
}
|
|
|
|
nodeID := types.NodeID(1)
|
|
removedID1 := types.NodeID(123)
|
|
removedID2 := types.NodeID(456)
|
|
|
|
builder := m.NewMapResponseBuilder(nodeID).
|
|
WithPeersRemoved(removedID1, removedID2)
|
|
|
|
expected := []tailcfg.NodeID{
|
|
removedID1.NodeID(),
|
|
removedID2.NodeID(),
|
|
}
|
|
assert.Equal(t, expected, builder.resp.PeersRemoved)
|
|
assert.False(t, builder.hasErrors())
|
|
}
|
|
|
|
func TestMapResponseBuilder_ErrorHandling(t *testing.T) {
|
|
cfg := &types.Config{}
|
|
mockState := &state.State{}
|
|
m := &mapper{
|
|
cfg: cfg,
|
|
state: mockState,
|
|
}
|
|
|
|
nodeID := types.NodeID(1)
|
|
|
|
// Simulate an error in the builder
|
|
builder := m.NewMapResponseBuilder(nodeID)
|
|
builder.addError(assert.AnError)
|
|
|
|
// All subsequent calls should continue to work and accumulate errors
|
|
result := builder.
|
|
WithDomain().
|
|
WithCollectServicesDisabled().
|
|
WithDebugConfig()
|
|
|
|
assert.True(t, result.hasErrors())
|
|
assert.Len(t, result.errs, 1)
|
|
assert.Equal(t, assert.AnError, result.errs[0])
|
|
|
|
// Build should return the error
|
|
data, err := result.Build()
|
|
assert.Nil(t, data)
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestMapResponseBuilder_ChainedCalls(t *testing.T) {
|
|
domain := "chained.example.com"
|
|
cfg := &types.Config{
|
|
ServerURL: "https://chained.example.com",
|
|
BaseDomain: domain,
|
|
LogTail: types.LogTailConfig{
|
|
Enabled: false,
|
|
},
|
|
}
|
|
|
|
mockState := &state.State{}
|
|
m := &mapper{
|
|
cfg: cfg,
|
|
state: mockState,
|
|
}
|
|
|
|
nodeID := types.NodeID(1)
|
|
capVer := tailcfg.CapabilityVersion(99)
|
|
|
|
builder := m.NewMapResponseBuilder(nodeID).
|
|
WithCapabilityVersion(capVer).
|
|
WithDomain().
|
|
WithCollectServicesDisabled().
|
|
WithDebugConfig()
|
|
|
|
// Verify all fields are set correctly
|
|
assert.Equal(t, capVer, builder.capVer)
|
|
assert.Equal(t, domain, builder.resp.Domain)
|
|
value, isSet := builder.resp.CollectServices.Get()
|
|
assert.True(t, isSet)
|
|
assert.False(t, value)
|
|
assert.NotNil(t, builder.resp.Debug)
|
|
assert.True(t, builder.resp.Debug.DisableLogTail)
|
|
assert.False(t, builder.hasErrors())
|
|
}
|
|
|
|
func TestMapResponseBuilder_MultipleWithPeersRemoved(t *testing.T) {
|
|
cfg := &types.Config{}
|
|
mockState := &state.State{}
|
|
m := &mapper{
|
|
cfg: cfg,
|
|
state: mockState,
|
|
}
|
|
|
|
nodeID := types.NodeID(1)
|
|
removedID1 := types.NodeID(100)
|
|
removedID2 := types.NodeID(200)
|
|
|
|
// Test calling WithPeersRemoved multiple times
|
|
builder := m.NewMapResponseBuilder(nodeID).
|
|
WithPeersRemoved(removedID1).
|
|
WithPeersRemoved(removedID2)
|
|
|
|
// Second call should overwrite the first
|
|
expected := []tailcfg.NodeID{removedID2.NodeID()}
|
|
assert.Equal(t, expected, builder.resp.PeersRemoved)
|
|
assert.False(t, builder.hasErrors())
|
|
}
|
|
|
|
func TestMapResponseBuilder_EmptyPeerChangedPatch(t *testing.T) {
|
|
cfg := &types.Config{}
|
|
mockState := &state.State{}
|
|
m := &mapper{
|
|
cfg: cfg,
|
|
state: mockState,
|
|
}
|
|
|
|
nodeID := types.NodeID(1)
|
|
|
|
builder := m.NewMapResponseBuilder(nodeID).
|
|
WithPeerChangedPatch([]*tailcfg.PeerChange{})
|
|
|
|
assert.Empty(t, builder.resp.PeersChangedPatch)
|
|
assert.False(t, builder.hasErrors())
|
|
}
|
|
|
|
func TestMapResponseBuilder_NilPeerChangedPatch(t *testing.T) {
|
|
cfg := &types.Config{}
|
|
mockState := &state.State{}
|
|
m := &mapper{
|
|
cfg: cfg,
|
|
state: mockState,
|
|
}
|
|
|
|
nodeID := types.NodeID(1)
|
|
|
|
builder := m.NewMapResponseBuilder(nodeID).
|
|
WithPeerChangedPatch(nil)
|
|
|
|
assert.Nil(t, builder.resp.PeersChangedPatch)
|
|
assert.False(t, builder.hasErrors())
|
|
}
|
|
|
|
func TestMapResponseBuilder_MultipleErrors(t *testing.T) {
|
|
cfg := &types.Config{}
|
|
mockState := &state.State{}
|
|
m := &mapper{
|
|
cfg: cfg,
|
|
state: mockState,
|
|
}
|
|
|
|
nodeID := types.NodeID(1)
|
|
|
|
// Create a builder and add multiple errors
|
|
builder := m.NewMapResponseBuilder(nodeID)
|
|
builder.addError(assert.AnError)
|
|
builder.addError(assert.AnError)
|
|
builder.addError(nil) // This should be ignored
|
|
|
|
// All subsequent calls should continue to work
|
|
result := builder.
|
|
WithDomain().
|
|
WithCollectServicesDisabled()
|
|
|
|
assert.True(t, result.hasErrors())
|
|
assert.Len(t, result.errs, 2) // nil error should be ignored
|
|
|
|
// Build should return a multierr
|
|
data, err := result.Build()
|
|
assert.Nil(t, data)
|
|
require.Error(t, err)
|
|
|
|
// The error should contain information about multiple errors
|
|
assert.Contains(t, err.Error(), "multiple errors")
|
|
}
|