mirror of
https://github.com/harivansh-afk/betterNAS.git
synced 2026-04-17 16:02:38 +00:00
Stabilize the node agent runtime loop.
Keep the NAS-side runtime bounded to the configured export path, make WebDAV and registration behavior env-driven, and add runtime coverage so the first storage loop can be verified locally. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
a7f85f4871
commit
273af4b0ab
14 changed files with 3294 additions and 36 deletions
404
apps/node-agent/internal/nodeagent/config_test.go
Normal file
404
apps/node-agent/internal/nodeagent/config_test.go
Normal file
|
|
@ -0,0 +1,404 @@
|
|||
package nodeagent
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestLoadConfigResolvesRelativeExportPathFromWorkspaceRoot(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
repoRoot := t.TempDir()
|
||||
agentDir := filepath.Join(repoRoot, "apps", "node-agent")
|
||||
|
||||
if err := os.MkdirAll(agentDir, 0o755); err != nil {
|
||||
t.Fatalf("create agent dir: %v", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(filepath.Join(repoRoot, "pnpm-workspace.yaml"), []byte("packages:\n - apps/*\n"), 0o644); err != nil {
|
||||
t.Fatalf("write workspace file: %v", err)
|
||||
}
|
||||
|
||||
cfg, err := loadConfig(
|
||||
mapLookup(map[string]string{
|
||||
exportPathEnvKey: ".state/nas/export",
|
||||
"BETTERNAS_NODE_MACHINE_ID": "nas-machine-id",
|
||||
"BETTERNAS_EXPORT_TAGS": "finder, photos, finder",
|
||||
"BETTERNAS_NODE_REGISTER_ENABLED": "true",
|
||||
"BETTERNAS_NODE_HEARTBEAT_ENABLED": "true",
|
||||
"BETTERNAS_CONTROL_PLANE_URL": "http://127.0.0.1:8081/",
|
||||
"BETTERNAS_CONTROL_PLANE_AUTH_TOKEN": "node-auth-token",
|
||||
"BETTERNAS_NODE_HEARTBEAT_INTERVAL": "45s",
|
||||
}),
|
||||
agentDir,
|
||||
"nas-box",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("load config: %v", err)
|
||||
}
|
||||
|
||||
expectedExportPath := filepath.Join(repoRoot, ".state", "nas", "export")
|
||||
if cfg.ExportPath != expectedExportPath {
|
||||
t.Fatalf("export path = %q, want %q", cfg.ExportPath, expectedExportPath)
|
||||
}
|
||||
|
||||
if cfg.ListenAddress != defaultListenAddress(defaultPort) {
|
||||
t.Fatalf("listen address = %q, want %q", cfg.ListenAddress, defaultListenAddress(defaultPort))
|
||||
}
|
||||
|
||||
if cfg.MachineID != "nas-machine-id" {
|
||||
t.Fatalf("machine id = %q, want nas-machine-id", cfg.MachineID)
|
||||
}
|
||||
|
||||
if cfg.DisplayName != "nas-machine-id" {
|
||||
t.Fatalf("display name = %q, want nas-machine-id", cfg.DisplayName)
|
||||
}
|
||||
|
||||
if cfg.DirectAddress != "http://localhost:8090" {
|
||||
t.Fatalf("direct address = %q, want loopback default", cfg.DirectAddress)
|
||||
}
|
||||
|
||||
if cfg.ExportLabel != "export" {
|
||||
t.Fatalf("export label = %q, want export", cfg.ExportLabel)
|
||||
}
|
||||
|
||||
if len(cfg.ExportTags) != 2 || cfg.ExportTags[0] != "finder" || cfg.ExportTags[1] != "photos" {
|
||||
t.Fatalf("export tags = %#v, want [finder photos]", cfg.ExportTags)
|
||||
}
|
||||
|
||||
if !cfg.RegisterEnabled {
|
||||
t.Fatalf("register enabled = false, want true")
|
||||
}
|
||||
|
||||
if !cfg.HeartbeatEnabled {
|
||||
t.Fatalf("heartbeat enabled = false, want true")
|
||||
}
|
||||
|
||||
if cfg.HeartbeatInterval != 45*time.Second {
|
||||
t.Fatalf("heartbeat interval = %s, want 45s", cfg.HeartbeatInterval)
|
||||
}
|
||||
|
||||
if cfg.ControlPlaneURL != "http://127.0.0.1:8081" {
|
||||
t.Fatalf("control plane url = %q, want trimmed url", cfg.ControlPlaneURL)
|
||||
}
|
||||
|
||||
if cfg.ControlPlaneToken != "node-auth-token" {
|
||||
t.Fatalf("control plane token = %q, want node-auth-token", cfg.ControlPlaneToken)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfigDefaultsRegistrationToDisabled(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg, err := loadConfig(
|
||||
mapLookup(map[string]string{
|
||||
exportPathEnvKey: ".state/nas/export",
|
||||
}),
|
||||
t.TempDir(),
|
||||
"nas-box",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("load config: %v", err)
|
||||
}
|
||||
|
||||
if cfg.RegisterEnabled {
|
||||
t.Fatal("register enabled = true, want false")
|
||||
}
|
||||
|
||||
if cfg.HeartbeatEnabled {
|
||||
t.Fatal("heartbeat enabled = true, want false")
|
||||
}
|
||||
|
||||
if cfg.ControlPlaneURL != "" {
|
||||
t.Fatalf("control plane url = %q, want empty", cfg.ControlPlaneURL)
|
||||
}
|
||||
|
||||
if cfg.MachineID != "nas-box" {
|
||||
t.Fatalf("machine id = %q, want nas-box", cfg.MachineID)
|
||||
}
|
||||
|
||||
if cfg.ListenAddress != defaultListenAddress(defaultPort) {
|
||||
t.Fatalf("listen address = %q, want %q", cfg.ListenAddress, defaultListenAddress(defaultPort))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfigDefaultsHeartbeatToDisabledEvenWhenRegistrationEnabled(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg, err := loadConfig(
|
||||
mapLookup(map[string]string{
|
||||
exportPathEnvKey: ".state/nas/export",
|
||||
"BETTERNAS_NODE_MACHINE_ID": "nas-machine-id",
|
||||
"BETTERNAS_NODE_REGISTER_ENABLED": "true",
|
||||
"BETTERNAS_CONTROL_PLANE_URL": "http://127.0.0.1:8081",
|
||||
}),
|
||||
t.TempDir(),
|
||||
"nas-box",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("load config: %v", err)
|
||||
}
|
||||
|
||||
if !cfg.RegisterEnabled {
|
||||
t.Fatal("register enabled = false, want true")
|
||||
}
|
||||
|
||||
if cfg.HeartbeatEnabled {
|
||||
t.Fatal("heartbeat enabled = true, want false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfigRejectsHeartbeatWithoutRegistration(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := loadConfig(
|
||||
mapLookup(map[string]string{
|
||||
exportPathEnvKey: ".state/nas/export",
|
||||
"BETTERNAS_NODE_HEARTBEAT_ENABLED": "true",
|
||||
}),
|
||||
t.TempDir(),
|
||||
"nas-box",
|
||||
)
|
||||
if err == nil {
|
||||
t.Fatal("expected heartbeat-only config to fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfigRequiresExportPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
values map[string]string
|
||||
}{
|
||||
{
|
||||
name: "missing",
|
||||
values: map[string]string{},
|
||||
},
|
||||
{
|
||||
name: "blank",
|
||||
values: map[string]string{
|
||||
exportPathEnvKey: " ",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := loadConfig(mapLookup(testCase.values), t.TempDir(), "nas-box")
|
||||
if err == nil {
|
||||
t.Fatal("expected missing export path to fail")
|
||||
}
|
||||
|
||||
if !strings.Contains(err.Error(), exportPathEnvKey) {
|
||||
t.Fatalf("error = %q, want %q", err.Error(), exportPathEnvKey)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfigDefaultsListenAddressToLoopback(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg, err := loadConfig(
|
||||
mapLookup(map[string]string{
|
||||
exportPathEnvKey: ".state/nas/export",
|
||||
"PORT": "9100",
|
||||
}),
|
||||
t.TempDir(),
|
||||
"nas-box",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("load config: %v", err)
|
||||
}
|
||||
|
||||
if cfg.ListenAddress != "127.0.0.1:9100" {
|
||||
t.Fatalf("listen address = %q, want 127.0.0.1:9100", cfg.ListenAddress)
|
||||
}
|
||||
|
||||
if cfg.DirectAddress != "http://localhost:9100" {
|
||||
t.Fatalf("direct address = %q, want http://localhost:9100", cfg.DirectAddress)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfigUsesExplicitWildcardListenAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg, err := loadConfig(
|
||||
mapLookup(map[string]string{
|
||||
exportPathEnvKey: ".state/nas/export",
|
||||
listenAddressEnvKey: ":9090",
|
||||
}),
|
||||
t.TempDir(),
|
||||
"nas-box",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("load config: %v", err)
|
||||
}
|
||||
|
||||
if cfg.ListenAddress != ":9090" {
|
||||
t.Fatalf("listen address = %q, want :9090", cfg.ListenAddress)
|
||||
}
|
||||
|
||||
if cfg.DirectAddress != "" {
|
||||
t.Fatalf("direct address = %q, want empty for wildcard listener", cfg.DirectAddress)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfigDerivesDirectAddressFromExplicitHostListenAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg, err := loadConfig(
|
||||
mapLookup(map[string]string{
|
||||
exportPathEnvKey: ".state/nas/export",
|
||||
listenAddressEnvKey: "192.0.2.10:9443",
|
||||
}),
|
||||
t.TempDir(),
|
||||
"nas-box",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("load config: %v", err)
|
||||
}
|
||||
|
||||
if cfg.DirectAddress != "http://192.0.2.10:9443" {
|
||||
t.Fatalf("direct address = %q, want http://192.0.2.10:9443", cfg.DirectAddress)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfigDoesNotDeriveDirectAddressFromWildcardHostListenAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
listenAddress string
|
||||
}{
|
||||
{
|
||||
name: "ipv4 wildcard",
|
||||
listenAddress: "0.0.0.0:9443",
|
||||
},
|
||||
{
|
||||
name: "ipv6 wildcard",
|
||||
listenAddress: "[::]:9443",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg, err := loadConfig(
|
||||
mapLookup(map[string]string{
|
||||
exportPathEnvKey: ".state/nas/export",
|
||||
listenAddressEnvKey: testCase.listenAddress,
|
||||
}),
|
||||
t.TempDir(),
|
||||
"nas-box",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("load config: %v", err)
|
||||
}
|
||||
|
||||
if cfg.DirectAddress != "" {
|
||||
t.Fatalf("direct address = %q, want empty for %q", cfg.DirectAddress, testCase.listenAddress)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfigRejectsInvalidListenAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := loadConfig(
|
||||
mapLookup(map[string]string{
|
||||
exportPathEnvKey: ".state/nas/export",
|
||||
listenAddressEnvKey: "localhost",
|
||||
}),
|
||||
t.TempDir(),
|
||||
"nas-box",
|
||||
)
|
||||
if err == nil {
|
||||
t.Fatal("expected invalid listen address to fail")
|
||||
}
|
||||
|
||||
if !strings.Contains(err.Error(), listenAddressEnvKey) {
|
||||
t.Fatalf("error = %q, want %q", err.Error(), listenAddressEnvKey)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfigAllowsRegistrationWithoutControlPlaneToken(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg, err := loadConfig(
|
||||
mapLookup(map[string]string{
|
||||
exportPathEnvKey: ".state/nas/export",
|
||||
"BETTERNAS_NODE_MACHINE_ID": "nas-machine-id",
|
||||
"BETTERNAS_NODE_REGISTER_ENABLED": "true",
|
||||
"BETTERNAS_CONTROL_PLANE_URL": "http://127.0.0.1:8081",
|
||||
}),
|
||||
t.TempDir(),
|
||||
"nas-box",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("load config: %v", err)
|
||||
}
|
||||
|
||||
if cfg.ControlPlaneToken != "" {
|
||||
t.Fatalf("control-plane token = %q, want empty", cfg.ControlPlaneToken)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfigRejectsRegistrationWithoutMachineID(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := loadConfig(
|
||||
mapLookup(map[string]string{
|
||||
exportPathEnvKey: ".state/nas/export",
|
||||
"BETTERNAS_NODE_REGISTER_ENABLED": "true",
|
||||
"BETTERNAS_CONTROL_PLANE_URL": "http://127.0.0.1:8081",
|
||||
}),
|
||||
t.TempDir(),
|
||||
"nas-box",
|
||||
)
|
||||
if err == nil {
|
||||
t.Fatal("expected missing machine id to fail")
|
||||
}
|
||||
|
||||
if !strings.Contains(err.Error(), "BETTERNAS_NODE_MACHINE_ID") {
|
||||
t.Fatalf("error = %q, want missing-machine-id message", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfigRejectsRegistrationWithoutControlPlaneURL(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := loadConfig(
|
||||
mapLookup(map[string]string{
|
||||
exportPathEnvKey: ".state/nas/export",
|
||||
"BETTERNAS_NODE_MACHINE_ID": "nas-machine-id",
|
||||
"BETTERNAS_NODE_REGISTER_ENABLED": "true",
|
||||
}),
|
||||
t.TempDir(),
|
||||
"nas-box",
|
||||
)
|
||||
if err == nil {
|
||||
t.Fatal("expected missing control-plane url to fail")
|
||||
}
|
||||
|
||||
if !strings.Contains(err.Error(), "BETTERNAS_CONTROL_PLANE_URL") {
|
||||
t.Fatalf("error = %q, want missing-control-plane-url message", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func mapLookup(values map[string]string) envLookup {
|
||||
return func(key string) (string, bool) {
|
||||
value, ok := values[key]
|
||||
return value, ok
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue