Repository: Synthetixio/synthetix-v3
Pull request #1036 (2022-07-04 18:01:32)

Make validate-council scripts more flexible

codecov[bot]: # [Codecov](https://codecov.io/gh/Synthetixio/synthetix-v3/pull/1036?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Synthetixio) Report
> Merging [#1036](https://codecov.io/gh/Synthetixio/synthetix-v3/pull/1036?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Synthetixio) (71325bf) into [main](https://codecov.io/gh/Synthetixio/synthetix-v3/commit/ac3738b753d3a33af5f4cea84e2098141fd1b4c8?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Synthetixio) (ac3738b) will **increase** coverage by `0.94%`.
> The diff coverage is `50.00%`.

```diff
@@            Coverage Diff             @@
##             main    #1036      +/-   ##
==========================================
+ Coverage   98.27%   99.22%   +0.94%     
==========================================
  Files          90       24      -66     
  Lines        1507      385    -1122     
  Branches      115        0     -115     
==========================================
- Hits         1481      382    -1099     
+ Misses         26        3      -23     
```

| Flag | Coverage Δ | |
|---|---|---|
| ambassador-council | `?` | |
| core-contracts | `?` | |
| core-js | `99.22% <50.00%> (-0.26%)` | :arrow_down: |
| core-modules | `?` | |
| deployer | `?` | |
| grants-council | `?` | |
| spartan-council | `?` | |
| synthetix-governance | `?` | |
| synthetix-main | `?` | |
| treasury-council | `?` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Synthetixio#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/Synthetixio/synthetix-v3/pull/1036?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Synthetixio) | Coverage Δ | |
|---|---|---|
| [packages/core-js/utils/misc/dates.js](https://codecov.io/gh/Synthetixio/synthetix-v3/pull/1036/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Synthetixio#diff-cGFja2FnZXMvY29yZS1qcy91dGlscy9taXNjL2RhdGVzLmpz) | `85.71% <50.00%> (-14.29%)` | :arrow_down: |
| [packages/deployer/subtasks/validate-satellites.js](https://codecov.io/gh/Synthetixio/synthetix-v3/pull/1036/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Synthetixio#diff-cGFja2FnZXMvZGVwbG95ZXIvc3VidGFza3MvdmFsaWRhdGUtc2F0ZWxsaXRlcy5qcw==) | | |
| [...ix-governance/contracts/modules/ElectionModule.sol](https://codecov.io/gh/Synthetixio/synthetix-v3/pull/1036/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Synthetixio#diff-cGFja2FnZXMvc3ludGhldGl4LWdvdmVybmFuY2UvY29udHJhY3RzL21vZHVsZXMvRWxlY3Rpb25Nb2R1bGUuc29s) | | |
| [...kages/deployer/internal/router-source-validator.js](https://codecov.io/gh/Synthetixio/synthetix-v3/pull/1036/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Synthetixio#diff-cGFja2FnZXMvZGVwbG95ZXIvaW50ZXJuYWwvcm91dGVyLXNvdXJjZS12YWxpZGF0b3IuanM=) | | |
| [...ackages/core-contracts/contracts/utils/SetUtil.sol](https://codecov.io/gh/Synthetixio/synthetix-v3/pull/1036/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Synthetixio#diff-cGFja2FnZXMvY29yZS1jb250cmFjdHMvY29udHJhY3RzL3V0aWxzL1NldFV0aWwuc29s) | | |
| [packages/deployer/subtasks/get-deployment-info.js](https://codecov.io/gh/Synthetixio/synthetix-v3/pull/1036/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Synthetixio#diff-cGFja2FnZXMvZGVwbG95ZXIvc3VidGFza3MvZ2V0LWRlcGxveW1lbnQtaW5mby5qcw==) | | |
| [packages/deployer/subtasks/deploy-router.js](https://codecov.io/gh/Synthetixio/synthetix-v3/pull/1036/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Synthetixio#diff-cGFja2FnZXMvZGVwbG95ZXIvc3VidGFza3MvZGVwbG95LXJvdXRlci5qcw==) | | |
| [packages/deployer/subtasks/upgrade-proxy.js](https://codecov.io/gh/Synthetixio/synthetix-v3/pull/1036/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Synthetixio#diff-cGFja2FnZXMvZGVwbG95ZXIvc3VidGFza3MvdXBncmFkZS1wcm94eS5qcw==) | | |
| [.../synthetix-main/contracts/modules/SynthsModule.sol](https://codecov.io/gh/Synthetixio/synthetix-v3/pull/1036/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Synthetixio#diff-cGFja2FnZXMvc3ludGhldGl4LW1haW4vY29udHJhY3RzL21vZHVsZXMvU3ludGhzTW9kdWxlLnNvbA==) | | |
| [packages/deployer/subtasks/load-deployment.js](https://codecov.io/gh/Synthetixio/synthetix-v3/pull/1036/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Synthetixio#diff-cGFja2FnZXMvZGVwbG95ZXIvc3VidGFza3MvbG9hZC1kZXBsb3ltZW50Lmpz) | | |
| ... and [57 more](https://codecov.io/gh/Synthetixio/synthetix-v3/pull/1036/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Synthetixio) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Synthetixio/synthetix-v3/pull/1036?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Synthetixio).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Synthetixio)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/Synthetixio/synthetix-v3/pull/1036?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Synthetixio). Last update [ac3738b...71325bf](https://codecov.io/gh/Synthetixio/synthetix-v3/pull/1036?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Synthetixio). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Synthetixio).


Patch:
diff --git a/packages/core-js/utils/misc/dates.js b/packages/core-js/utils/misc/dates.js
index 163580e6..8a5bbff7 100644
--- a/packages/core-js/utils/misc/dates.js
+++ b/packages/core-js/utils/misc/dates.js
@@ -11,8 +11,12 @@ function formatDate(date = new Date()) {
   return date.toISOString().slice(0, 10);
 }
 
-function getUnixTimestamp() {
-  return Math.floor(new Date().getTime() / 1000);
+function getUnixTimestamp(date = new Date()) {
+  return Math.floor(date.getTime() / 1000);
+}
+
+function fromUnixTimestamp(timestamp) {
+  return new Date(timestamp * 1000);
 }
 
 const daysToSeconds = (days) => days * 3600 * 24;
@@ -20,5 +24,6 @@ const daysToSeconds = (days) => days * 3600 * 24;
 module.exports = {
   formatDate,
   getUnixTimestamp,
+  fromUnixTimestamp,
   daysToSeconds,
 };
diff --git a/packages/scripts/data/councils-epoch-0.json b/packages/scripts/data/councils-epoch-0.json
index c45e1d31..6c4cf8a7 100644
--- a/packages/scripts/data/councils-epoch-0.json
+++ b/packages/scripts/data/councils-epoch-0.json
@@ -3,13 +3,15 @@
     "name": "ambassador-council",
     "owner": "0x6cd3f878852769e04A723A5f66CA7DD4d9E38A6C",
     "nominatedOwner": "0x0000000000000000000000000000000000000000",
-    "getNominationPeriodStartDate": "2022-06-10",
-    "getVotingPeriodStartDate": "2022-06-17",
-    "getEpochEndDate": "2022-07-01",
-    "getNextEpochSeatCount": 3,
+    "nominationPeriodStartDate": "2022-06-10",
+    "votingPeriodStartDate": "2022-06-17",
+    "epochEndDate": "2022-07-01",
+    "nextEpochSeatCount": 3,
+    "epochIndex": 0,
+    "currentPeriod": 0,
     "councilTokenName": "Synthetix Ambassador Council Token",
     "councilTokenSymbol": "SNX-ACT",
-    "getCouncilMembers": [
+    "councilMembers": [
       "0xDe910777C787903F78C89e7a0bf7F4C435cBB1Fe"
     ]
   },
@@ -17,13 +19,15 @@
     "name": "grants-council",
     "owner": "0x6cd3f878852769e04A723A5f66CA7DD4d9E38A6C",
     "nominatedOwner": "0x0000000000000000000000000000000000000000",
-    "getNominationPeriodStartDate": "2022-06-10",
-    "getVotingPeriodStartDate": "2022-06-17",
-    "getEpochEndDate": "2022-07-01",
-    "getNextEpochSeatCount": 5,
+    "nominationPeriodStartDate": "2022-06-10",
+    "votingPeriodStartDate": "2022-06-17",
+    "epochEndDate": "2022-07-01",
+    "nextEpochSeatCount": 5,
+    "epochIndex": 0,
+    "currentPeriod": 0,
     "councilTokenName": "Synthetix Grants Council Token",
     "councilTokenSymbol": "SNX-GCT",
-    "getCouncilMembers": [
+    "councilMembers": [
       "0xDe910777C787903F78C89e7a0bf7F4C435cBB1Fe"
     ]
   },
@@ -31,13 +35,15 @@
     "name": "spartan-council",
     "owner": "0x6cd3f878852769e04A723A5f66CA7DD4d9E38A6C",
     "nominatedOwner": "0x0000000000000000000000000000000000000000",
-    "getNominationPeriodStartDate": "2022-06-10",
-    "getVotingPeriodStartDate": "2022-06-17",
-    "getEpochEndDate": "2022-07-01",
-    "getNextEpochSeatCount": 8,
+    "nominationPeriodStartDate": "2022-06-10",
+    "votingPeriodStartDate": "2022-06-17",
+    "epochEndDate": "2022-07-01",
+    "nextEpochSeatCount": 8,
+    "epochIndex": 0,
+    "currentPeriod": 0,
     "councilTokenName": "Synthetix Spartan Council Token",
     "councilTokenSymbol": "SNX-SCT",
-    "getCouncilMembers": [
+    "councilMembers": [
       "0xDe910777C787903F78C89e7a0bf7F4C435cBB1Fe"
     ]
   },
@@ -45,13 +51,15 @@
     "name": "treasury-council",
     "owner": "0x6cd3f878852769e04A723A5f66CA7DD4d9E38A6C",
     "nominatedOwner": "0x0000000000000000000000000000000000000000",
-    "getNominationPeriodStartDate": "2022-06-10",
-    "getVotingPeriodStartDate": "2022-06-17",
-    "getEpochEndDate": "2022-07-01",
-    "getNextEpochSeatCount": 4,
+    "nominationPeriodStartDate": "2022-06-10",
+    "votingPeriodStartDate": "2022-06-17",
+    "epochEndDate": "2022-07-01",
+    "nextEpochSeatCount": 4,
+    "epochIndex": 0,
+    "currentPeriod": 0,
     "councilTokenName": "Synthetix Treasury Council Token",
     "councilTokenSymbol": "SNX-TCT",
-    "getCouncilMembers": [
+    "councilMembers": [
       "0xDe910777C787903F78C89e7a0bf7F4C435cBB1Fe"
     ]
   }
diff --git a/packages/scripts/data/councils-epoch-1.json b/packages/scripts/data/councils-epoch-1.json
index d71867bb..cff20bb9 100644
--- a/packages/scripts/data/councils-epoch-1.json
+++ b/packages/scripts/data/councils-epoch-1.json
@@ -3,13 +3,13 @@
     "name": "ambassador-council",
     "owner": "0x6cd3f878852769e04A723A5f66CA7DD4d9E38A6C",
     "nominatedOwner": "0x0000000000000000000000000000000000000000",
-    "getNominationPeriodStartDate": "2022-07-12T11:46:21.000Z",
-    "getVotingPeriodStartDate": "2022-07-19T11:46:21.000Z",
-    "getEpochEndDate": "2022-08-02T11:46:21.000Z",
-    "getNextEpochSeatCount": 3,
-    "getEpochIndex": 1,
-    "getCurrentPeriod": 0,
-    "getCouncilMembers": [
+    "nominationPeriodStartDate": "2022-09-10",
+    "votingPeriodStartDate": "2022-09-17",
+    "epochEndDate": "2022-10-01",
+    "nextEpochSeatCount": "3",
+    "epochIndex": "1",
+    "currentPeriod": "0",
+    "councilMembers": [
       "0x585639fBf797c1258eBA8875c080Eb63C833d252",
       "0x98Ab20307fdABa1ce8b16d69d22461c6dbe85459",
       "0xF68D2BfCecd7895BBa05a7451Dd09A1749026454"
@@ -21,13 +21,13 @@
     "name": "grants-council",
     "owner": "0x6cd3f878852769e04A723A5f66CA7DD4d9E38A6C",
     "nominatedOwner": "0x0000000000000000000000000000000000000000",
-    "getNominationPeriodStartDate": "2022-07-12T11:41:40.000Z",
-    "getVotingPeriodStartDate": "2022-07-19T11:41:40.000Z",
-    "getEpochEndDate": "2022-08-02T11:41:40.000Z",
-    "getNextEpochSeatCount": 5,
-    "getEpochIndex": 1,
-    "getCurrentPeriod": 0,
-    "getCouncilMembers": [
+    "nominationPeriodStartDate": "2022-09-10",
+    "votingPeriodStartDate": "2022-09-17",
+    "epochEndDate": "2022-10-01",
+    "nextEpochSeatCount": "5",
+    "epochIndex": "1",
+    "currentPeriod": "0",
+    "councilMembers": [
       "0xE1f02F7E90ea5F21D0AC6F12c659C3484c143B03",
       "0x1a207bEefC754735871CEEb4C506686F044B1c41",
       "0x4f370B4d03D2b46CcE26F1aEFE142708E03D7FFE",
@@ -41,13 +41,13 @@
     "name": "spartan-council",
     "owner": "0x6cd3f878852769e04A723A5f66CA7DD4d9E38A6C",
     "nominatedOwner": "0x0000000000000000000000000000000000000000",
-    "getNominationPeriodStartDate": "2022-07-12T11:40:43.000Z",
-    "getVotingPeriodStartDate": "2022-07-19T11:40:43.000Z",
-    "getEpochEndDate": "2022-08-02T11:40:43.000Z",
-    "getNextEpochSeatCount": 8,
-    "getEpochIndex": 1,
-    "getCurrentPeriod": 0,
-    "getCouncilMembers": [
+    "nominationPeriodStartDate": "2022-09-10",
+    "votingPeriodStartDate": "2022-09-17",
+    "epochEndDate": "2022-10-01",
+    "nextEpochSeatCount": "8",
+    "epochIndex": "1",
+    "currentPeriod": "0",
+    "councilMembers": [
       "0x45a10F35BeFa4aB841c77860204b133118B7CcAE",
       "0x42f9134E9d3Bf7eEE1f8A5Ac2a4328B059E7468c",
       "0xA9903BDA477b9A57BD795AdFf9922cB98DB65F04",
@@ -64,13 +64,13 @@
     "name": "treasury-council",
     "owner": "0x6cd3f878852769e04A723A5f66CA7DD4d9E38A6C",
     "nominatedOwner": "0x0000000000000000000000000000000000000000",
-    "getNominationPeriodStartDate": "2022-07-12T11:38:12.000Z",
-    "getVotingPeriodStartDate": "2022-07-19T11:38:12.000Z",
-    "getEpochEndDate": "2022-08-02T11:38:12.000Z",
-    "getNextEpochSeatCount": 4,
-    "getEpochIndex": 1,
-    "getCurrentPeriod": 0,
-    "getCouncilMembers": [
+    "nominationPeriodStartDate": "2022-09-10",
+    "votingPeriodStartDate": "2022-09-17",
+    "epochEndDate": "2022-10-01",
+    "nextEpochSeatCount": "4",
+    "epochIndex": "1",
+    "currentPeriod": "0",
+    "councilMembers": [
       "0x604F127145CAC2467389124f0871227D3fD6F628",
       "0xD09583bBf77D0Da34Cd02612Fe41aF4AE37ebC95",
       "0xf04A6E38C6CFE0AcFbeB472888ed990787e69072",
diff --git a/packages/scripts/tasks/validate-councils.js b/packages/scripts/tasks/validate-councils.js
index 5f33b073..d484c5a4 100644
--- a/packages/scripts/tasks/validate-councils.js
+++ b/packages/scripts/tasks/validate-councils.js
@@ -1,9 +1,8 @@
 const path = require('path');
 const assert = require('assert/strict');
-const { equal, deepEqual } = require('assert/strict');
 const { task } = require('hardhat/config');
 const logger = require('@synthetixio/core-js/utils/io/logger');
-const { formatDate, getUnixTimestamp } = require('@synthetixio/core-js/utils/misc/dates');
+const { formatDate, fromUnixTimestamp } = require('@synthetixio/core-js/utils/misc/dates');
 const types = require('@synthetixio/core-js/utils/hardhat/argument-types');
 const { getDeployment, getDeploymentAbis } = require('@synthetixio/deployer/utils/deployments');
 const getPackageProxy = require('../internal/get-package-proxy');
@@ -20,7 +19,7 @@ task('validate-councils')
   .setAction(async ({ instance, epoch }, hre) => {
     epoch = Number(epoch);
 
-    logger.info(`Validating councils on network "${hre.network.name}"`);
+    logger.info(`Validating councils on network "${hre.network.name}" (${hre.network.config.url})`);
 
     const councils = await importJson(`${__dirname}/../data/councils-epoch-${epoch}.json`);
 
@@ -52,9 +51,9 @@ task('validate-councils')
         assert(deployment.properties.completed, `Deployment of ${council.name} is not completed`);
         logger.success('Deployment complete');
 
-        await validateCouncil({ Council, Token, council, epoch });
+        await validateCouncil({ Council, council });
 
-        await validateCouncilToken({ Token, council, epoch });
+        await validateCouncilToken({ Token, council });
       } catch (err) {
         console.error(err);
         logger.error(`Council "${council.name}" is not valid`);
@@ -62,81 +61,169 @@ task('validate-councils')
     }
   });
 
-async function validateCouncil({ Council, council, epoch }) {
+async function validateCouncil({ Council, council }) {
   logger.info('Council validations:');
 
-  // Validate Initializatiion
-  await expect(Council, 'isOwnerModuleInitialized', true);
-  await expect(Council, 'isElectionModuleInitialized', true);
-  await expect(Council, 'getEpochIndex', epoch);
-
-  // Validate Epoch Properties
-  await expect(Council, 'owner', council.owner);
-  await expect(Council, 'nominatedOwner', council.nominatedOwner);
-  await expect(
-    Council,
-    'getNominationPeriodStartDate',
-    getUnixTimestamp(council.getNominationPeriodStartDate)
-  );
-  await expect(
-    Council,
-    'getVotingPeriodStartDate',
-    getUnixTimestamp(council.getVotingPeriodStartDate)
-  );
-  await expect(Council, 'getEpochEndDate', getUnixTimestamp(council.getEpochEndDate));
-  await expect(Council, 'getNextEpochSeatCount', council.getNextEpochSeatCount);
-  await expect(Council, 'getEpochIndex', council.getEpochIndex);
-  await expect(Council, 'getCurrentPeriod', council.getCurrentPeriod);
-
-  if (council.getCouncilMembers) {
-    await expect(Council, 'getCouncilMembers', council.getCouncilMembers);
-  }
+  await validate({
+    action: Council.isOwnerModuleInitialized(),
+    expectedResult: true,
+    validation: (result, expectedResult) => result === expectedResult,
+    successFn: (result) => logger.success(`OwnerModule is initialized: ${result}`),
+    errorFn: (result, expected) =>
+      logger.error(
+        `Was expecting OwnerModule.isOwnerModuleInitialized to be ${expected}, but received ${result}`
+      ),
+  });
+
+  await validate({
+    action: Council.isElectionModuleInitialized(),
+    expectedResult: true,
+    validation: (result, expectedResult) => result === expectedResult,
+    successFn: (result) => logger.success(`ElectionModule is initialized: ${result}`),
+    errorFn: (result, expected) =>
+      logger.error(
+        `Was expecting ElectionModule.isElectionModuleInitialized to be ${expected}, but received ${result}`
+      ),
+  });
+
+  await validate({
+    action: Council.getEpochIndex(),
+    expectedResult: council.epochIndex,
+    validation: (result, expectedResult) => result.toString() === expectedResult,
+    successFn: (result) => logger.success(`Epoch index is: ${result}`),
+    errorFn: (result, expected) =>
+      logger.error(`Was expecting epoch index to be ${expected}, but received ${result}`),
+  });
+
+  await validate({
+    action: Council.getCurrentPeriod(),
+    expectedResult: council.currentPeriod,
+    validation: (result, expectedResult) => result.toString() === expectedResult,
+    successFn: (result) => logger.success(`Current period is: ${result}`),
+    errorFn: (result, expected) =>
+      logger.error(`Was expecting current period to be ${expected}, but received ${result}`),
+  });
+
+  await validate({
+    action: Council.owner(),
+    expectedResult: council.owner,
+    validation: (result, expectedResult) => result === expectedResult,
+    successFn: (result) => logger.success(`Owner is: ${result}`),
+    errorFn: (result, expected) =>
+      logger.error(`Was expecting owner to be ${expected}, but received ${result}`),
+  });
+
+  await validate({
+    action: Council.nominatedOwner(),
+    expectedResult: council.nominatedOwner,
+    validation: (result, expectedResult) => result === expectedResult,
+    successFn: (result) => logger.success(`Nominated owner is: ${result}`),
+    errorFn: (result, expected) =>
+      logger.error(`Was expecting nominated owner to be ${expected}, but received ${result}`),
+  });
+
+  await validate({
+    action: Council.getNextEpochSeatCount(),
+    expectedResult: council.nextEpochSeatCount,
+    validation: (result, expectedResult) => result.toString() === expectedResult,
+    successFn: (result) => logger.success(`Next epoch seat count is: ${result}`),
+    errorFn: (result, expected) =>
+      logger.error(`Was expecting next epoch seat count to be ${expected}, but received ${result}`),
+  });
+
+  await validate({
+    action: Council.getCouncilMembers(),
+    expectedResult: council.councilMembers.join(','),
+    validation: (result, expectedResult) => result.toString() === expectedResult,
+    successFn: (result) => logger.success(`Council members are: ${result}`),
+    errorFn: (result, expected) =>
+      logger.error(`Was expecting council members to be ${expected}, but received ${result}`),
+  });
+
+  await validate({
+    action: Council.getNominationPeriodStartDate(),
+    expectedResult: council.nominationPeriodStartDate,
+    validation: (result, expectedResult) => readableDate(result) === expectedResult,
+    successFn: (result) =>
+      logger.success(`Nomination period start date is: ${readableDate(result)}`),
+    errorFn: (result, expected) =>
+      logger.error(
+        `Was expecting nomination period start date to be ${expected}, but received ${readableDate(
+          result
+        )}`
+      ),
+  });
+
+  await validate({
+    action: Council.getVotingPeriodStartDate(),
+    expectedResult: council.votingPeriodStartDate,
+    validation: (result, expectedResult) => readableDate(result) === expectedResult,
+    successFn: (result) => logger.success(`Voting period start date is: ${readableDate(result)}`),
+    errorFn: (result, expected) =>
+      logger.error(
+        `Was expecting voting period start date to be ${expected}, but received ${readableDate(
+          result
+        )}`
+      ),
+  });
+
+  await validate({
+    action: Council.getEpochEndDate(),
+    expectedResult: council.epochEndDate,
+    validation: (result, expectedResult) => readableDate(result) === expectedResult,
+    successFn: (result) => logger.success(`Epoch end date is: ${readableDate(result)}`),
+    errorFn: (result, expected) =>
+      logger.error(
+        `Was expecting epoch end date to be ${expected}, but received ${readableDate(result)}`
+      ),
+  });
 }
 
 async function validateCouncilToken({ Token, council }) {
   logger.info('CouncilToken validations:');
 
-  await expect(Token, 'name', council.councilTokenName);
-  await expect(Token, 'symbol', council.councilTokenSymbol);
+  await validate({
+    action: Token.name(),
+    expectedResult: council.councilTokenName,
+    validation: (result, expectedResult) => result === expectedResult,
+    successFn: (result) => logger.success(`Council token name is: ${result}`),
+    errorFn: (result, expected) =>
+      logger.error(`Was expecting council token name to be ${expected}, but received ${result}`),
+  });
 
-  // Validate that the council members have 1 CouncilToken
-  for (const address of await council.getCouncilMembers) {
-    await expect(Token, 'balanceOf', address, 1);
+  await validate({
+    action: Token.symbol(),
+    expectedResult: council.councilTokenSymbol,
+    validation: (result, expectedResult) => result === expectedResult,
+    successFn: (result) => logger.success(`Council token symbol is: ${result}`),
+    errorFn: (result, expected) =>
+      logger.error(`Was expecting council token symbol to be ${expected}, but received ${result}`),
+  });
+
+  for (const address of await council.councilMembers) {
+    await validate({
+      action: Token.balanceOf(address),
+      expectedResult: '1',
+      validation: (result, expectedResult) => result.toString() === expectedResult,
+      successFn: (result) => logger.success(`Token balance of ${address}: ${result}`),
+      errorFn: (result, expected) =>
+        logger.error(
+          `Was expecting token balance of ${address} to be ${expected}, but received ${result.toString()}`
+        ),
+    });
   }
 }
 
-function typeOf(val) {
-  return Object.prototype.toString.call(val).slice(8, -1);
+function readableDate(timestamp) {
+  return formatDate(fromUnixTimestamp(timestamp));
 }
 
-async function expect(Contract, methodName, ...args) {
-  const fnParams = args.slice(0, args.length - 1);
-  const [expected] = args.slice(-1);
-  const result = await Contract[methodName](...fnParams);
-  const actual = typeof expected === 'number' ? Number(result) : result;
-
-  const resultIsDate = methodName.includes('Date');
-
-  const paramsStr = fnParams.map(JSON.stringify).join(', ');
-
-  const actualReadable = resultIsDate
-    ? formatDate(new Date(actual * 1000))
-    : JSON.stringify(actual);
-  const resultReadable = resultIsDate
-    ? formatDate(new Date(result * 1000))
-    : JSON.stringify(result);
-
-  try {
-    if (['Array', 'Object'].includes(typeOf(expected))) {
-      deepEqual(actual, expected);
-    } else {
-      equal(actual, expected);
-    }
+async function validate({ action, expectedResult, validation, successFn, errorFn }) {
+  const result = await action;
 
-    logger.success(`${methodName}(${paramsStr}) is ${actualReadable}`);
-  } catch (_) {
-    logger.error(
-      `${methodName}(${paramsStr}) expected ${resultReadable}, but got ${actualReadable}`
-    );
+  if (validation(result, expectedResult)) {
+    successFn(result);
+  } else {
+    errorFn(result, expectedResult);
   }
 }


Test patch:
diff --git a/packages/core-js/test/utils/misc/dates.test.js b/packages/core-js/test/utils/misc/dates.test.js
index cc89b501..29ac865c 100644
--- a/packages/core-js/test/utils/misc/dates.test.js
+++ b/packages/core-js/test/utils/misc/dates.test.js
@@ -1,5 +1,10 @@
-const { ok, equal, throws } = require('assert/strict');
-const { formatDate, getUnixTimestamp, daysToSeconds } = require('../../../utils/misc/dates');
+const { ok, equal, deepEqual, throws } = require('assert/strict');
+const {
+  formatDate,
+  getUnixTimestamp,
+  fromUnixTimestamp,
+  daysToSeconds,
+} = require('../../../utils/misc/dates');
 
 describe('utils/misc/dates.js', function () {
   describe('formatDate', function () {
@@ -27,6 +32,14 @@ describe('utils/misc/dates.js', function () {
     });
   });
 
+  describe('fromUnixTimestamp', function () {
+    it('builds a valid date from a unix timestamp', async function () {
+      const now = getUnixTimestamp();
+
+      deepEqual(fromUnixTimestamp(now), new Date(now * 1000));
+    });
+  });
+
   describe('daysToSeconds', function () {
     it('returns the expected value', async function () {
       equal(daysToSeconds(7), 60 * 60 * 24 * 7);