/**
 * Copyright (c) HashiCorp, Inc.
 * SPDX-License-Identifier: MPL-2.0
 */

import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import {
  flattenDataset,
  formatByMonths,
  formatByNamespace,
  homogenizeClientNaming,
  sortMonthsByTimestamp,
  namespaceArrayToObject,
} from 'core/utils/client-count-utils';
import { parseAPITimestamp } from 'core/utils/date-formatters';
import isBefore from 'date-fns/isBefore';
import isAfter from 'date-fns/isAfter';

// import { setupMirage } from 'ember-cli-mirage/test-support';
// import ENV from 'vault/config/environment';
// import { formatRFC3339 } from 'date-fns';

module('Integration | Util | client count utils', function (hooks) {
  setupTest(hooks);
  // setupMirage(hooks);

  // TODO: wire up to stubbed API/mirage?
  // hooks.before(function () {
  //   ENV['ember-cli-mirage'].handler = 'clients';
  // });
  // hooks.after(function () {
  //   ENV['ember-cli-mirage'].handler = null;
  // });

  /* MONTHS array contains: (update when backend work done on months )
  - one month with only old client naming
  */

  const MONTHS = [
    {
      timestamp: '2021-05-01T00:00:00Z',
      counts: {
        distinct_entities: 25,
        non_entity_tokens: 25,
        clients: 50,
      },
      namespaces: [
        {
          namespace_id: 'root',
          namespace_path: '',
          counts: {
            distinct_entities: 13,
            non_entity_tokens: 7,
            clients: 20,
          },
          mounts: [
            {
              mount_path: 'auth/up2/',
              counts: {
                distinct_entities: 8,
                non_entity_tokens: 0,
                clients: 8,
              },
            },
            {
              mount_path: 'auth/up1/',
              counts: {
                distinct_entities: 0,
                non_entity_tokens: 7,
                clients: 7,
              },
            },
          ],
        },
        {
          namespace_id: 's07UR',
          namespace_path: 'ns1/',
          counts: {
            distinct_entities: 5,
            non_entity_tokens: 5,
            clients: 10,
          },
          mounts: [
            {
              mount_path: 'auth/up1/',
              counts: {
                distinct_entities: 0,
                non_entity_tokens: 5,
                clients: 5,
              },
            },
            {
              mount_path: 'auth/up2/',
              counts: {
                distinct_entities: 5,
                non_entity_tokens: 0,
                clients: 5,
              },
            },
          ],
        },
      ],
      new_clients: {
        counts: {
          distinct_entities: 3,
          non_entity_tokens: 2,
          clients: 5,
        },
        namespaces: [
          {
            namespace_id: 'root',
            namespace_path: '',
            counts: {
              distinct_entities: 3,
              non_entity_tokens: 2,
              clients: 5,
            },
            mounts: [
              {
                mount_path: 'auth/up2/',
                counts: {
                  distinct_entities: 3,
                  non_entity_tokens: 0,
                  clients: 3,
                },
              },
              {
                mount_path: 'auth/up1/',
                counts: {
                  distinct_entities: 0,
                  non_entity_tokens: 2,
                  clients: 2,
                },
              },
            ],
          },
        ],
      },
    },
    {
      timestamp: '2021-10-01T00:00:00Z',
      counts: {
        distinct_entities: 20,
        entity_clients: 20,
        non_entity_tokens: 20,
        non_entity_clients: 20,
        clients: 40,
      },
      namespaces: [
        {
          namespace_id: 'root',
          namespace_path: '',
          counts: {
            distinct_entities: 8,
            entity_clients: 8,
            non_entity_tokens: 7,
            non_entity_clients: 7,
            clients: 15,
          },
          mounts: [
            {
              mount_path: 'auth/up2/',
              counts: {
                distinct_entities: 8,
                entity_clients: 8,
                non_entity_tokens: 0,
                non_entity_clients: 0,
                clients: 8,
              },
            },
            {
              mount_path: 'auth/up1/',
              counts: {
                distinct_entities: 0,
                entity_clients: 0,
                non_entity_tokens: 7,
                non_entity_clients: 7,
                clients: 7,
              },
            },
          ],
        },
        {
          namespace_id: 's07UR',
          namespace_path: 'ns1/',
          counts: {
            distinct_entities: 5,
            entity_clients: 5,
            non_entity_tokens: 5,
            non_entity_clients: 5,
            clients: 10,
          },
          mounts: [
            {
              mount_path: 'auth/up1/',
              counts: {
                distinct_entities: 0,
                entity_clients: 0,
                non_entity_tokens: 5,
                non_entity_clients: 5,
                clients: 5,
              },
            },
            {
              mount_path: 'auth/up2/',
              counts: {
                distinct_entities: 5,
                entity_clients: 5,
                non_entity_tokens: 0,
                non_entity_clients: 0,
                clients: 5,
              },
            },
          ],
        },
      ],
      new_clients: {
        counts: {
          distinct_entities: 3,
          entity_clients: 3,
          non_entity_tokens: 2,
          non_entity_clients: 2,
          clients: 5,
        },
        namespaces: [
          {
            namespace_id: 'root',
            namespace_path: '',
            counts: {
              distinct_entities: 3,
              entity_clients: 3,
              non_entity_tokens: 2,
              non_entity_clients: 2,
              clients: 5,
            },
            mounts: [
              {
                mount_path: 'auth/up2/',
                counts: {
                  distinct_entities: 3,
                  entity_clients: 3,
                  non_entity_tokens: 0,
                  non_entity_clients: 0,
                  clients: 3,
                },
              },
              {
                mount_path: 'auth/up1/',
                counts: {
                  distinct_entities: 0,
                  entity_clients: 0,
                  non_entity_tokens: 2,
                  non_entity_clients: 2,
                  clients: 2,
                },
              },
            ],
          },
        ],
      },
    },
    {
      timestamp: '2021-09-01T00:00:00Z',
      counts: {
        distinct_entities: 0,
        entity_clients: 17,
        non_entity_tokens: 0,
        non_entity_clients: 18,
        clients: 35,
      },
      namespaces: [
        {
          namespace_id: 'oImjk',
          namespace_path: 'ns2/',
          counts: {
            distinct_entities: 0,
            entity_clients: 5,
            non_entity_tokens: 0,
            non_entity_clients: 5,
            clients: 10,
          },
          mounts: [
            {
              mount_path: 'auth/up1/',
              counts: {
                distinct_entities: 0,
                entity_clients: 0,
                non_entity_tokens: 0,
                non_entity_clients: 5,
                clients: 5,
              },
            },
            {
              mount_path: 'auth/up2/',
              counts: {
                distinct_entities: 0,
                entity_clients: 5,
                non_entity_tokens: 0,
                non_entity_clients: 0,
                clients: 5,
              },
            },
          ],
        },
        {
          namespace_id: 'root',
          namespace_path: '',
          counts: {
            distinct_entities: 0,
            entity_clients: 2,
            non_entity_tokens: 0,
            non_entity_clients: 3,
            clients: 5,
          },
          mounts: [
            {
              mount_path: 'auth/up1/',
              counts: {
                distinct_entities: 0,
                entity_clients: 0,
                non_entity_tokens: 0,
                non_entity_clients: 3,
                clients: 3,
              },
            },
            {
              mount_path: 'auth/up2/',
              counts: {
                distinct_entities: 0,
                entity_clients: 2,
                non_entity_tokens: 0,
                non_entity_clients: 0,
                clients: 2,
              },
            },
          ],
        },
        {
          namespace_id: 's07UR',
          namespace_path: 'ns1/',
          counts: {
            distinct_entities: 0,
            entity_clients: 3,
            non_entity_tokens: 0,
            non_entity_clients: 2,
            clients: 5,
          },
          mounts: [
            {
              mount_path: 'auth/up2/',
              counts: {
                distinct_entities: 0,
                entity_clients: 3,
                non_entity_tokens: 0,
                non_entity_clients: 0,
                clients: 3,
              },
            },
            {
              mount_path: 'auth/up1/',
              counts: {
                distinct_entities: 0,
                entity_clients: 0,
                non_entity_tokens: 0,
                non_entity_clients: 2,
                clients: 2,
              },
            },
          ],
        },
      ],
      new_clients: {
        counts: {
          distinct_entities: 0,
          entity_clients: 10,
          non_entity_tokens: 0,
          non_entity_clients: 10,
          clients: 20,
        },
        namespaces: [
          {
            namespace_id: 'oImjk',
            namespace_path: 'ns2/',
            counts: {
              distinct_entities: 0,
              entity_clients: 5,
              non_entity_tokens: 0,
              non_entity_clients: 5,
              clients: 10,
            },
            mounts: [
              {
                mount_path: 'auth/up1/',
                counts: {
                  distinct_entities: 0,
                  entity_clients: 0,
                  non_entity_tokens: 0,
                  non_entity_clients: 5,
                  clients: 5,
                },
              },
              {
                mount_path: 'auth/up2/',
                counts: {
                  distinct_entities: 0,
                  entity_clients: 5,
                  non_entity_tokens: 0,
                  non_entity_clients: 0,
                  clients: 5,
                },
              },
            ],
          },
          {
            namespace_id: 'root',
            namespace_path: '',
            counts: {
              distinct_entities: 0,
              entity_clients: 2,
              non_entity_tokens: 0,
              non_entity_clients: 3,
              clients: 5,
            },
            mounts: [
              {
                mount_path: 'auth/up1/',
                counts: {
                  distinct_entities: 0,
                  entity_clients: 0,
                  non_entity_tokens: 0,
                  non_entity_clients: 3,
                  clients: 3,
                },
              },
              {
                mount_path: 'auth/up2/',
                counts: {
                  distinct_entities: 0,
                  entity_clients: 2,
                  non_entity_tokens: 0,
                  non_entity_clients: 0,
                  clients: 2,
                },
              },
            ],
          },
          {
            namespace_id: 's07UR',
            namespace_path: 'ns1/',
            counts: {
              distinct_entities: 0,
              entity_clients: 3,
              non_entity_tokens: 0,
              non_entity_clients: 2,
              clients: 5,
            },
            mounts: [
              {
                mount_path: 'auth/up2/',
                counts: {
                  distinct_entities: 0,
                  entity_clients: 3,
                  non_entity_tokens: 0,
                  non_entity_clients: 0,
                  clients: 3,
                },
              },
              {
                mount_path: 'auth/up1/',
                counts: {
                  distinct_entities: 0,
                  entity_clients: 0,
                  non_entity_tokens: 0,
                  non_entity_clients: 2,
                  clients: 2,
                },
              },
            ],
          },
        ],
      },
    },
  ];

  const BY_NAMESPACE = [
    {
      namespace_id: '96OwG',
      namespace_path: 'test-ns/',
      counts: {
        distinct_entities: 18290,
        entity_clients: 18290,
        non_entity_tokens: 18738,
        non_entity_clients: 18738,
        clients: 37028,
      },
      mounts: [
        {
          mount_path: 'path-1',
          counts: {
            distinct_entities: 6403,
            entity_clients: 6403,
            non_entity_tokens: 6300,
            non_entity_clients: 6300,
            clients: 12703,
          },
        },
        {
          mount_path: 'path-2',
          counts: {
            distinct_entities: 5699,
            entity_clients: 5699,
            non_entity_tokens: 6777,
            non_entity_clients: 6777,
            clients: 12476,
          },
        },
        {
          mount_path: 'path-3',
          counts: {
            distinct_entities: 6188,
            entity_clients: 6188,
            non_entity_tokens: 5661,
            non_entity_clients: 5661,
            clients: 11849,
          },
        },
      ],
    },
    {
      namespace_id: 'root',
      namespace_path: '',
      counts: {
        distinct_entities: 19099,
        entity_clients: 19099,
        non_entity_tokens: 17781,
        non_entity_clients: 17781,
        clients: 36880,
      },
      mounts: [
        {
          mount_path: 'path-3',
          counts: {
            distinct_entities: 6863,
            entity_clients: 6863,
            non_entity_tokens: 6801,
            non_entity_clients: 6801,
            clients: 13664,
          },
        },
        {
          mount_path: 'path-2',
          counts: {
            distinct_entities: 6047,
            entity_clients: 6047,
            non_entity_tokens: 5957,
            non_entity_clients: 5957,
            clients: 12004,
          },
        },
        {
          mount_path: 'path-1',
          counts: {
            distinct_entities: 6189,
            entity_clients: 6189,
            non_entity_tokens: 5023,
            non_entity_clients: 5023,
            clients: 11212,
          },
        },
        {
          mount_path: 'auth/up2/',
          counts: {
            distinct_entities: 0,
            entity_clients: 50,
            non_entity_tokens: 0,
            non_entity_clients: 23,
            clients: 73,
          },
        },
        {
          mount_path: 'auth/up1/',
          counts: {
            distinct_entities: 0,
            entity_clients: 25,
            non_entity_tokens: 0,
            non_entity_clients: 15,
            clients: 40,
          },
        },
      ],
    },
  ];

  const EMPTY_MONTHS = [
    {
      timestamp: '2021-06-01T00:00:00Z',
      counts: null,
      namespaces: null,
      new_clients: null,
    },
    {
      timestamp: '2021-07-01T00:00:00Z',
      counts: null,
      namespaces: null,
      new_clients: null,
    },
  ];

  const SOME_OBJECT = { foo: 'bar' };

  test('formatByMonths: formats the months array', async function (assert) {
    assert.expect(103);
    const keyNameAssertions = (object, objectName) => {
      const objectKeys = Object.keys(object);
      assert.false(objectKeys.includes('counts'), `${objectName} doesn't include 'counts' key`);
      assert.true(objectKeys.includes('clients'), `${objectName} includes 'clients' key`);
      assert.true(objectKeys.includes('entity_clients'), `${objectName} includes 'entity_clients' key`);
      assert.true(
        objectKeys.includes('non_entity_clients'),
        `${objectName} includes 'non_entity_clients' key`
      );
    };
    const assertClientCounts = (object, originalObject) => {
      const newObjectKeys = ['clients', 'entity_clients', 'non_entity_clients'];
      const originalKeys = Object.keys(originalObject.counts).includes('entity_clients')
        ? newObjectKeys
        : ['clients', 'distinct_entities', 'non_entity_tokens'];

      newObjectKeys.forEach((key, i) => {
        assert.strictEqual(
          object[key],
          originalObject.counts[originalKeys[i]],
          `${object.month} ${key} equal original counts`
        );
      });
    };

    const formattedMonths = formatByMonths(MONTHS);
    assert.notEqual(formattedMonths, MONTHS, 'does not modify original array');

    formattedMonths.forEach((month) => {
      const originalMonth = MONTHS.find((m) => month.month === parseAPITimestamp(m.timestamp, 'M/yy'));
      // if originalMonth is found (not undefined) then the formatted month has an accurate, parsed timestamp
      assert.ok(originalMonth, `month has parsed timestamp of ${month.month}`);
      assert.ok(month.namespaces_by_key, `month includes 'namespaces_by_key' key`);

      keyNameAssertions(month, 'formatted month');
      assertClientCounts(month, originalMonth);

      assert.ok(month.new_clients.month, 'new clients key has a month key');
      keyNameAssertions(month.new_clients, 'formatted month new_clients');
      assertClientCounts(month.new_clients, originalMonth.new_clients);

      month.namespaces.forEach((namespace) => keyNameAssertions(namespace, 'namespace within month'));
      month.new_clients.namespaces.forEach((namespace) =>
        keyNameAssertions(namespace, 'new client namespaces within month')
      );
    });

    // method fails gracefully
    const expected = [
      {
        counts: null,
        month: '6/21',
        namespaces: [],
        namespaces_by_key: {},
        new_clients: {
          month: '6/21',
          namespaces: [],
          timestamp: '2021-06-01T00:00:00Z',
        },
        timestamp: '2021-06-01T00:00:00Z',
      },
      {
        counts: null,
        month: '7/21',
        namespaces: [],
        namespaces_by_key: {},
        new_clients: {
          month: '7/21',
          namespaces: [],
          timestamp: '2021-07-01T00:00:00Z',
        },
        timestamp: '2021-07-01T00:00:00Z',
      },
    ];
    assert.strictEqual(formatByMonths(SOME_OBJECT), SOME_OBJECT, 'it returns if arg is not an array');
    assert.propEqual(expected, formatByMonths(EMPTY_MONTHS), 'it does not error with null months');
    assert.ok(formatByMonths([...EMPTY_MONTHS, ...MONTHS]), 'it does not error with combined data');
  });

  test('formatByNamespace: formats namespace arrays with and without mounts', async function (assert) {
    assert.expect(102);
    const keyNameAssertions = (object, objectName) => {
      const objectKeys = Object.keys(object);
      assert.false(objectKeys.includes('counts'), `${objectName} doesn't include 'counts' key`);
      assert.true(objectKeys.includes('label'), `${objectName} includes 'label' key`);
      assert.true(objectKeys.includes('clients'), `${objectName} includes 'clients' key`);
      assert.true(objectKeys.includes('entity_clients'), `${objectName} includes 'entity_clients' key`);
      assert.true(
        objectKeys.includes('non_entity_clients'),
        `${objectName} includes 'non_entity_clients' key`
      );
    };
    const keyValueAssertions = (object, pathName, originalObject) => {
      const keysToAssert = ['clients', 'entity_clients', 'non_entity_clients'];
      assert.strictEqual(object.label, originalObject[pathName], `${pathName} matches label`);

      keysToAssert.forEach((key) => {
        assert.strictEqual(object[key], originalObject.counts[key], `number of ${key} equal original`);
      });
    };

    const formattedNamespaces = formatByNamespace(BY_NAMESPACE);
    assert.notEqual(formattedNamespaces, MONTHS, 'does not modify original array');

    formattedNamespaces.forEach((namespace) => {
      const origNamespace = BY_NAMESPACE.find((ns) => ns.namespace_path === namespace.label);
      keyNameAssertions(namespace, 'formatted namespace');
      keyValueAssertions(namespace, 'namespace_path', origNamespace);

      namespace.mounts.forEach((mount) => {
        const origMount = origNamespace.mounts.find((m) => m.mount_path === mount.label);
        keyNameAssertions(mount, 'formatted mount');
        keyValueAssertions(mount, 'mount_path', origMount);
      });
    });

    const nsWithoutMounts = {
      namespace_id: '96OwG',
      namespace_path: 'no-mounts-ns/',
      counts: {
        distinct_entities: 18290,
        entity_clients: 18290,
        non_entity_tokens: 18738,
        non_entity_clients: 18738,
        clients: 37028,
      },
      mounts: [],
    };

    const formattedNsWithoutMounts = formatByNamespace([nsWithoutMounts])[0];
    keyNameAssertions(formattedNsWithoutMounts, 'namespace without mounts');
    keyValueAssertions(formattedNsWithoutMounts, 'namespace_path', nsWithoutMounts);
    assert.strictEqual(formattedNsWithoutMounts.mounts.length, 0, 'formatted namespace has no mounts');

    assert.strictEqual(formatByNamespace(SOME_OBJECT), SOME_OBJECT, 'it returns if arg is not an array');
  });

  test('homogenizeClientNaming: homogenizes key names when both old and new keys exist, or just old key names', async function (assert) {
    assert.expect(168);
    const keyNameAssertions = (object, objectName) => {
      const objectKeys = Object.keys(object);
      assert.false(
        objectKeys.includes('distinct_entities'),
        `${objectName} doesn't include 'distinct_entities' key`
      );
      assert.false(
        objectKeys.includes('non_entity_tokens'),
        `${objectName} doesn't include 'non_entity_tokens' key`
      );
      assert.true(objectKeys.includes('entity_clients'), `${objectName} includes 'entity_clients' key`);
      assert.true(
        objectKeys.includes('non_entity_clients'),
        `${objectName} includes 'non_entity_clients' key`
      );
    };

    const transformedMonths = [...MONTHS];
    transformedMonths.forEach((month) => {
      month.counts = homogenizeClientNaming(month.counts);
      keyNameAssertions(month.counts, 'month counts');

      month.new_clients.counts = homogenizeClientNaming(month.new_clients.counts);
      keyNameAssertions(month.new_clients.counts, 'month new counts');

      month.namespaces.forEach((ns) => {
        ns.counts = homogenizeClientNaming(ns.counts);
        keyNameAssertions(ns.counts, 'namespace counts');

        ns.mounts.forEach((mount) => {
          mount.counts = homogenizeClientNaming(mount.counts);
          keyNameAssertions(mount.counts, 'mount counts');
        });
      });

      month.new_clients.namespaces.forEach((ns) => {
        ns.counts = homogenizeClientNaming(ns.counts);
        keyNameAssertions(ns.counts, 'namespace new counts');

        ns.mounts.forEach((mount) => {
          mount.counts = homogenizeClientNaming(mount.counts);
          keyNameAssertions(mount.counts, 'mount new counts');
        });
      });
    });
  });

  test('flattenDataset: removes the counts key and flattens the dataset', async function (assert) {
    assert.expect(22);
    const flattenedNamespace = flattenDataset(BY_NAMESPACE[0]);
    const flattenedMount = flattenDataset(BY_NAMESPACE[0].mounts[0]);
    const flattenedMonth = flattenDataset(MONTHS[0]);
    const flattenedNewMonthClients = flattenDataset(MONTHS[0].new_clients);
    const objectNullCounts = { counts: null, foo: 'bar' };

    const keyNameAssertions = (object, objectName) => {
      const objectKeys = Object.keys(object);
      assert.false(objectKeys.includes('counts'), `${objectName} doesn't include 'counts' key`);
      assert.true(objectKeys.includes('clients'), `${objectName} includes 'clients' key`);
      assert.true(objectKeys.includes('entity_clients'), `${objectName} includes 'entity_clients' key`);
      assert.true(
        objectKeys.includes('non_entity_clients'),
        `${objectName} includes 'non_entity_clients' key`
      );
    };

    keyNameAssertions(flattenedNamespace, 'namespace object');
    keyNameAssertions(flattenedMount, 'mount object');
    keyNameAssertions(flattenedMonth, 'month object');
    keyNameAssertions(flattenedNewMonthClients, 'month new_clients object');

    assert.strictEqual(
      flattenDataset(SOME_OBJECT),
      SOME_OBJECT,
      "it returns original object if counts key doesn't exist"
    );

    assert.strictEqual(
      flattenDataset(objectNullCounts),
      objectNullCounts,
      'it returns original object if counts are null'
    );

    assert.propEqual(
      ['some array'],
      flattenDataset(['some array']),
      'it fails gracefully if an array is passed in'
    );
    assert.strictEqual(flattenDataset(null), null, 'it fails gracefully if null is passed in');
    assert.strictEqual(
      flattenDataset('some string'),
      'some string',
      'it fails gracefully if a string is passed in'
    );
    assert.propEqual(
      new Object(),
      flattenDataset(new Object()),
      'it fails gracefully if an empty object is passed in'
    );
  });

  test('sortMonthsByTimestamp: sorts timestamps chronologically, oldest to most recent', async function (assert) {
    assert.expect(4);
    const sortedMonths = sortMonthsByTimestamp(MONTHS);
    assert.ok(
      isBefore(parseAPITimestamp(sortedMonths[0].timestamp), parseAPITimestamp(sortedMonths[1].timestamp)),
      'first timestamp date is earlier than second'
    );
    assert.ok(
      isAfter(parseAPITimestamp(sortedMonths[2].timestamp), parseAPITimestamp(sortedMonths[1].timestamp)),
      'third timestamp date is later second'
    );
    assert.notEqual(sortedMonths[1], MONTHS[1], 'it does not modify original array');
    assert.strictEqual(sortedMonths[0], MONTHS[0], 'it does not modify original array');
  });

  test('namespaceArrayToObject: transforms data without modifying original', async function (assert) {
    assert.expect(30);

    const assertClientCounts = (object, originalObject) => {
      const valuesToCheck = ['clients', 'entity_clients', 'non_entity_clients'];

      valuesToCheck.forEach((key) => {
        assert.strictEqual(object[key], originalObject[key], `${key} equal original counts`);
      });
    };
    const totalClientsByNamespace = formatByNamespace(MONTHS[1].namespaces);
    const newClientsByNamespace = formatByNamespace(MONTHS[1].new_clients.namespaces);

    const byNamespaceKeyObject = namespaceArrayToObject(
      totalClientsByNamespace,
      newClientsByNamespace,
      '10/21'
    );

    assert.propEqual(
      totalClientsByNamespace,
      formatByNamespace(MONTHS[1].namespaces),
      'it does not modify original array'
    );
    assert.propEqual(
      newClientsByNamespace,
      formatByNamespace(MONTHS[1].new_clients.namespaces),
      'it does not modify original array'
    );

    const namespaceKeys = Object.keys(byNamespaceKeyObject);
    namespaceKeys.forEach((nsKey) => {
      const newNsObject = byNamespaceKeyObject[nsKey];
      const originalNsData = totalClientsByNamespace.find((ns) => ns.label === nsKey);
      assertClientCounts(newNsObject, originalNsData);
      const mountKeys = Object.keys(newNsObject.mounts_by_key);
      mountKeys.forEach((mKey) => {
        const mountData = originalNsData.mounts.find((m) => m.label === mKey);
        assertClientCounts(newNsObject.mounts_by_key[mKey], mountData);
      });
    });

    namespaceKeys.forEach((nsKey) => {
      const newNsObject = byNamespaceKeyObject[nsKey];
      const originalNsData = newClientsByNamespace.find((ns) => ns.label === nsKey);
      if (!originalNsData) return;
      assertClientCounts(newNsObject.new_clients, originalNsData);
      const mountKeys = Object.keys(newNsObject.mounts_by_key);

      mountKeys.forEach((mKey) => {
        const mountData = originalNsData.mounts.find((m) => m.label === mKey);
        assertClientCounts(newNsObject.mounts_by_key[mKey].new_clients, mountData);
      });
    });

    assert.propEqual(
      {},
      namespaceArrayToObject(null, null, '10/21'),
      'returns an empty object when totalClientsByNamespace = null'
    );
  });
});
