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  });