github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ui/src/util/localities.spec.ts (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 import { assert } from "chai"; 12 13 import * as protos from "src/js/protos"; 14 import { LocalityTier, LocalityTree } from "src/redux/localities"; 15 import { 16 generateLocalityRoute, 17 parseLocalityRoute, 18 getNodeLocalityTiers, 19 getChildLocalities, 20 getLocalityLabel, 21 getLeaves, 22 getLocality, 23 allNodesHaveLocality, 24 } from "./localities"; 25 import { cockroach } from "src/js/protos"; 26 type INodeStatus = cockroach.server.status.statuspb.INodeStatus; 27 28 describe("parseLocalityRoute", function() { 29 describe("with an empty route", function() { 30 it("returns an empty array when passed undefined", function() { 31 const tiers = parseLocalityRoute(undefined); 32 33 assert.deepEqual(tiers, []); 34 }); 35 36 it("returns an empty array when passed an empty string", function() { 37 const tiers = parseLocalityRoute(""); 38 39 assert.deepEqual(tiers, []); 40 }); 41 }); 42 43 describe("with a single-segment route", function() { 44 it("returns an array with a single tier", function() { 45 const key = "region"; 46 const value = "us-east-1"; 47 48 const tiers = parseLocalityRoute(key + "=" + value); 49 50 assert.deepEqual(tiers, [{ key, value }]); 51 }); 52 }); 53 54 describe("with a multi-segment route", function() { 55 it("returns an array with all the tiers in the route", function() { 56 const expectedTiers: LocalityTier[] = [ 57 { key: "region", value: "us-east" }, 58 { key: "zone", value: "us-east-1" }, 59 { key: "datacenter", value: "us-east-1b" }, 60 ]; 61 62 const route = expectedTiers 63 .map(({ key, value }) => key + "=" + value) 64 .join("/"); 65 66 const tiers = parseLocalityRoute(route); 67 68 assert.deepEqual(tiers, expectedTiers); 69 }); 70 }); 71 }); 72 73 describe("generateLocalityRoute", function() { 74 describe("with empty tiers", function() { 75 it("returns an empty string", function() { 76 const route = generateLocalityRoute([]); 77 78 assert.equal(route, "/"); 79 }); 80 }); 81 82 describe("with a single tier", function() { 83 it("returns a route with a single segment", function() { 84 const key = "region"; 85 const value = "us-east-1"; 86 87 const route = generateLocalityRoute([{ key, value }]); 88 89 assert.equal(route, "/" + key + "=" + value); 90 }); 91 }); 92 93 describe("with multiple tiers", function() { 94 it("returns a route with a segment for each tier", function() { 95 const tiers: LocalityTier[] = [ 96 { key: "region", value: "us-east" }, 97 { key: "zone", value: "us-east-1" }, 98 { key: "datacenter", value: "us-east-1b" }, 99 ]; 100 101 const expectedRoute = "/" + tiers 102 .map(({ key, value }) => key + "=" + value) 103 .join("/"); 104 105 const route = generateLocalityRoute(tiers); 106 107 assert.equal(route, expectedRoute); 108 }); 109 }); 110 }); 111 112 describe("getNodeLocalityTiers", function() { 113 it("returns the locality of a node", function() { 114 const tiers: protos.cockroach.roachpb.ITier[] = [ 115 { key: "region", value: "us-east" }, 116 { key: "zone", value: "us-east-1" }, 117 { key: "datacenter", value: "us-east-1b" }, 118 ]; 119 const node = { 120 desc: { 121 locality: { 122 tiers: tiers, 123 }, 124 }, 125 }; 126 127 const locality = getNodeLocalityTiers(node); 128 129 assert.deepEqual(locality, tiers); 130 }); 131 }); 132 133 describe("getChildLocalities", function() { 134 describe("with no children", function() { 135 it("returns an empty list", function() { 136 const locality: LocalityTree = { 137 tiers: [], 138 localities: {}, 139 nodes: [], 140 }; 141 142 const children = getChildLocalities(locality); 143 144 assert.deepEqual(children, []); 145 }); 146 }); 147 148 describe("with child localities", function() { 149 it("returns a list of the children", function() { 150 const usEast: LocalityTree = { 151 tiers: [{ key: "region", value: "us-east" }], 152 localities: {}, 153 nodes: [], 154 }; 155 156 const usWest: LocalityTree = { 157 tiers: [{ key: "region", value: "us-west" }], 158 localities: {}, 159 nodes: [], 160 }; 161 162 const locality: LocalityTree = { 163 tiers: [], 164 localities: { 165 region: { 166 "us-east": usEast, 167 "us-west": usWest, 168 }, 169 }, 170 nodes: [], 171 }; 172 173 const children = getChildLocalities(locality); 174 175 assert.lengthOf(children, 2); 176 assert.deepInclude(children, usEast); 177 assert.deepInclude(children, usWest); 178 }); 179 }); 180 }); 181 182 describe("getLocality", function() { 183 const localityTree: LocalityTree = { 184 tiers: [], 185 localities: { 186 region: { 187 "us-east": { 188 tiers: [{ key: "region", value: "us-east" }], 189 localities: { 190 zone: { 191 "us-east-1": { 192 tiers: [ 193 { key: "region", value: "us-east" }, 194 { key: "zone", value: "us-east-1" }, 195 ], 196 localities: {}, 197 nodes: [ 198 { 199 desc: { 200 node_id: 1, 201 locality: { 202 tiers: [ 203 { key: "region", value: "us-east" }, 204 { key: "zone", value: "us-east-1" }, 205 ], 206 }, 207 }, 208 }, 209 ], 210 }, 211 }, 212 }, 213 nodes: [], 214 }, 215 }, 216 }, 217 nodes: [], 218 }; 219 220 describe("with an empty list of tiers", function() { 221 it("returns the original locality tree", function() { 222 const tiers: LocalityTier[] = []; 223 224 const tree = getLocality(localityTree, tiers); 225 226 assert.deepEqual(tree, localityTree); 227 }); 228 }); 229 230 describe("with a single tier", function() { 231 it("returns the child locality if the tier exists", function() { 232 const tiers: LocalityTier[] = [{ key: "region", value: "us-east" }]; 233 234 const tree = getLocality(localityTree, tiers); 235 236 assert.deepEqual(tree, localityTree.localities.region["us-east"]); 237 }); 238 239 it("returns null if the tier key does not exist", function() { 240 const tiers: LocalityTier[] = [{ key: "country", value: "us-east" }]; 241 242 const tree = getLocality(localityTree, tiers); 243 244 assert.equal(tree, null); 245 }); 246 247 it("returns null if the tier value does not exist", function() { 248 const tiers: LocalityTier[] = [{ key: "region", value: "eu-north" }]; 249 250 const tree = getLocality(localityTree, tiers); 251 252 assert.equal(tree, null); 253 }); 254 }); 255 256 describe("with multiple tiers", function() { 257 it("returns the grandchild locality if the tiers exist", function() { 258 const tiers: LocalityTier[] = [ 259 { key: "region", value: "us-east" }, 260 { key: "zone", value: "us-east-1" }, 261 ]; 262 263 const tree = getLocality(localityTree, tiers); 264 265 assert.deepEqual(tree, localityTree.localities.region["us-east"].localities.zone["us-east-1"]); 266 }); 267 268 it("returns null if the first tier key does not exist", function() { 269 const tiers: LocalityTier[] = [ 270 { key: "country", value: "us-east" }, 271 { key: "zone", value: "us-east-1" }, 272 ]; 273 274 const tree = getLocality(localityTree, tiers); 275 276 assert.equal(tree, null); 277 }); 278 279 it("returns null if the first tier value does not exist", function() { 280 const tiers: LocalityTier[] = [ 281 { key: "region", value: "eu-north" }, 282 { key: "zone", value: "us-east-1" }, 283 ]; 284 285 const tree = getLocality(localityTree, tiers); 286 287 assert.equal(tree, null); 288 }); 289 290 it("returns null if the second tier key does not exist", function() { 291 const tiers: LocalityTier[] = [ 292 { key: "region", value: "us-east" }, 293 { key: "datacenter", value: "us-east-1" }, 294 ]; 295 296 const tree = getLocality(localityTree, tiers); 297 298 assert.equal(tree, null); 299 }); 300 301 it("returns null if the second tier value does not exist", function() { 302 const tiers: LocalityTier[] = [ 303 { key: "region", value: "us-east" }, 304 { key: "zone", value: "us-east-42" }, 305 ]; 306 307 const tree = getLocality(localityTree, tiers); 308 309 assert.equal(tree, null); 310 }); 311 }); 312 }); 313 314 describe("getLeaves", function() { 315 it("returns the leaves of a locality tree", function() { 316 const node1 = { 317 desc: { 318 node_id: 1, 319 locality: { 320 tiers: [ 321 { key: "region", value: "us-east" }, 322 { key: "zone", value: "us-east-1" }, 323 ], 324 }, 325 }, 326 }; 327 const node2 = { 328 desc: { 329 node_id: 1, 330 locality: { 331 tiers: [ 332 { key: "region", value: "us-east" }, 333 ], 334 }, 335 }, 336 }; 337 // Uneven tree depth is intentional. 338 const localityTree: LocalityTree = { 339 tiers: [], 340 localities: { 341 region: { 342 "us-east": { 343 tiers: [{ key: "region", value: "us-east" }], 344 localities: { 345 zone: { 346 "us-east-1": { 347 tiers: [ 348 { key: "region", value: "us-east" }, 349 { key: "zone", value: "us-east-1" }, 350 ], 351 localities: {}, 352 nodes: [node1], 353 }, 354 }, 355 }, 356 nodes: [], 357 }, 358 "us-west": { 359 tiers: [{ key: "region", value: "us-west" }], 360 localities: {}, 361 nodes: [node2], 362 }, 363 }, 364 }, 365 nodes: [], 366 }; 367 368 const leaves = getLeaves(localityTree); 369 370 assert.deepEqual(leaves, [node1, node2]); 371 }); 372 }); 373 374 describe("getLocalityLabel", function() { 375 describe("with an empty list of tiers", function() { 376 it("returns the string \"Cluster\"", function() { 377 const label = getLocalityLabel([]); 378 379 assert.equal(label, "Cluster"); 380 }); 381 }); 382 383 describe("with a single tier", function() { 384 it("returns the tier label", function() { 385 const key = "region"; 386 const value = "us-east-1"; 387 388 const label = getLocalityLabel([{ key, value }]); 389 390 assert.equal(label, key + "=" + value); 391 }); 392 }); 393 394 describe("with multiple tiers", function() { 395 it("returns the last tier's label", function() { 396 const key = "region"; 397 const value = "us-east-1"; 398 399 const label = getLocalityLabel([ 400 { key: "country", value: "us" }, 401 { key, value }, 402 ]); 403 404 assert.equal(label, key + "=" + value); 405 }); 406 }); 407 }); 408 409 describe("allNodesHaveLocality", function() { 410 411 it("returns false if a node exists without a locality", function() { 412 const nodes: INodeStatus[] = [ 413 { desc: { node_id: 1, locality: { tiers: [] } } }, 414 { desc: { node_id: 2, locality: { tiers: [{ key: "region", value: "us-east-1" }] } } }, 415 ]; 416 417 assert.isFalse(allNodesHaveLocality(nodes)); 418 }); 419 420 it("returns true if all nodes have localities", function() { 421 const nodes: INodeStatus[] = [ 422 { desc: { node_id: 1, locality: { tiers: [{ key: "region", value: "us-west-1" }] } } }, 423 { desc: { node_id: 2, locality: { tiers: [{ key: "region", value: "us-east-1" }] } } }, 424 ]; 425 426 assert.isTrue(allNodesHaveLocality(nodes)); 427 }); 428 429 });