a4fa33d224
Release / release (push) Successful in 51s
API client: - NewClient now accepts tokenVersion (0 = auto-detect from token prefix) - tokenVersion stored on Client, used for 403 error hints - All callers pass cfg.NetBox.TokenVersion Tests added: - netbox: TokenVersion, NewClient auto-detect, explicit version, 403 v1 hint, 403 v2 no-hint, Authorization header verification - config: token_version preserved/auto-detected, defaults, missing file, invalid YAML, Path() - setup: save roundtrip, file permissions (0600), empty fields omitted, dir creation, full save→load roundtrip Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
165 lines
4.5 KiB
Go
165 lines
4.5 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 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)
|
|
}
|
|
}
|