github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/bucket-replication-utils_test.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "context" 22 "testing" 23 24 "github.com/minio/minio/internal/bucket/replication" 25 ) 26 27 var replicatedInfosTests = []struct { 28 name string 29 tgtInfos []replicatedTargetInfo 30 expectedCompletedSize int64 31 expectedReplicationStatusInternal string 32 expectedReplicationStatus replication.StatusType 33 expectedOpType replication.Type 34 expectedAction replicationAction 35 }{ 36 { // 1. empty tgtInfos slice 37 name: "no replicated targets", 38 tgtInfos: []replicatedTargetInfo{}, 39 expectedCompletedSize: 0, 40 expectedReplicationStatusInternal: "", 41 expectedReplicationStatus: replication.StatusType(""), 42 expectedOpType: replication.UnsetReplicationType, 43 expectedAction: replicateNone, 44 }, 45 { // 2. replication completed to single target 46 name: "replication completed to single target", 47 tgtInfos: []replicatedTargetInfo{ 48 { 49 Arn: "arn1", 50 Size: 249, 51 PrevReplicationStatus: replication.Pending, 52 ReplicationStatus: replication.Completed, 53 OpType: replication.ObjectReplicationType, 54 ReplicationAction: replicateAll, 55 }, 56 }, 57 expectedCompletedSize: 249, 58 expectedReplicationStatusInternal: "arn1=COMPLETED;", 59 expectedReplicationStatus: replication.Completed, 60 expectedOpType: replication.ObjectReplicationType, 61 expectedAction: replicateAll, 62 }, 63 { // 3. replication completed to single target; failed to another 64 name: "replication completed to single target", 65 tgtInfos: []replicatedTargetInfo{ 66 { 67 Arn: "arn1", 68 Size: 249, 69 PrevReplicationStatus: replication.Pending, 70 ReplicationStatus: replication.Completed, 71 OpType: replication.ObjectReplicationType, 72 ReplicationAction: replicateAll, 73 }, 74 { 75 Arn: "arn2", 76 Size: 249, 77 PrevReplicationStatus: replication.Pending, 78 ReplicationStatus: replication.Failed, 79 OpType: replication.ObjectReplicationType, 80 ReplicationAction: replicateAll, 81 }, 82 }, 83 expectedCompletedSize: 249, 84 expectedReplicationStatusInternal: "arn1=COMPLETED;arn2=FAILED;", 85 expectedReplicationStatus: replication.Failed, 86 expectedOpType: replication.ObjectReplicationType, 87 expectedAction: replicateAll, 88 }, 89 { // 4. replication pending on one target; failed to another 90 name: "replication completed to single target", 91 tgtInfos: []replicatedTargetInfo{ 92 { 93 Arn: "arn1", 94 Size: 249, 95 PrevReplicationStatus: replication.Pending, 96 ReplicationStatus: replication.Pending, 97 OpType: replication.ObjectReplicationType, 98 ReplicationAction: replicateAll, 99 }, 100 { 101 Arn: "arn2", 102 Size: 249, 103 PrevReplicationStatus: replication.Pending, 104 ReplicationStatus: replication.Failed, 105 OpType: replication.ObjectReplicationType, 106 ReplicationAction: replicateAll, 107 }, 108 }, 109 expectedCompletedSize: 0, 110 expectedReplicationStatusInternal: "arn1=PENDING;arn2=FAILED;", 111 expectedReplicationStatus: replication.Failed, 112 expectedOpType: replication.ObjectReplicationType, 113 expectedAction: replicateAll, 114 }, 115 } 116 117 func TestReplicatedInfos(t *testing.T) { 118 for i, test := range replicatedInfosTests { 119 rinfos := replicatedInfos{ 120 Targets: test.tgtInfos, 121 } 122 if actualSize := rinfos.CompletedSize(); actualSize != test.expectedCompletedSize { 123 t.Errorf("Test%d (%s): Size got %d , want %d", i+1, test.name, actualSize, test.expectedCompletedSize) 124 } 125 if repStatusStr := rinfos.ReplicationStatusInternal(); repStatusStr != test.expectedReplicationStatusInternal { 126 t.Errorf("Test%d (%s): Internal replication status got %s , want %s", i+1, test.name, repStatusStr, test.expectedReplicationStatusInternal) 127 } 128 if repStatus := rinfos.ReplicationStatus(); repStatus != test.expectedReplicationStatus { 129 t.Errorf("Test%d (%s): ReplicationStatus got %s , want %s", i+1, test.name, repStatus, test.expectedReplicationStatus) 130 } 131 if action := rinfos.Action(); action != test.expectedAction { 132 t.Errorf("Test%d (%s): Action got %s , want %s", i+1, test.name, action, test.expectedAction) 133 } 134 } 135 } 136 137 var parseReplicationDecisionTest = []struct { 138 name string 139 dsc string 140 expDsc ReplicateDecision 141 expErr error 142 }{ 143 { // 1. 144 name: "empty string", 145 dsc: "", 146 expDsc: ReplicateDecision{ 147 targetsMap: map[string]replicateTargetDecision{}, 148 }, 149 expErr: nil, 150 }, 151 152 { // 2. 153 name: "replicate decision for one target", 154 dsc: "arn:minio:replication::id:bucket=true;false;arn:minio:replication::id:bucket;id", 155 expErr: nil, 156 expDsc: ReplicateDecision{ 157 targetsMap: map[string]replicateTargetDecision{ 158 "arn:minio:replication::id:bucket": newReplicateTargetDecision("arn:minio:replication::id:bucket", true, false), 159 }, 160 }, 161 }, 162 { // 3. 163 name: "replicate decision for multiple targets", 164 dsc: "arn:minio:replication::id:bucket=true;false;arn:minio:replication::id:bucket;id,arn:minio:replication::id2:bucket=false;true;arn:minio:replication::id2:bucket;id2", 165 expErr: nil, 166 expDsc: ReplicateDecision{ 167 targetsMap: map[string]replicateTargetDecision{ 168 "arn:minio:replication::id:bucket": newReplicateTargetDecision("arn:minio:replication::id:bucket", true, false), 169 "arn:minio:replication::id2:bucket": newReplicateTargetDecision("arn:minio:replication::id2:bucket", false, true), 170 }, 171 }, 172 }, 173 { // 4. 174 name: "invalid format replicate decision for one target", 175 dsc: "arn:minio:replication::id:bucket:true;false;arn:minio:replication::id:bucket;id", 176 expErr: errInvalidReplicateDecisionFormat, 177 expDsc: ReplicateDecision{ 178 targetsMap: map[string]replicateTargetDecision{ 179 "arn:minio:replication::id:bucket": newReplicateTargetDecision("arn:minio:replication::id:bucket", true, false), 180 }, 181 }, 182 }, 183 } 184 185 func TestParseReplicateDecision(t *testing.T) { 186 for i, test := range parseReplicationDecisionTest { 187 dsc, err := parseReplicateDecision(context.Background(), "bucket", test.expDsc.String()) 188 if err != nil { 189 if test.expErr != err { 190 t.Errorf("Test%d (%s): Expected parse error got %t , want %t", i+1, test.name, err, test.expErr) 191 } 192 continue 193 } 194 if len(dsc.targetsMap) != len(test.expDsc.targetsMap) { 195 t.Errorf("Test%d (%s): Invalid number of entries in targetsMap got %d , want %d", i+1, test.name, len(dsc.targetsMap), len(test.expDsc.targetsMap)) 196 } 197 for arn, tdsc := range dsc.targetsMap { 198 expDsc, ok := test.expDsc.targetsMap[arn] 199 if !ok || expDsc != tdsc { 200 t.Errorf("Test%d (%s): Invalid target replicate decision: got %+v, want %+v", i+1, test.name, tdsc, expDsc) 201 } 202 } 203 } 204 } 205 206 var replicationStateTest = []struct { 207 name string 208 rs ReplicationState 209 arn string 210 expStatus replication.StatusType 211 }{ 212 { // 1. no replication status header 213 name: "no replicated targets", 214 rs: ReplicationState{}, 215 expStatus: replication.StatusType(""), 216 }, 217 { // 2. replication status for one target 218 name: "replication status for one target", 219 rs: ReplicationState{ReplicationStatusInternal: "arn1=PENDING;", Targets: map[string]replication.StatusType{"arn1": "PENDING"}}, 220 expStatus: replication.Pending, 221 }, 222 { // 3. replication status for one target - incorrect format 223 name: "replication status for one target", 224 rs: ReplicationState{ReplicationStatusInternal: "arn1=PENDING"}, 225 expStatus: replication.StatusType(""), 226 }, 227 { // 4. replication status for 3 targets, one of them failed 228 name: "replication status for 3 targets - one failed", 229 rs: ReplicationState{ 230 ReplicationStatusInternal: "arn1=COMPLETED;arn2=COMPLETED;arn3=FAILED;", 231 Targets: map[string]replication.StatusType{"arn1": "COMPLETED", "arn2": "COMPLETED", "arn3": "FAILED"}, 232 }, 233 expStatus: replication.Failed, 234 }, 235 { // 5. replication status for replica version 236 name: "replication status for replica version", 237 rs: ReplicationState{ReplicationStatusInternal: string(replication.Replica)}, 238 expStatus: replication.Replica, 239 }, 240 } 241 242 func TestCompositeReplicationStatus(t *testing.T) { 243 for i, test := range replicationStateTest { 244 if rstatus := test.rs.CompositeReplicationStatus(); rstatus != test.expStatus { 245 t.Errorf("Test%d (%s): Overall replication status got %s , want %s", i+1, test.name, rstatus, test.expStatus) 246 } 247 } 248 }