Files
ssh-netbox-wrapper/internal/setup/wizard_test.go
T
Sebastian Unterschütz d127a3b957
Release / release (push) Successful in 49s
feat: enhance host resolution, filtering, and cache management
- **Strategies**: Add resolver strategy input validation and parsing in setup wizard. Support comma-separated input with known strategy mapping.
- **Client**: Extend Search and SearchAll to include kind and tag filters. Add pagination for full cache refresh handling large datasets.
- **Cache**: Introduce `RecentlyUsed` and `MarkUsed`. Persist `LastUsed` timestamps for entries.
- **TUI**: Add recent hosts view, tag/kind filters, and inline editor for user/port override.
- **Tests**: Comprehensive unit tests for new features, including strategy validation, cache behavior, and client filtering.
- **Docs**: Update README with new TUI features and cache subcommands.
2026-05-23 17:06:24 +02:00

227 lines
6.1 KiB
Go

package setup
import (
"os"
"path/filepath"
"strings"
"testing"
"git.zb-server.de/Sebi/ssh-netbox-wrapper/internal/config"
)
func TestSave_WritesFile(t *testing.T) {
dir := t.TempDir()
orig := os.Getenv("XDG_CONFIG_HOME")
os.Setenv("XDG_CONFIG_HOME", dir)
defer os.Setenv("XDG_CONFIG_HOME", orig)
cfg := config.Config{
NetBox: config.NetBoxConfig{
URL: "https://netbox.example.com",
Token: "nbt_abc123",
TokenVersion: 2,
},
SSH: config.SSHConfig{DefaultUser: "admin"},
Resolver: config.ResolverConfig{
Strategies: []string{"primary_ip", "management_subnet"},
ManagementSubnets: []string{"10.0.0.0/8"},
},
Cache: config.CacheConfig{TTL: 3600},
}
if err := save(cfg); err != nil {
t.Fatalf("save: %v", err)
}
data, err := os.ReadFile(filepath.Join(dir, "netssh.yaml"))
if err != nil {
t.Fatalf("reading saved file: %v", err)
}
content := string(data)
for _, want := range []string{
`"https://netbox.example.com"`,
`"nbt_abc123"`,
`token_version: 2`,
`- primary_ip`,
`- management_subnet`,
`- 10.0.0.0/8`,
`ttl: 3600`,
`"admin"`,
} {
if !strings.Contains(content, want) {
t.Errorf("saved config missing %q\nfull content:\n%s", want, content)
}
}
}
func TestSave_FilePermissions(t *testing.T) {
dir := t.TempDir()
orig := os.Getenv("XDG_CONFIG_HOME")
os.Setenv("XDG_CONFIG_HOME", dir)
defer os.Setenv("XDG_CONFIG_HOME", orig)
if err := save(config.Config{
NetBox: config.NetBoxConfig{URL: "http://x", Token: "t", TokenVersion: 1},
Cache: config.CacheConfig{TTL: 60},
}); err != nil {
t.Fatalf("save: %v", err)
}
info, err := os.Stat(filepath.Join(dir, "netssh.yaml"))
if err != nil {
t.Fatalf("stat: %v", err)
}
if perm := info.Mode().Perm(); perm != 0o600 {
t.Errorf("file permissions: got %o, want 600", perm)
}
}
func TestSave_OmitsEmptyOptionalFields(t *testing.T) {
dir := t.TempDir()
orig := os.Getenv("XDG_CONFIG_HOME")
os.Setenv("XDG_CONFIG_HOME", dir)
defer os.Setenv("XDG_CONFIG_HOME", orig)
cfg := config.Config{
NetBox: config.NetBoxConfig{URL: "http://x", Token: "t", TokenVersion: 1},
Cache: config.CacheConfig{TTL: 60},
// No DefaultUser, no ManagementSubnets, no InterfaceName
}
if err := save(cfg); err != nil {
t.Fatalf("save: %v", err)
}
data, _ := os.ReadFile(filepath.Join(dir, "netssh.yaml"))
content := string(data)
for _, absent := range []string{"default_user", "management_subnets", "interface_name"} {
if strings.Contains(content, absent) {
t.Errorf("config should not contain %q when field is empty\nfull content:\n%s", absent, content)
}
}
}
func TestSave_CreatesConfigDir(t *testing.T) {
dir := filepath.Join(t.TempDir(), "does", "not", "exist")
orig := os.Getenv("XDG_CONFIG_HOME")
os.Setenv("XDG_CONFIG_HOME", dir)
defer os.Setenv("XDG_CONFIG_HOME", orig)
if err := save(config.Config{
NetBox: config.NetBoxConfig{URL: "http://x", Token: "t", TokenVersion: 1},
Cache: config.CacheConfig{TTL: 60},
}); err != nil {
t.Fatalf("save should create missing directories: %v", err)
}
}
func TestParseStrategies(t *testing.T) {
tests := []struct {
in string
want []string
}{
{"primary_ip", []string{"primary_ip"}},
{"management_subnet, primary_ip", []string{"management_subnet", "primary_ip"}},
{"primary_ip,management_subnet,interface_name", []string{"primary_ip", "management_subnet", "interface_name"}},
{" primary_ip , management_subnet ", []string{"primary_ip", "management_subnet"}},
{"", nil},
{" , ", nil},
}
for _, tt := range tests {
got := parseStrategies(tt.in)
if len(got) != len(tt.want) {
t.Errorf("parseStrategies(%q): got %v, want %v", tt.in, got, tt.want)
continue
}
for i := range got {
if got[i] != tt.want[i] {
t.Errorf("parseStrategies(%q)[%d]: got %q, want %q", tt.in, i, got[i], tt.want[i])
}
}
}
}
func TestParseStrategies_PreservesOrder(t *testing.T) {
got := parseStrategies("interface_name, management_subnet, primary_ip")
want := []string{"interface_name", "management_subnet", "primary_ip"}
for i, s := range got {
if s != want[i] {
t.Errorf("order not preserved at [%d]: got %q, want %q", i, s, want[i])
}
}
}
func TestValidateStrategies_Valid(t *testing.T) {
cases := []string{
"primary_ip",
"management_subnet, primary_ip",
"interface_name, management_subnet, primary_ip",
}
for _, c := range cases {
if err := validateStrategies(c); err != nil {
t.Errorf("validateStrategies(%q) should be valid, got: %v", c, err)
}
}
}
func TestValidateStrategies_Invalid(t *testing.T) {
cases := []string{
"",
"unknown_strategy",
"primary_ip, typo",
}
for _, c := range cases {
if err := validateStrategies(c); err == nil {
t.Errorf("validateStrategies(%q) should return an error", c)
}
}
}
func TestSave_RoundtripViaLoad(t *testing.T) {
dir := t.TempDir()
orig := os.Getenv("XDG_CONFIG_HOME")
os.Setenv("XDG_CONFIG_HOME", dir)
defer os.Setenv("XDG_CONFIG_HOME", orig)
original := config.Config{
NetBox: config.NetBoxConfig{
URL: "https://netbox.zb-server.de",
Token: "nbt_supersecret",
TokenVersion: 2,
},
SSH: config.SSHConfig{DefaultUser: "root"},
Resolver: config.ResolverConfig{
Strategies: []string{"primary_ip"},
ManagementSubnets: []string{"192.168.0.0/16"},
InterfaceName: "eth0",
},
Cache: config.CacheConfig{TTL: 7200},
}
if err := save(original); err != nil {
t.Fatalf("save: %v", err)
}
loaded, err := config.Load()
if err != nil {
t.Fatalf("Load after save: %v", err)
}
if loaded.NetBox.URL != original.NetBox.URL {
t.Errorf("URL: got %q, want %q", loaded.NetBox.URL, original.NetBox.URL)
}
if loaded.NetBox.Token != original.NetBox.Token {
t.Errorf("Token: got %q, want %q", loaded.NetBox.Token, original.NetBox.Token)
}
if loaded.NetBox.TokenVersion != original.NetBox.TokenVersion {
t.Errorf("TokenVersion: got %d, want %d", loaded.NetBox.TokenVersion, original.NetBox.TokenVersion)
}
if loaded.SSH.DefaultUser != original.SSH.DefaultUser {
t.Errorf("DefaultUser: got %q, want %q", loaded.SSH.DefaultUser, original.SSH.DefaultUser)
}
if loaded.Cache.TTL != original.Cache.TTL {
t.Errorf("TTL: got %d, want %d", loaded.Cache.TTL, original.Cache.TTL)
}
}