Files
seaweedfs/weed/storage/blockvol/testrunner/actions/devops_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

102 lines
2.4 KiB
Go

package actions
import (
"sort"
"testing"
tr "github.com/seaweedfs/seaweedfs/weed/storage/blockvol/testrunner"
)
func TestDevOpsActions_Registration(t *testing.T) {
registry := tr.NewRegistry()
RegisterDevOpsActions(registry)
expected := []string{
"build_deploy_weed",
"start_weed_master",
"start_weed_volume",
"stop_weed",
"wait_cluster_ready",
"create_block_volume",
"cluster_status",
}
for _, name := range expected {
if _, err := registry.Get(name); err != nil {
t.Errorf("action %q not registered: %v", name, err)
}
}
}
func TestDevOpsActions_Tier(t *testing.T) {
registry := tr.NewRegistry()
RegisterDevOpsActions(registry)
byTier := registry.ListByTier()
devopsActions := byTier[tr.TierDevOps]
if len(devopsActions) != 7 {
t.Errorf("devops tier has %d actions, want 7", len(devopsActions))
}
// Verify all are in devops tier.
sort.Strings(devopsActions)
for _, name := range devopsActions {
if tier := registry.ActionTier(name); tier != tr.TierDevOps {
t.Errorf("action %q has tier %q, want devops", name, tier)
}
}
}
func TestDevOpsActions_TierGating(t *testing.T) {
registry := tr.NewRegistry()
RegisterDevOpsActions(registry)
// Without gating, all should be accessible.
if _, err := registry.Get("start_weed_master"); err != nil {
t.Errorf("ungated: %v", err)
}
// Enable only core tier — devops should be blocked.
registry.EnableTiers([]string{tr.TierCore})
if _, err := registry.Get("start_weed_master"); err == nil {
t.Error("expected error when devops tier is disabled")
}
// Enable devops tier — should work again.
registry.EnableTiers([]string{tr.TierDevOps})
if _, err := registry.Get("start_weed_master"); err != nil {
t.Errorf("devops enabled: %v", err)
}
}
func TestAllActions_Registration(t *testing.T) {
registry := tr.NewRegistry()
RegisterAll(registry)
byTier := registry.ListByTier()
// Verify tier counts.
if n := len(byTier[tr.TierCore]); n != 7 {
t.Errorf("core: %d, want 7", n)
}
if n := len(byTier[tr.TierBlock]); n != 33 {
t.Errorf("block: %d, want 33", n)
}
if n := len(byTier[tr.TierDevOps]); n != 7 {
t.Errorf("devops: %d, want 7", n)
}
if n := len(byTier[tr.TierChaos]); n != 5 {
t.Errorf("chaos: %d, want 5", n)
}
// Total should be 52.
total := 0
for _, actions := range byTier {
total += len(actions)
}
if total != 52 {
t.Errorf("total actions: %d, want 52", total)
}
}