Files
seaweedfs/weed/storage/blockvol/testrunner/baseline_test.go
T
Ping QiuandClaude Opus 4.6 da1b81d1c9 feat: CP8-3-1 durability modes + testrunner platform + 21 adversarial tests
Durability mode implementation (sync_all, sync_quorum, best_effort):
- DurabilityMode type with superblock persistence, parse/validate/string
- MakeDistributedSync mode-aware barrier enforcement in dist_group_commit
- blockerr sentinel package (ErrDurabilityBarrierFailed, ErrDurabilityQuorumLost)
- gRPC create path: mode validation, idempotent create consistency, partial cleanup
- F1: strict mode rejects partial replica provisioning with cleanup
- F3: empty heartbeat does not overwrite persisted strict mode
- F4: SCSI error mapping uses errors.Is sentinels (not string matching)
- Proto/wire/blockapi/CLI/UI plumbing for durability_mode field
- Observability dashboard: cluster health cards + per-volume columns

Testrunner platform (YAML-driven integration test framework):
- Engine, parser, registry, reporter (JUnit XML + HTML), metrics scraping
- 52 registered actions: block, iSCSI, I/O, fault injection, assertions
- Baseline regression framework with 7 hard-fail conditions
- 15 YAML scenarios (smoke, crash, HA, fault, consistency, snapshot)
- 49 unit tests for testrunner internals

QA adversarial suite (21 tests, all PASS):
- Idempotent create mode/RF mismatch detection
- Heartbeat mode downgrade prevention (F3)
- sync_all/sync_quorum partial replica enforcement (F1)
- Concurrent create race safety
- Failover/expand mode preservation
- Cleanup resilience when delete fails
- Master restart auto-register mode handling
- Superblock roundtrip all 3 modes
- Validate edge cases (mode×RF matrix)
- RequiredReplicas quorum math verification
- Sentinel error categorization

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

110 lines
2.6 KiB
Go

package testrunner
import (
"os"
"path/filepath"
"testing"
"time"
)
func TestSaveAndLoadBaseline(t *testing.T) {
dir := t.TempDir()
b := &Baseline{
Version: 1,
GitSHA: "8b2b5f6f6abc123def",
Scenario: "cp84-soak-4h.yaml",
ScenarioSHA: "abc123",
Topology: BaselineTopology{Nodes: 2, Servers: []string{"m01", "M02"}},
Config: BaselineConfig{ReplicaFactor: 2, Durability: "best_effort", WALSize: "64MB"},
Timestamp: time.Date(2026, 3, 6, 12, 0, 0, 0, time.UTC),
Metrics: map[string]float64{
"p99_write_latency_seconds": 0.005,
"write_iops": 50000,
},
DurationSec: 14400,
HardFailsPassed: true,
}
path, err := SaveBaseline(dir, b)
if err != nil {
t.Fatalf("SaveBaseline: %v", err)
}
if filepath.Base(path) != "20260306-8b2b5f6f6a.json" {
t.Errorf("unexpected filename: %s", filepath.Base(path))
}
// Verify file exists.
if _, err := os.Stat(path); err != nil {
t.Fatalf("file not created: %v", err)
}
// Load it back.
loaded, err := LoadLatestBaseline(dir)
if err != nil {
t.Fatalf("LoadLatestBaseline: %v", err)
}
if loaded.GitSHA != b.GitSHA {
t.Errorf("GitSHA = %q, want %q", loaded.GitSHA, b.GitSHA)
}
if loaded.Metrics["write_iops"] != 50000 {
t.Errorf("write_iops = %f, want 50000", loaded.Metrics["write_iops"])
}
}
func TestSaveBaselineIdempotent(t *testing.T) {
dir := t.TempDir()
b := &Baseline{
Version: 1,
GitSHA: "abc123",
Timestamp: time.Date(2026, 3, 6, 12, 0, 0, 0, time.UTC),
Metrics: map[string]float64{},
}
p1, err := SaveBaseline(dir, b)
if err != nil {
t.Fatalf("first save: %v", err)
}
p2, err := SaveBaseline(dir, b)
if err != nil {
t.Fatalf("second save: %v", err)
}
if p1 != p2 {
t.Errorf("paths differ: %s vs %s", p1, p2)
}
}
func TestLoadLatestBaseline_Empty(t *testing.T) {
dir := t.TempDir()
_, err := LoadLatestBaseline(dir)
if err == nil {
t.Error("expected error for empty dir")
}
}
func TestLoadLatestBaseline_PicksLatest(t *testing.T) {
dir := t.TempDir()
old := &Baseline{
Version: 1, GitSHA: "old1234567",
Timestamp: time.Date(2026, 3, 1, 0, 0, 0, 0, time.UTC),
Metrics: map[string]float64{"write_iops": 40000},
}
newer := &Baseline{
Version: 1, GitSHA: "new1234567",
Timestamp: time.Date(2026, 3, 6, 0, 0, 0, 0, time.UTC),
Metrics: map[string]float64{"write_iops": 50000},
}
SaveBaseline(dir, old)
SaveBaseline(dir, newer)
loaded, err := LoadLatestBaseline(dir)
if err != nil {
t.Fatalf("LoadLatestBaseline: %v", err)
}
if loaded.Metrics["write_iops"] != 50000 {
t.Errorf("expected latest baseline (50000), got %f", loaded.Metrics["write_iops"])
}
}