77 lines
3.0 KiB
Go
77 lines
3.0 KiB
Go
package cluster
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
)
|
|
|
|
func TestWriteServiceErrorDisallowedRemovalBlockedIncludesBreakdownDetails(t *testing.T) {
|
|
recorder := httptest.NewRecorder()
|
|
handled := writeServiceError(recorder, &FabricStandardCleanupBlockedError{
|
|
BlockedOperation: "create_breaking_release",
|
|
Report: StaleNodeRiskReport{
|
|
HeartbeatStaleAfterSeconds: 900,
|
|
FabricStandardCleanupAllowed: false,
|
|
BridgeHoldRequired: true,
|
|
BridgeHoldNodeIDs: []string{"node-1"},
|
|
BridgeHoldReasons: []string{"standard_contract_overlap"},
|
|
BlockedOperations: []string{"create_breaking_release", "target_breaking_update_policy", "remove_recovery_bridge_overlap"},
|
|
Nodes: []StaleNodeRiskNode{
|
|
{NodeID: "node-1", Blocked: true, RecoveryBridgeRequired: true},
|
|
{NodeID: "node-2", Blocked: false},
|
|
},
|
|
Summary: StaleNodeRiskSummary{
|
|
StaleNodes: 1,
|
|
BlockedNodes: 1,
|
|
UpdaterRuntimeMissingNodes: 1,
|
|
StagedSelfUpdatePendingNodes: 1,
|
|
ArtifactGapNodes: 0,
|
|
UnknownProfileNodes: 0,
|
|
WaitingUpdateStatusNodes: 0,
|
|
UnknownVersionNodes: 0,
|
|
StandardRecoveryContractNodes: 0,
|
|
WaitingRecoveryHeartbeatNodes: 1,
|
|
},
|
|
},
|
|
})
|
|
if !handled {
|
|
t.Fatalf("writeServiceError returned false")
|
|
}
|
|
if recorder.Code != http.StatusConflict {
|
|
t.Fatalf("status = %d, want %d", recorder.Code, http.StatusConflict)
|
|
}
|
|
var payload struct {
|
|
Error struct {
|
|
Details map[string]any `json:"details"`
|
|
} `json:"error"`
|
|
}
|
|
if err := json.Unmarshal(recorder.Body.Bytes(), &payload); err != nil {
|
|
t.Fatalf("unmarshal response: %v", err)
|
|
}
|
|
if payload.Error.Details["blocked_operation"] != "create_breaking_release" {
|
|
t.Fatalf("blocked_operation = %v", payload.Error.Details["blocked_operation"])
|
|
}
|
|
if payload.Error.Details["waiting_recovery_heartbeat_nodes"] != float64(1) {
|
|
t.Fatalf("waiting_recovery_heartbeat_nodes = %v", payload.Error.Details["waiting_recovery_heartbeat_nodes"])
|
|
}
|
|
if payload.Error.Details["staged_self_update_pending_nodes"] != float64(1) {
|
|
t.Fatalf("staged_self_update_pending_nodes = %v", payload.Error.Details["staged_self_update_pending_nodes"])
|
|
}
|
|
if payload.Error.Details["updater_runtime_missing_nodes"] != float64(1) {
|
|
t.Fatalf("updater_runtime_missing_nodes = %v", payload.Error.Details["updater_runtime_missing_nodes"])
|
|
}
|
|
if payload.Error.Details["bridge_hold_required"] != true {
|
|
t.Fatalf("bridge_hold_required = %v", payload.Error.Details["bridge_hold_required"])
|
|
}
|
|
blockedNodeIDs, ok := payload.Error.Details["blocked_node_ids"].([]any)
|
|
if !ok || len(blockedNodeIDs) != 1 || blockedNodeIDs[0] != "node-1" {
|
|
t.Fatalf("blocked_node_ids = %#v", payload.Error.Details["blocked_node_ids"])
|
|
}
|
|
bridgeHoldNodeIDs, ok := payload.Error.Details["bridge_hold_node_ids"].([]any)
|
|
if !ok || len(bridgeHoldNodeIDs) != 1 || bridgeHoldNodeIDs[0] != "node-1" {
|
|
t.Fatalf("bridge_hold_node_ids = %#v", payload.Error.Details["bridge_hold_node_ids"])
|
|
}
|
|
}
|