mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-28 11:30:15 +00:00
731 lines
18 KiB
Go
731 lines
18 KiB
Go
package smartctl
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
)
|
|
|
|
func TestDetectDiskType(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
data smartctlJSON
|
|
expected string
|
|
}{
|
|
{
|
|
name: "NVMe protocol",
|
|
data: smartctlJSON{
|
|
Device: struct {
|
|
Name string `json:"name"`
|
|
Type string `json:"type"`
|
|
Protocol string `json:"protocol"`
|
|
}{
|
|
Protocol: "NVMe",
|
|
},
|
|
},
|
|
expected: "nvme",
|
|
},
|
|
{
|
|
name: "SAS protocol",
|
|
data: smartctlJSON{
|
|
Device: struct {
|
|
Name string `json:"name"`
|
|
Type string `json:"type"`
|
|
Protocol string `json:"protocol"`
|
|
}{
|
|
Protocol: "SAS",
|
|
},
|
|
},
|
|
expected: "sas",
|
|
},
|
|
{
|
|
name: "SATA protocol",
|
|
data: smartctlJSON{
|
|
Device: struct {
|
|
Name string `json:"name"`
|
|
Type string `json:"type"`
|
|
Protocol string `json:"protocol"`
|
|
}{
|
|
Protocol: "ATA",
|
|
},
|
|
},
|
|
expected: "sata",
|
|
},
|
|
{
|
|
name: "NVMe device type fallback",
|
|
data: smartctlJSON{
|
|
Device: struct {
|
|
Name string `json:"name"`
|
|
Type string `json:"type"`
|
|
Protocol string `json:"protocol"`
|
|
}{
|
|
Type: "nvme",
|
|
Protocol: "",
|
|
},
|
|
},
|
|
expected: "nvme",
|
|
},
|
|
{
|
|
name: "Empty defaults to sata",
|
|
data: smartctlJSON{
|
|
Device: struct {
|
|
Name string `json:"name"`
|
|
Type string `json:"type"`
|
|
Protocol string `json:"protocol"`
|
|
}{},
|
|
},
|
|
expected: "sata",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := detectDiskType(tt.data)
|
|
if result != tt.expected {
|
|
t.Errorf("detectDiskType() = %q, want %q", result, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFormatWWN(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
naa uint64
|
|
oui uint64
|
|
id uint64
|
|
expected string
|
|
}{
|
|
{
|
|
name: "Standard WWN",
|
|
naa: 5,
|
|
oui: 0x000c50,
|
|
id: 0x123456789abc,
|
|
expected: "5-c50-123456789abc",
|
|
},
|
|
{
|
|
name: "Zero values",
|
|
naa: 0,
|
|
oui: 0,
|
|
id: 0,
|
|
expected: "0-0-0",
|
|
},
|
|
{
|
|
name: "Simple values",
|
|
naa: 5,
|
|
oui: 1234,
|
|
id: 5678,
|
|
expected: "5-4d2-162e",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := formatWWN(tt.naa, tt.oui, tt.id)
|
|
if result != tt.expected {
|
|
t.Errorf("formatWWN(%d, %d, %d) = %q, want %q", tt.naa, tt.oui, tt.id, result, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDiskSMARTStruct(t *testing.T) {
|
|
// Verify struct can be properly created
|
|
disk := DiskSMART{
|
|
Device: "sda",
|
|
Model: "Samsung SSD 870 EVO 1TB",
|
|
Serial: "S123456789",
|
|
WWN: "5-abc-123",
|
|
Type: "sata",
|
|
Temperature: 35,
|
|
Health: "PASSED",
|
|
Standby: false,
|
|
}
|
|
|
|
if disk.Device != "sda" {
|
|
t.Errorf("unexpected Device: %s", disk.Device)
|
|
}
|
|
if disk.Temperature != 35 {
|
|
t.Errorf("unexpected Temperature: %d", disk.Temperature)
|
|
}
|
|
if disk.Health != "PASSED" {
|
|
t.Errorf("unexpected Health: %s", disk.Health)
|
|
}
|
|
}
|
|
|
|
func TestSmartctlJSONParsing(t *testing.T) {
|
|
// Test parsing the smartctlJSON struct
|
|
data := smartctlJSON{
|
|
ModelName: "Samsung SSD 870 EVO 1TB",
|
|
SerialNumber: "S123456789",
|
|
}
|
|
data.Device.Name = "/dev/sda"
|
|
data.Device.Protocol = "ATA"
|
|
data.SmartStatus = &struct {
|
|
Passed bool `json:"passed"`
|
|
}{Passed: true}
|
|
data.Temperature.Current = 35
|
|
|
|
if data.ModelName != "Samsung SSD 870 EVO 1TB" {
|
|
t.Errorf("unexpected ModelName: %s", data.ModelName)
|
|
}
|
|
if data.SmartStatus == nil || data.SmartStatus.Passed != true {
|
|
t.Errorf("expected Passed to be true")
|
|
}
|
|
if data.Temperature.Current != 35 {
|
|
t.Errorf("unexpected Temperature: %d", data.Temperature.Current)
|
|
}
|
|
}
|
|
|
|
func TestLinuxSMARTSkipReason(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
device lsblkDevice
|
|
skip bool
|
|
}{
|
|
{
|
|
name: "physical sata disk",
|
|
device: lsblkDevice{
|
|
Name: "sda",
|
|
Type: "disk",
|
|
Model: "Samsung SSD 870 EVO",
|
|
Vendor: "ATA",
|
|
},
|
|
skip: false,
|
|
},
|
|
{
|
|
name: "zfs zvol device",
|
|
device: lsblkDevice{
|
|
Name: "zd16",
|
|
Type: "disk",
|
|
},
|
|
skip: true,
|
|
},
|
|
{
|
|
name: "device mapper device",
|
|
device: lsblkDevice{
|
|
Name: "dm-0",
|
|
Type: "disk",
|
|
},
|
|
skip: true,
|
|
},
|
|
{
|
|
name: "virtio transport",
|
|
device: lsblkDevice{
|
|
Name: "vda",
|
|
Type: "disk",
|
|
Tran: "virtio",
|
|
},
|
|
skip: true,
|
|
},
|
|
{
|
|
name: "vmware virtual disk",
|
|
device: lsblkDevice{
|
|
Name: "sdb",
|
|
Type: "disk",
|
|
Model: "Virtual disk",
|
|
Vendor: "VMware",
|
|
},
|
|
skip: true,
|
|
},
|
|
{
|
|
name: "virtual subsystem",
|
|
device: lsblkDevice{
|
|
Name: "sdc",
|
|
Type: "disk",
|
|
Subsystems: "block:scsi:vmbus:pci",
|
|
},
|
|
skip: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
reason := linuxSMARTSkipReason(tt.device)
|
|
if tt.skip && reason == "" {
|
|
t.Fatalf("expected device to be skipped")
|
|
}
|
|
if !tt.skip && reason != "" {
|
|
t.Fatalf("expected device to be collected, got skip reason %q", reason)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNVMeTemperatureFallback(t *testing.T) {
|
|
// Test that NVMe temperature is extracted from the correct location
|
|
data := smartctlJSON{
|
|
Device: struct {
|
|
Name string `json:"name"`
|
|
Type string `json:"type"`
|
|
Protocol string `json:"protocol"`
|
|
}{
|
|
Protocol: "NVMe",
|
|
},
|
|
}
|
|
data.NVMeSmartHealthInformationLog.Temperature = 42
|
|
|
|
// Verify the type detection
|
|
diskType := detectDiskType(data)
|
|
if diskType != "nvme" {
|
|
t.Errorf("expected nvme, got %s", diskType)
|
|
}
|
|
|
|
// Verify NVMe temp location is populated
|
|
if data.NVMeSmartHealthInformationLog.Temperature != 42 {
|
|
t.Errorf("NVMe temperature not set correctly")
|
|
}
|
|
}
|
|
|
|
func TestParseSMARTAttributes_SATA(t *testing.T) {
|
|
data := &smartctlJSON{}
|
|
data.ATASmartAttributes.Table = []struct {
|
|
ID int `json:"id"`
|
|
Name string `json:"name"`
|
|
Value int `json:"value"`
|
|
Worst int `json:"worst"`
|
|
Thresh int `json:"thresh"`
|
|
Raw struct {
|
|
Value int64 `json:"value"`
|
|
String string `json:"string"`
|
|
} `json:"raw"`
|
|
}{
|
|
{ID: 5, Name: "Reallocated_Sector_Ct", Raw: struct {
|
|
Value int64 `json:"value"`
|
|
String string `json:"string"`
|
|
}{Value: 3}},
|
|
{ID: 9, Name: "Power_On_Hours", Raw: struct {
|
|
Value int64 `json:"value"`
|
|
String string `json:"string"`
|
|
}{Value: 12345}},
|
|
{ID: 12, Name: "Power_Cycle_Count", Raw: struct {
|
|
Value int64 `json:"value"`
|
|
String string `json:"string"`
|
|
}{Value: 42}},
|
|
{ID: 197, Name: "Current_Pending_Sector", Raw: struct {
|
|
Value int64 `json:"value"`
|
|
String string `json:"string"`
|
|
}{Value: 0}},
|
|
{ID: 198, Name: "Offline_Uncorrectable", Raw: struct {
|
|
Value int64 `json:"value"`
|
|
String string `json:"string"`
|
|
}{Value: 1}},
|
|
{ID: 199, Name: "UDMA_CRC_Error_Count", Raw: struct {
|
|
Value int64 `json:"value"`
|
|
String string `json:"string"`
|
|
}{Value: 7}},
|
|
}
|
|
|
|
attrs := parseSMARTAttributes(data, "sata")
|
|
if attrs == nil {
|
|
t.Fatal("expected non-nil attributes for SATA disk with data")
|
|
}
|
|
if attrs.PowerOnHours == nil || *attrs.PowerOnHours != 12345 {
|
|
t.Errorf("PowerOnHours: got %v, want 12345", attrs.PowerOnHours)
|
|
}
|
|
if attrs.PowerCycles == nil || *attrs.PowerCycles != 42 {
|
|
t.Errorf("PowerCycles: got %v, want 42", attrs.PowerCycles)
|
|
}
|
|
if attrs.ReallocatedSectors == nil || *attrs.ReallocatedSectors != 3 {
|
|
t.Errorf("ReallocatedSectors: got %v, want 3", attrs.ReallocatedSectors)
|
|
}
|
|
if attrs.PendingSectors == nil || *attrs.PendingSectors != 0 {
|
|
t.Errorf("PendingSectors: got %v, want 0", attrs.PendingSectors)
|
|
}
|
|
if attrs.OfflineUncorrectable == nil || *attrs.OfflineUncorrectable != 1 {
|
|
t.Errorf("OfflineUncorrectable: got %v, want 1", attrs.OfflineUncorrectable)
|
|
}
|
|
if attrs.UDMACRCErrors == nil || *attrs.UDMACRCErrors != 7 {
|
|
t.Errorf("UDMACRCErrors: got %v, want 7", attrs.UDMACRCErrors)
|
|
}
|
|
// NVMe fields should be nil
|
|
if attrs.PercentageUsed != nil {
|
|
t.Errorf("PercentageUsed should be nil for SATA disk")
|
|
}
|
|
if attrs.AvailableSpare != nil {
|
|
t.Errorf("AvailableSpare should be nil for SATA disk")
|
|
}
|
|
}
|
|
|
|
func TestParseSMARTAttributes_NVMe(t *testing.T) {
|
|
data := &smartctlJSON{}
|
|
data.NVMeSmartHealthInformationLog.PowerOnHours = 5000
|
|
data.NVMeSmartHealthInformationLog.PowerCycles = 100
|
|
data.NVMeSmartHealthInformationLog.PercentageUsed = 5
|
|
data.NVMeSmartHealthInformationLog.AvailableSpare = 100
|
|
data.NVMeSmartHealthInformationLog.MediaErrors = 0
|
|
data.NVMeSmartHealthInformationLog.UnsafeShutdowns = 12
|
|
|
|
attrs := parseSMARTAttributes(data, "nvme")
|
|
if attrs == nil {
|
|
t.Fatal("expected non-nil attributes for NVMe disk")
|
|
}
|
|
if attrs.PowerOnHours == nil || *attrs.PowerOnHours != 5000 {
|
|
t.Errorf("PowerOnHours: got %v, want 5000", attrs.PowerOnHours)
|
|
}
|
|
if attrs.PowerCycles == nil || *attrs.PowerCycles != 100 {
|
|
t.Errorf("PowerCycles: got %v, want 100", attrs.PowerCycles)
|
|
}
|
|
if attrs.PercentageUsed == nil || *attrs.PercentageUsed != 5 {
|
|
t.Errorf("PercentageUsed: got %v, want 5", attrs.PercentageUsed)
|
|
}
|
|
if attrs.AvailableSpare == nil || *attrs.AvailableSpare != 100 {
|
|
t.Errorf("AvailableSpare: got %v, want 100", attrs.AvailableSpare)
|
|
}
|
|
if attrs.MediaErrors == nil || *attrs.MediaErrors != 0 {
|
|
t.Errorf("MediaErrors: got %v, want 0", attrs.MediaErrors)
|
|
}
|
|
if attrs.UnsafeShutdowns == nil || *attrs.UnsafeShutdowns != 12 {
|
|
t.Errorf("UnsafeShutdowns: got %v, want 12", attrs.UnsafeShutdowns)
|
|
}
|
|
// SATA fields should be nil
|
|
if attrs.ReallocatedSectors != nil {
|
|
t.Errorf("ReallocatedSectors should be nil for NVMe disk")
|
|
}
|
|
}
|
|
|
|
func TestParseSMARTAttributes_PartialData(t *testing.T) {
|
|
// SATA disk with only power-on hours — other attributes missing
|
|
data := &smartctlJSON{}
|
|
data.ATASmartAttributes.Table = []struct {
|
|
ID int `json:"id"`
|
|
Name string `json:"name"`
|
|
Value int `json:"value"`
|
|
Worst int `json:"worst"`
|
|
Thresh int `json:"thresh"`
|
|
Raw struct {
|
|
Value int64 `json:"value"`
|
|
String string `json:"string"`
|
|
} `json:"raw"`
|
|
}{
|
|
{ID: 9, Name: "Power_On_Hours", Raw: struct {
|
|
Value int64 `json:"value"`
|
|
String string `json:"string"`
|
|
}{Value: 100}},
|
|
}
|
|
|
|
attrs := parseSMARTAttributes(data, "sata")
|
|
if attrs == nil {
|
|
t.Fatal("expected non-nil attributes")
|
|
}
|
|
if attrs.PowerOnHours == nil || *attrs.PowerOnHours != 100 {
|
|
t.Errorf("PowerOnHours: got %v, want 100", attrs.PowerOnHours)
|
|
}
|
|
if attrs.ReallocatedSectors != nil {
|
|
t.Errorf("ReallocatedSectors should be nil when not in table")
|
|
}
|
|
if attrs.PendingSectors != nil {
|
|
t.Errorf("PendingSectors should be nil when not in table")
|
|
}
|
|
}
|
|
|
|
func TestParseSMARTAttributes_Standby(t *testing.T) {
|
|
// No data at all — simulates standby or no SMART support
|
|
data := &smartctlJSON{}
|
|
attrs := parseSMARTAttributes(data, "sata")
|
|
if attrs != nil {
|
|
t.Errorf("expected nil attributes for empty data, got %+v", attrs)
|
|
}
|
|
|
|
attrs = parseSMARTAttributes(data, "nvme")
|
|
if attrs != nil {
|
|
t.Errorf("expected nil attributes for empty NVMe data, got %+v", attrs)
|
|
}
|
|
}
|
|
|
|
func TestParseSMARTOutputStandbyPowerMode(t *testing.T) {
|
|
payload := smartctlJSON{
|
|
ModelName: "WDC WD40EFRX",
|
|
SerialNumber: "WD-123",
|
|
PowerMode: "STANDBY",
|
|
}
|
|
payload.Device.Protocol = "ATA"
|
|
|
|
out, err := json.Marshal(payload)
|
|
if err != nil {
|
|
t.Fatalf("marshal payload: %v", err)
|
|
}
|
|
|
|
result, err := parseSMARTOutput(out, smartctlTarget{Path: "/dev/ada0"})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if result == nil {
|
|
t.Fatal("expected result")
|
|
}
|
|
if !result.Standby {
|
|
t.Fatalf("expected standby result, got %#v", result)
|
|
}
|
|
if result.Model != "WDC WD40EFRX" || result.Serial != "WD-123" {
|
|
t.Fatalf("expected model/serial to be preserved, got %#v", result)
|
|
}
|
|
}
|
|
|
|
func TestParseSMARTOutputUsesATASCTTemperature(t *testing.T) {
|
|
payload := smartctlJSON{
|
|
ModelName: "WDC WD40EFRX",
|
|
SerialNumber: "WD-456",
|
|
}
|
|
payload.Device.Protocol = "ATA"
|
|
payload.SmartStatus = &struct {
|
|
Passed bool `json:"passed"`
|
|
}{Passed: true}
|
|
payload.ATASCTStatus.Current.Value = 41
|
|
|
|
out, err := json.Marshal(payload)
|
|
if err != nil {
|
|
t.Fatalf("marshal payload: %v", err)
|
|
}
|
|
|
|
result, err := parseSMARTOutput(out, smartctlTarget{Path: "/dev/ada0"})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if result == nil {
|
|
t.Fatal("expected result")
|
|
}
|
|
if result.Temperature != 41 {
|
|
t.Fatalf("expected SCT temperature 41, got %#v", result)
|
|
}
|
|
}
|
|
|
|
func TestParseSMARTOutputFallsBackToOriginalTextTemperature(t *testing.T) {
|
|
payload := smartctlJSON{
|
|
ModelName: "WDC WD40EFRX",
|
|
SerialNumber: "WD-789",
|
|
}
|
|
payload.Device.Protocol = "ATA"
|
|
payload.SmartStatus = &struct {
|
|
Passed bool `json:"passed"`
|
|
}{Passed: true}
|
|
payload.Smartctl.Output = []string{
|
|
"=== START OF READ SMART DATA SECTION ===",
|
|
"194 Temperature_Celsius 0x0022 120 099 000 Old_age Always - 37 (Min/Max 20/45)",
|
|
}
|
|
|
|
out, err := json.Marshal(payload)
|
|
if err != nil {
|
|
t.Fatalf("marshal payload: %v", err)
|
|
}
|
|
|
|
result, err := parseSMARTOutput(out, smartctlTarget{Path: "/dev/ada0"})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if result == nil {
|
|
t.Fatal("expected result")
|
|
}
|
|
if result.Temperature != 37 {
|
|
t.Fatalf("expected text-fallback temperature 37, got %#v", result)
|
|
}
|
|
if result.Model != "WDC WD40EFRX" || result.Serial != "WD-789" || result.Health != "PASSED" {
|
|
t.Fatalf("expected model/serial/health to be preserved, got %#v", result)
|
|
}
|
|
}
|
|
|
|
func TestParseSMARTOutputFallsBackToPlainTextOutput(t *testing.T) {
|
|
output := []byte(`
|
|
=== START OF INFORMATION SECTION ===
|
|
Device Model: WDC WD40EFRX
|
|
Serial Number: WD-123
|
|
SMART overall-health self-assessment test result: PASSED
|
|
Current Temperature: 39 C
|
|
`)
|
|
|
|
result, err := parseSMARTOutput(output, smartctlTarget{Path: "/dev/ada0"})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if result == nil {
|
|
t.Fatal("expected result")
|
|
}
|
|
if result.Model != "WDC WD40EFRX" || result.Serial != "WD-123" {
|
|
t.Fatalf("expected text model/serial, got %#v", result)
|
|
}
|
|
if result.Health != "PASSED" || result.Temperature != 39 {
|
|
t.Fatalf("expected text-fallback health/temp, got %#v", result)
|
|
}
|
|
if result.Type != "sata" {
|
|
t.Fatalf("expected sata fallback type, got %#v", result)
|
|
}
|
|
}
|
|
|
|
func TestParseRawValue(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
str string
|
|
value int64
|
|
expected int64
|
|
}{
|
|
{"plain number", "16951", 150323855943, 16951},
|
|
{"seagate power-on hours", "16951 (223 173 0)", 150323855943, 16951},
|
|
{"simple value matches", "12345", 12345, 12345},
|
|
{"empty string fallback", "", 42, 42},
|
|
{"whitespace only fallback", " ", 42, 42},
|
|
{"zero", "0", 999, 0},
|
|
{"non-numeric fallback", "unknown", 77, 77},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := parseRawValue(tt.str, tt.value)
|
|
if got != tt.expected {
|
|
t.Errorf("parseRawValue(%q, %d) = %d, want %d", tt.str, tt.value, got, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseSMARTAttributes_SeagateRawValues(t *testing.T) {
|
|
// Seagate drives pack vendor-specific data in upper bytes of the 48-bit raw
|
|
// value, making raw.value huge. raw.string contains the correct interpretation.
|
|
data := &smartctlJSON{}
|
|
data.ATASmartAttributes.Table = []struct {
|
|
ID int `json:"id"`
|
|
Name string `json:"name"`
|
|
Value int `json:"value"`
|
|
Worst int `json:"worst"`
|
|
Thresh int `json:"thresh"`
|
|
Raw struct {
|
|
Value int64 `json:"value"`
|
|
String string `json:"string"`
|
|
} `json:"raw"`
|
|
}{
|
|
{ID: 9, Name: "Power_On_Hours", Raw: struct {
|
|
Value int64 `json:"value"`
|
|
String string `json:"string"`
|
|
}{Value: 150323855943, String: "16951 (223 173 0)"}},
|
|
{ID: 12, Name: "Power_Cycle_Count", Raw: struct {
|
|
Value int64 `json:"value"`
|
|
String string `json:"string"`
|
|
}{Value: 94, String: "94"}},
|
|
}
|
|
|
|
attrs := parseSMARTAttributes(data, "sata")
|
|
if attrs == nil {
|
|
t.Fatal("expected non-nil attributes")
|
|
}
|
|
if attrs.PowerOnHours == nil || *attrs.PowerOnHours != 16951 {
|
|
t.Errorf("PowerOnHours: got %v, want 16951 (should parse from raw.string, not raw.value)", attrs.PowerOnHours)
|
|
}
|
|
if attrs.PowerCycles == nil || *attrs.PowerCycles != 94 {
|
|
t.Errorf("PowerCycles: got %v, want 94", attrs.PowerCycles)
|
|
}
|
|
}
|
|
|
|
func TestMatchesDeviceExclude(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
deviceName string
|
|
devicePath string
|
|
patterns []string
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "exact match on name",
|
|
deviceName: "sda",
|
|
devicePath: "/dev/sda",
|
|
patterns: []string{"sda"},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "exact match on full path",
|
|
deviceName: "sda",
|
|
devicePath: "/dev/sda",
|
|
patterns: []string{"/dev/sda"},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "no match",
|
|
deviceName: "sdb",
|
|
devicePath: "/dev/sdb",
|
|
patterns: []string{"sda"},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "prefix pattern on name",
|
|
deviceName: "nvme0n1",
|
|
devicePath: "/dev/nvme0n1",
|
|
patterns: []string{"nvme*"},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "prefix pattern on path",
|
|
deviceName: "nvme0n1",
|
|
devicePath: "/dev/nvme0n1",
|
|
patterns: []string{"/dev/nvme*"},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "contains pattern",
|
|
deviceName: "sdcache1",
|
|
devicePath: "/dev/sdcache1",
|
|
patterns: []string{"*cache*"},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "contains pattern no match",
|
|
deviceName: "sda",
|
|
devicePath: "/dev/sda",
|
|
patterns: []string{"*cache*"},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "empty patterns",
|
|
deviceName: "sda",
|
|
devicePath: "/dev/sda",
|
|
patterns: []string{},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "nil patterns",
|
|
deviceName: "sda",
|
|
devicePath: "/dev/sda",
|
|
patterns: nil,
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "multiple patterns first matches",
|
|
deviceName: "sda",
|
|
devicePath: "/dev/sda",
|
|
patterns: []string{"sda", "sdb"},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "multiple patterns second matches",
|
|
deviceName: "sdb",
|
|
devicePath: "/dev/sdb",
|
|
patterns: []string{"sda", "sdb"},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "whitespace trimmed",
|
|
deviceName: "sda",
|
|
devicePath: "/dev/sda",
|
|
patterns: []string{" sda "},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "empty pattern ignored",
|
|
deviceName: "sda",
|
|
devicePath: "/dev/sda",
|
|
patterns: []string{"", " ", "sdb"},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "sd prefix pattern",
|
|
deviceName: "sdc",
|
|
devicePath: "/dev/sdc",
|
|
patterns: []string{"sd*"},
|
|
expected: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := matchesDeviceExclude(tt.deviceName, tt.devicePath, tt.patterns)
|
|
if result != tt.expected {
|
|
t.Errorf("matchesDeviceExclude(%q, %q, %v) = %t, want %t",
|
|
tt.deviceName, tt.devicePath, tt.patterns, result, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|