adaf86ad57
gates / gates (push) Successful in 53s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
65 lines
3.0 KiB
Go
65 lines
3.0 KiB
Go
package storage
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// R-330 (disk health Phase 2) — attributes 187, 188 and 199 ride the wire.
|
|
//
|
|
// The failing drive of 2026-08-14 carried 187 Reported_Uncorrect at raw 1001 while SMART said PASSED;
|
|
// none of the three reached the controller. The values are carried as RAW counters; an absent
|
|
// attribute is OMITTED from the JSON (unknown), never sent as 0 (S-39).
|
|
|
|
// The 2026-08-14 shape: PASSED, 187 raw 1001, plus 188/199 and the existing three.
|
|
const r330SATA = `{"smart_status":{"passed":true},"ata_smart_attributes":{"table":[
|
|
{"id":5,"raw":{"value":0}},
|
|
{"id":187,"raw":{"value":1001}},
|
|
{"id":188,"raw":{"value":4295032833}},
|
|
{"id":197,"raw":{"value":8}},
|
|
{"id":198,"raw":{"value":8}},
|
|
{"id":199,"raw":{"value":3}}]}}`
|
|
|
|
// COMPANION RED-PROOF (observed): delete the three R-330 cases in parseSMART → this fails with
|
|
// "187 Reported_Uncorrect must be carried (raw 1001); got <nil>". Restored.
|
|
func TestParseSMART_R330_CarriesTheThreeCounters(t *testing.T) {
|
|
s := parseSMART([]byte(r330SATA))
|
|
if s.ReportedUncorrect == nil || *s.ReportedUncorrect != 1001 {
|
|
t.Fatalf("187 Reported_Uncorrect must be carried (raw 1001); got %v", s.ReportedUncorrect)
|
|
}
|
|
// 188's raw value is vendor-packed on some drives (this one is 0x100010001): carried as reported, not truncated.
|
|
if s.CommandTimeout == nil || *s.CommandTimeout != 4295032833 {
|
|
t.Fatalf("188 Command_Timeout must be carried as the full raw value; got %v", s.CommandTimeout)
|
|
}
|
|
if s.UDMACRCErrors == nil || *s.UDMACRCErrors != 3 {
|
|
t.Fatalf("199 UDMA_CRC_Error_Count must be carried (raw 3); got %v", s.UDMACRCErrors)
|
|
}
|
|
if s.PendingSectors == nil || *s.PendingSectors != 8 {
|
|
t.Fatalf("the existing counters must be unchanged; pending=%v", s.PendingSectors)
|
|
}
|
|
}
|
|
|
|
// A drive (or an NVMe device) that does not report the attributes leaves them nil, and the JSON
|
|
// OMITS the keys — the receiver reads "unknown", never a measured zero.
|
|
//
|
|
// COMPANION RED-PROOF (observed): drop `,omitempty` from the three tags in hub.SmartSummary → this
|
|
// fails with "an unreported attribute must be omitted, not sent: … reported_uncorrect …". Restored.
|
|
func TestParseSMART_R330_AbsentIsOmittedNotZero(t *testing.T) {
|
|
for name, raw := range map[string]string{
|
|
"sata without the three": `{"smart_status":{"passed":true},"ata_smart_attributes":{"table":[{"id":5,"raw":{"value":0}}]}}`,
|
|
"nvme": `{"smart_status":{"passed":true},"nvme_smart_health_information_log":{"critical_warning":0,"media_errors":0,"percentage_used":3}}`,
|
|
} {
|
|
s := parseSMART([]byte(raw))
|
|
if s.ReportedUncorrect != nil || s.CommandTimeout != nil || s.UDMACRCErrors != nil {
|
|
t.Fatalf("%s: unreported attributes must stay nil; got %v %v %v", name, s.ReportedUncorrect, s.CommandTimeout, s.UDMACRCErrors)
|
|
}
|
|
b, _ := json.Marshal(s)
|
|
for _, k := range []string{"reported_uncorrect", "command_timeout", "udma_crc_errors"} {
|
|
if strings.Contains(string(b), `"`+k+`"`) {
|
|
t.Fatalf("%s: an unreported attribute must be omitted, not sent: %s", name, b)
|
|
}
|
|
}
|
|
}
|
|
}
|