mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-07-09 16:00:59 +00:00
Fix CI SMART and grant refresh tests
Refs Build and Test failures on pulse/v6-release.
This commit is contained in:
parent
70a20f6ac7
commit
7e26592f11
3 changed files with 76 additions and 48 deletions
|
|
@ -14,29 +14,24 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func TestCollectLocal_LogsStructuredContextOnDeviceCollectionFailure(t *testing.T) {
|
||||
func TestCollectLocal_LogsStructuredContextAndIdentityOnlyDiskOnDeviceCollectionFailure(t *testing.T) {
|
||||
stubLinuxSysfs(t,
|
||||
[]string{"sda"},
|
||||
map[string]string{
|
||||
"/sys/block/sda/size": "200\n",
|
||||
"/sys/block/sda/device/model": "Troubled Disk\n",
|
||||
},
|
||||
)
|
||||
|
||||
origRun := smartRunCommandOutput
|
||||
origLook := execLookPath
|
||||
origGOOS := runtimeGOOS
|
||||
origReadDir := readDir
|
||||
t.Cleanup(func() {
|
||||
smartRunCommandOutput = origRun
|
||||
execLookPath = origLook
|
||||
runtimeGOOS = origGOOS
|
||||
readDir = origReadDir
|
||||
})
|
||||
|
||||
runtimeGOOS = "linux"
|
||||
readDir = func(string) ([]os.DirEntry, error) { return nil, errors.New("sysfs unavailable") }
|
||||
execLookPath = func(string) (string, error) { return "smartctl", nil }
|
||||
smartRunCommandOutput = func(ctx context.Context, name string, args ...string) ([]byte, error) {
|
||||
if name == "lsblk" {
|
||||
data := lsblkJSON{Blockdevices: []lsblkDevice{
|
||||
{Name: "sda", Type: "disk", Tran: "sata", Subsystems: "block:scsi:pci"},
|
||||
}}
|
||||
out, _ := json.Marshal(data)
|
||||
return out, nil
|
||||
}
|
||||
if name == "smartctl" {
|
||||
return nil, errors.New("read failed")
|
||||
}
|
||||
|
|
@ -48,14 +43,22 @@ func TestCollectLocal_LogsStructuredContextOnDeviceCollectionFailure(t *testing.
|
|||
if err != nil {
|
||||
t.Fatalf("CollectSMARTLocal returned unexpected error: %v", err)
|
||||
}
|
||||
if len(results) != 0 {
|
||||
t.Fatalf("CollectSMARTLocal returned results despite command failure: %#v", results)
|
||||
if len(results) != 1 {
|
||||
t.Fatalf("expected failed physical disk to remain visible as identity-only, got %#v", results)
|
||||
}
|
||||
disk := results[0]
|
||||
if disk.Device != "sda" || disk.Model != "Troubled Disk" || disk.SizeBytes != 102400 {
|
||||
t.Fatalf("unexpected identity-only disk: %#v", disk)
|
||||
}
|
||||
if disk.Health != "UNKNOWN" || disk.Temperature != 0 || disk.Attributes != nil {
|
||||
t.Fatalf("identity-only disk must not fabricate SMART data: %#v", disk)
|
||||
}
|
||||
|
||||
for _, expected := range []string{
|
||||
`"component":"smartctl_collector"`,
|
||||
`"action":"collect_device_smart_failed"`,
|
||||
`"device":"/dev/sda"`,
|
||||
`"action":"identity_only_disk"`,
|
||||
`"action":"collect_local_complete"`,
|
||||
} {
|
||||
if !strings.Contains(logOutput.String(), expected) {
|
||||
|
|
|
|||
|
|
@ -467,31 +467,34 @@ func TestCollectSMARTLocalListDevicesError(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCollectSMARTLocalSkipsErrors(t *testing.T) {
|
||||
func TestCollectSMARTLocalReportsIdentityOnlyForProbeErrors(t *testing.T) {
|
||||
stubLinuxSysfs(t,
|
||||
[]string{"sda", "sdb"},
|
||||
map[string]string{
|
||||
"/sys/block/sda/size": "100\n",
|
||||
"/sys/block/sda/device/model": "Failed Disk\n",
|
||||
"/sys/block/sdb/size": "200\n",
|
||||
},
|
||||
)
|
||||
|
||||
origRun := smartRunCommandOutput
|
||||
origLook := execLookPath
|
||||
origNow := timeNow
|
||||
origReadDir := readDir
|
||||
t.Cleanup(func() {
|
||||
smartRunCommandOutput = origRun
|
||||
execLookPath = origLook
|
||||
timeNow = origNow
|
||||
readDir = origReadDir
|
||||
})
|
||||
|
||||
fixed := time.Date(2024, 1, 2, 3, 4, 5, 0, time.UTC)
|
||||
timeNow = func() time.Time { return fixed }
|
||||
readDir = func(string) ([]os.DirEntry, error) { return nil, errors.New("sysfs unavailable") }
|
||||
execLookPath = func(string) (string, error) { return "smartctl", nil }
|
||||
|
||||
smartRunCommandOutput = func(ctx context.Context, name string, args ...string) ([]byte, error) {
|
||||
if name == "lsblk" {
|
||||
return mockLsblkJSON(
|
||||
lsblkDevice{Name: "sda", Type: "disk", Tran: "sata", Subsystems: "block:scsi:pci"},
|
||||
lsblkDevice{Name: "sdb", Type: "disk", Tran: "sata", Subsystems: "block:scsi:pci"},
|
||||
), nil
|
||||
}
|
||||
if name == "smartctl" {
|
||||
if len(args) == 1 && args[0] == "--scan-open" {
|
||||
return nil, nil
|
||||
}
|
||||
device := args[len(args)-1]
|
||||
if strings.Contains(device, "sda") {
|
||||
return nil, errors.New("read error")
|
||||
|
|
@ -515,11 +518,32 @@ func TestCollectSMARTLocalSkipsErrors(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("CollectSMARTLocal error: %v", err)
|
||||
}
|
||||
if len(result) != 1 {
|
||||
t.Fatalf("expected 1 result, got %d", len(result))
|
||||
if len(result) != 2 {
|
||||
t.Fatalf("expected successful disk plus identity-only failed disk, got %#v", result)
|
||||
}
|
||||
if result[0].Device != "sdb" || !result[0].LastUpdated.Equal(fixed) {
|
||||
t.Fatalf("unexpected result: %#v", result[0])
|
||||
|
||||
byDevice := make(map[string]DiskSMART, len(result))
|
||||
for _, disk := range result {
|
||||
byDevice[disk.Device] = disk
|
||||
}
|
||||
|
||||
sdb, ok := byDevice["sdb"]
|
||||
if !ok {
|
||||
t.Fatalf("expected successful sdb result, got %#v", result)
|
||||
}
|
||||
if sdb.Health != "PASSED" || sdb.Temperature != 30 || !sdb.LastUpdated.Equal(fixed) {
|
||||
t.Fatalf("unexpected successful disk result: %#v", sdb)
|
||||
}
|
||||
|
||||
sda, ok := byDevice["sda"]
|
||||
if !ok {
|
||||
t.Fatalf("expected failed sda to remain visible as identity-only, got %#v", result)
|
||||
}
|
||||
if sda.Model != "Failed Disk" || sda.SizeBytes != 51200 {
|
||||
t.Fatalf("unexpected identity-only disk: %#v", sda)
|
||||
}
|
||||
if sda.Health != "UNKNOWN" || sda.Temperature != 0 || sda.Attributes != nil || !sda.LastUpdated.Equal(fixed) {
|
||||
t.Fatalf("identity-only disk must not fabricate SMART data: %#v", sda)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,23 @@ func TestGrantRefreshLoop_StartStop(t *testing.T) {
|
|||
svc.StopGrantRefresh() // Duplicate stop is a no-op.
|
||||
}
|
||||
|
||||
func setImmediateGrantRefreshLoop(t *testing.T, svc *Service) {
|
||||
t.Helper()
|
||||
|
||||
svc.SetRefreshHints(RefreshHints{IntervalSeconds: 60, JitterPercent: 0})
|
||||
svc.mu.RLock()
|
||||
loop := svc.grantRefresh
|
||||
svc.mu.RUnlock()
|
||||
if loop == nil {
|
||||
t.Fatal("expected grant refresh loop to be initialized")
|
||||
}
|
||||
|
||||
loop.mu.Lock()
|
||||
loop.refreshInterval = time.Millisecond
|
||||
loop.jitterPercent = 0
|
||||
loop.mu.Unlock()
|
||||
}
|
||||
|
||||
func TestGrantRefreshLoop_RefreshesGrant(t *testing.T) {
|
||||
setupTestPublicKey(t)
|
||||
const expectedClientVersion = "6.0.0-rc.1"
|
||||
|
|
@ -205,14 +222,8 @@ func TestGrantRefreshLoop_NonRevocation401KeepsActivation(t *testing.T) {
|
|||
t.Fatalf("RestoreActivation: %v", err)
|
||||
}
|
||||
|
||||
setImmediateGrantRefreshLoop(t, svc)
|
||||
svc.StartGrantRefresh(context.Background())
|
||||
svc.mu.RLock()
|
||||
loop := svc.grantRefresh
|
||||
svc.mu.RUnlock()
|
||||
loop.mu.Lock()
|
||||
loop.refreshInterval = time.Millisecond
|
||||
loop.jitterPercent = 0
|
||||
loop.mu.Unlock()
|
||||
|
||||
// Wait for at least one failed refresh attempt.
|
||||
deadline := time.After(5 * time.Second)
|
||||
|
|
@ -303,17 +314,7 @@ func TestGrantRefreshLoop_401ClearsActivation(t *testing.T) {
|
|||
t.Fatalf("RestoreActivation: %v", err)
|
||||
}
|
||||
|
||||
// Set a very short refresh interval so the loop fires immediately.
|
||||
svc.SetRefreshHints(RefreshHints{IntervalSeconds: 60, JitterPercent: 0.01})
|
||||
// Override the loop's interval directly for instant firing.
|
||||
svc.mu.RLock()
|
||||
loop := svc.grantRefresh
|
||||
svc.mu.RUnlock()
|
||||
loop.mu.Lock()
|
||||
loop.refreshInterval = time.Millisecond
|
||||
loop.jitterPercent = 0
|
||||
loop.mu.Unlock()
|
||||
|
||||
setImmediateGrantRefreshLoop(t, svc)
|
||||
// Start the refresh loop — it should hit 401 and self-exit after clearing state.
|
||||
svc.StartGrantRefresh(context.Background())
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue