Files
seaweedfs/weed/storage/blockvol/csi/snapshot_ids_test.go
T
Ping QiuandClaude Opus 4.6 979a9b496c feat: Phase 8 CP8-1/2/3/4 -- ops control plane, multi-replica, CSI snapshots, observability
CP8-1: HTTP REST API (create/delete/lookup/list/assign/servers), blockapi Go
client with multi-master failover, 5 shell commands, HTML dashboard at /block/.

CP8-2: RF=2/RF=3 multi-replica support -- ShipperGroup fan-out, distributed
sync, health scoring, segment-based scrub, gated promotion (heartbeat
freshness + WAL LSN + role checks), failover/rebuild for N>2 replicas.

CP8-3: CSI snapshot + expansion -- CreateSnapshot/DeleteSnapshot/ListSnapshots
RPCs, NodeExpandVolume with iSCSI rescan, snapshot ID helpers, 20 adversarial
tests covering concurrent ops, edge cases, and error injection.

CP8-4: Observability -- EngineMetrics atomic counters for flusher/group-commit/
WAL-shipper/scrub, 10 new Prometheus metrics, barrier_lag_lsn SLO gauge,
failover/promotion/rebuild counters, request ID correlation in master gRPC
logs, baseline regression framework with 7 hard-fail conditions.

Total: 63 files, ~11.2K LOC, 160+ new tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 00:05:17 -08:00

85 lines
2.0 KiB
Go

package csi
import (
"testing"
)
func TestSnapshotID_FormatParseRoundTrip(t *testing.T) {
tests := []struct {
volumeID string
snapID uint32
}{
{"pvc-abc123", 42},
{"vol-with-dashes-in-name", 999},
{"simple", 0},
{"a", 4294967295}, // max uint32
}
for _, tt := range tests {
csiID := FormatSnapshotID(tt.volumeID, tt.snapID)
gotVol, gotSnap, err := ParseSnapshotID(csiID)
if err != nil {
t.Errorf("ParseSnapshotID(%q): %v", csiID, err)
continue
}
if gotVol != tt.volumeID {
t.Errorf("volumeID: got %q, want %q", gotVol, tt.volumeID)
}
if gotSnap != tt.snapID {
t.Errorf("snapID: got %d, want %d", gotSnap, tt.snapID)
}
}
}
func TestSnapshotID_ParseErrors(t *testing.T) {
bad := []string{
"",
"not-a-snap",
"snap-", // no volume or snap
"snap-vol", // no dash separator for snap ID
"snap-vol-abc", // non-numeric snap ID
}
for _, s := range bad {
_, _, err := ParseSnapshotID(s)
if err == nil {
t.Errorf("ParseSnapshotID(%q): expected error", s)
}
}
}
func TestSnapshotNameToID_Deterministic(t *testing.T) {
name := "my-snapshot-name"
id1 := snapshotNameToID(name)
id2 := snapshotNameToID(name)
if id1 != id2 {
t.Fatalf("FNV hash not deterministic: %d vs %d", id1, id2)
}
if id1 == 0 {
t.Fatal("expected non-zero hash")
}
}
func TestSnapshotNameToID_DifferentNames(t *testing.T) {
id1 := snapshotNameToID("snap-alpha")
id2 := snapshotNameToID("snap-beta")
if id1 == id2 {
t.Fatalf("different names produced same hash: %d", id1)
}
}
func TestSnapshotID_EmptyVolumeID(t *testing.T) {
// snap--42 should parse: volumeID="" but we reject it
_, _, err := ParseSnapshotID("snap--42")
if err != nil {
// We accept this is an error since lastDash==0 means empty volumeID
return
}
// If it doesn't error, that's also fine as long as parse is correct
}
func TestSnapshotID_FormatContainsComponents(t *testing.T) {
id := FormatSnapshotID("pvc-123", 7)
if id != "snap-pvc-123-7" {
t.Fatalf("got %q, want snap-pvc-123-7", id)
}
}