mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-20 18:55:40 +00:00
b707512e38
golangci-lint run ./... failed on ~190 pre-existing errcheck violations and 5 unformatted files, burying any new regression in noise. Fix all of them: - Test files that hand-rolled mock-mode set/restore (vmware, truenas, and friends) now use the canonical setMockModeForTest/testutil.SetMockMode helper instead of drift copies that ignored SetEnabled errors. - internal/mock and internal/monitoring tests get package-local mustSetEnabled/mustSetMockEnabled/mustSetMonitorMockMode helpers that fail the test on toggle errors. - pkg/auth/sqlite_manager.go, pkg/metrics/store.go, pkg/server/server.go: rollbacks in defers use the explicit-discard idiom, migration renames and rollup commits log failures, the hosted reaper goroutine logs an error exit, shutdown mock-disable logs failures. - Remaining test sites check errors with t.Fatalf/t.Errorf or explicitly discard best-effort calls (restore-chmods, handler-closure unmarshals) per existing repo style. - gofmt: internal/api/maintenance_verification.go, internal/ai/demo.go and three findings test files. Only dupl findings remain (44 pre-existing production-code duplication pairs) — those need real refactors, not mechanical fixes. Full test suites pass for every touched package.
128 lines
3.8 KiB
Go
128 lines
3.8 KiB
Go
package config
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/crypto"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/mock"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestSaveNodesConfig_Scenarios(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
cp := NewConfigPersistence(tempDir)
|
|
nodesFile := filepath.Join(tempDir, "nodes.enc")
|
|
|
|
// 1. Mock mode enabled
|
|
t.Run("MockModeEnabled", func(t *testing.T) {
|
|
if err := mock.SetEnabled(true); err != nil {
|
|
t.Fatalf("enable mock mode: %v", err)
|
|
}
|
|
defer func() {
|
|
if err := mock.SetEnabled(false); err != nil {
|
|
t.Errorf("disable mock mode: %v", err)
|
|
}
|
|
}()
|
|
|
|
err := cp.SaveNodesConfig([]PVEInstance{{Host: "test"}}, nil, nil)
|
|
assert.NoError(t, err)
|
|
|
|
// Verify file NOT created
|
|
_, err = os.Stat(nodesFile)
|
|
assert.True(t, os.IsNotExist(err))
|
|
})
|
|
|
|
// 2. Blocked Wipe branch
|
|
t.Run("BlockedWipe", func(t *testing.T) {
|
|
// Create a non-empty config first
|
|
initialNodes := []PVEInstance{{Host: "existing"}}
|
|
validData, _ := json.Marshal(NodesConfig{PVEInstances: initialNodes})
|
|
_ = os.WriteFile(nodesFile, validData, 0600)
|
|
|
|
// Attempt to save empty config with allowEmpty=false (default for SaveNodesConfig wrapper)
|
|
err := cp.SaveNodesConfig([]PVEInstance{}, nil, nil)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "refusing to save empty nodes config")
|
|
|
|
// Verify file still has original content
|
|
data, _ := os.ReadFile(nodesFile)
|
|
var cfg NodesConfig
|
|
_ = json.Unmarshal(data, &cfg)
|
|
assert.Equal(t, "existing", cfg.PVEInstances[0].Host)
|
|
})
|
|
|
|
// 3. Blocked Wipe with Crypto Decrypt Failure
|
|
t.Run("BlockedWipe_DecryptFailure", func(t *testing.T) {
|
|
cm, _ := crypto.NewCryptoManagerAt(tempDir)
|
|
cp.crypto = cm
|
|
|
|
// Write invalid encrypted data
|
|
_ = os.WriteFile(nodesFile, []byte("invalid-encrypted-data-too-short"), 0600)
|
|
|
|
err := cp.SaveNodesConfig([]PVEInstance{}, nil, nil)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "existing nodes config is not decryptable")
|
|
})
|
|
|
|
// 4. Blocked Wipe with JSON Parse Failure
|
|
t.Run("BlockedWipe_ParseFailure", func(t *testing.T) {
|
|
cp.crypto = nil
|
|
_ = os.WriteFile(nodesFile, []byte("not json"), 0600)
|
|
|
|
err := cp.SaveNodesConfig([]PVEInstance{}, nil, nil)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "existing nodes config is not parseable")
|
|
})
|
|
|
|
// 5. Success with Backups
|
|
t.Run("SuccessWithBackups", func(t *testing.T) {
|
|
_ = os.WriteFile(nodesFile, []byte("{}"), 0600) // Initial file
|
|
|
|
err := cp.SaveNodesConfig([]PVEInstance{{Host: "new"}}, nil, nil)
|
|
assert.NoError(t, err)
|
|
|
|
// Check for backup file
|
|
_, err = os.Stat(nodesFile + ".backup")
|
|
assert.NoError(t, err)
|
|
|
|
// Check for timestamped backup
|
|
matches, _ := filepath.Glob(nodesFile + ".backup-*")
|
|
assert.NotEmpty(t, matches)
|
|
})
|
|
|
|
// 6. Backup Rename Error
|
|
t.Run("BackupRenameError", func(t *testing.T) {
|
|
_ = os.WriteFile(nodesFile, []byte("{}"), 0600)
|
|
|
|
mfs := &mockFSRenameSpecific{FileSystem: defaultFileSystem{}, failPattern: ".backup"}
|
|
cp.SetFileSystem(mfs)
|
|
|
|
err := cp.SaveNodesConfig([]PVEInstance{{Host: "new"}}, nil, nil)
|
|
assert.NoError(t, err) // Should succeed despite backup error
|
|
})
|
|
|
|
// 7. Backup Write Error
|
|
t.Run("BackupWriteError", func(t *testing.T) {
|
|
_ = os.WriteFile(nodesFile, []byte("{}"), 0600)
|
|
|
|
mfs := &mockFSWriteSpecific{FileSystem: defaultFileSystem{}, failPattern: ".backup"}
|
|
cp.SetFileSystem(mfs)
|
|
|
|
err := cp.SaveNodesConfig([]PVEInstance{{Host: "new"}}, nil, nil)
|
|
assert.NoError(t, err) // Should succeed despite backup write error (logged warning)
|
|
})
|
|
|
|
// 8. Mkdir Error
|
|
t.Run("MkdirError", func(t *testing.T) {
|
|
mfs := &mockFSError{FileSystem: defaultFileSystem{}, mkdirError: os.ErrPermission}
|
|
cp.SetFileSystem(mfs)
|
|
|
|
err := cp.SaveNodesConfig([]PVEInstance{{Host: "new"}}, nil, nil)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "permission denied")
|
|
})
|
|
}
|