Repository: hyperjumptech/monika
Pull request #1006 (2023-03-03 04:20:39)

Refactor: Reduce Code Complexity on run Function
# Monika Pull Request (PR)  

## What feature/issue does this PR add  
Reduce code complexity. No functionality changed.

## How did you implement / how did you fix it  
1. Separate secondary commands from primary command
2. Separate some functions
3. Re-enable Eslint complexity rule
4. Add some tests
5. Bump Sinon package

## How to test  
1. Run `npm test`
2. Run Monika as usual



codecov[bot]: # [Codecov](https://codecov.io/gh/hyperjumptech/monika/pull/1006?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech) Report
> Merging [#1006](https://codecov.io/gh/hyperjumptech/monika/pull/1006?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech) (ff98b31) into [main](https://codecov.io/gh/hyperjumptech/monika/commit/51d83bd610c4df3a04c4e4aa0d993c6b0ea6f1e4?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech) (51d83bd) will **increase** coverage by `1.72%`.
> The diff coverage is `88.23%`.

> :exclamation: Current head ff98b31 differs from pull request most recent head 7084531. Consider uploading reports for the commit 7084531 to get more accurate results

```diff
@@            Coverage Diff             @@
##             main    #1006      +/-   ##
==========================================
+ Coverage   45.94%   47.67%   +1.72%     
==========================================
  Files         101      105       +4     
  Lines        3036     3069      +33     
  Branches      588      587       -1     
==========================================
+ Hits         1395     1463      +68     
+ Misses       1447     1418      -29     
+ Partials      194      188       -6     
```


| [Impacted Files](https://codecov.io/gh/hyperjumptech/monika/pull/1006?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech) | Coverage Δ | |
|---|---|---|
| [src/commands/help.ts](https://codecov.io/gh/hyperjumptech/monika/pull/1006?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2NvbW1hbmRzL2hlbHAudHM=) | `33.33% <33.33%> (ø)` | |
| [src/looper/index.ts](https://codecov.io/gh/hyperjumptech/monika/pull/1006?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2xvb3Blci9pbmRleC50cw==) | `54.28% <60.00%> (-2.24%)` | :arrow_down: |
| [src/index.ts](https://codecov.io/gh/hyperjumptech/monika/pull/1006?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2luZGV4LnRz) | `54.20% <82.60%> (+11.75%)` | :arrow_up: |
| [src/components/logger/startup-message.ts](https://codecov.io/gh/hyperjumptech/monika/pull/1006?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2NvbXBvbmVudHMvbG9nZ2VyL3N0YXJ0dXAtbWVzc2FnZS50cw==) | `97.22% <92.85%> (-1.09%)` | :arrow_down: |
| [src/components/config/sort.ts](https://codecov.io/gh/hyperjumptech/monika/pull/1006?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2NvbXBvbmVudHMvY29uZmlnL3NvcnQudHM=) | `95.45% <95.45%> (ø)` | |
| [src/commands/flush.ts](https://codecov.io/gh/hyperjumptech/monika/pull/1006?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2NvbW1hbmRzL2ZsdXNoLnRz) | `100.00% <100.00%> (ø)` | |
| [src/commands/index.ts](https://codecov.io/gh/hyperjumptech/monika/pull/1006?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2NvbW1hbmRzL2luZGV4LnRz) | `100.00% <100.00%> (ø)` | |
| [...c/components/notification/schedule-notification.ts](https://codecov.io/gh/hyperjumptech/monika/pull/1006?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2NvbXBvbmVudHMvbm90aWZpY2F0aW9uL3NjaGVkdWxlLW5vdGlmaWNhdGlvbi50cw==) | `100.00% <100.00%> (ø)` | |
| [src/symon/index.ts](https://codecov.io/gh/hyperjumptech/monika/pull/1006?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL3N5bW9uL2luZGV4LnRz) | `68.88% <100.00%> (+2.72%)` | :arrow_up: |
| [src/components/config/index.ts](https://codecov.io/gh/hyperjumptech/monika/pull/1006?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2NvbXBvbmVudHMvY29uZmlnL2luZGV4LnRz) | `63.26% <0.00%> (+0.68%)` | :arrow_up: |
| ... and [2 more](https://codecov.io/gh/hyperjumptech/monika/pull/1006?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech) | |

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech)


Patch:
diff --git a/package-lock.json b/package-lock.json
index 65c4b8af..dc5edbe2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -58,7 +58,7 @@
         "pkg": "^5.6.0",
         "prom-client": "13.1.0",
         "redis": "^4.3.0",
-        "sinon": "^11.1.2",
+        "sinon": "^15.0.1",
         "smtp-server": "^3.8.0",
         "sqlite": "^4.0.21",
         "sqlite3": "^5.0.2",
@@ -2012,6 +2012,7 @@
       "version": "1.8.3",
       "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz",
       "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==",
+      "dev": true,
       "dependencies": {
         "type-detect": "4.0.8"
       }
@@ -2020,24 +2021,33 @@
       "version": "7.1.2",
       "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz",
       "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==",
+      "dev": true,
       "dependencies": {
         "@sinonjs/commons": "^1.7.0"
       }
     },
     "node_modules/@sinonjs/samsam": {
-      "version": "6.0.2",
-      "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.0.2.tgz",
-      "integrity": "sha512-jxPRPp9n93ci7b8hMfJOFDPRLFYadN6FSpeROFTR4UNF4i5b+EK6m4QXPO46BDhFgRy1JuS87zAnFOzCUwMJcQ==",
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz",
+      "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==",
       "dependencies": {
-        "@sinonjs/commons": "^1.6.0",
+        "@sinonjs/commons": "^2.0.0",
         "lodash.get": "^4.4.2",
         "type-detect": "^4.0.8"
       }
     },
+    "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
+      "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
+      "dependencies": {
+        "type-detect": "4.0.8"
+      }
+    },
     "node_modules/@sinonjs/text-encoding": {
-      "version": "0.7.1",
-      "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz",
-      "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ=="
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz",
+      "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ=="
     },
     "node_modules/@tootallnate/once": {
       "version": "1.1.2",
@@ -8640,7 +8650,7 @@
     "node_modules/lodash.get": {
       "version": "4.4.2",
       "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
-      "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
+      "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="
     },
     "node_modules/lodash.merge": {
       "version": "4.6.2",
@@ -9784,21 +9794,37 @@
       "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
     },
     "node_modules/nise": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.0.tgz",
-      "integrity": "sha512-W5WlHu+wvo3PaKLsJJkgPup2LrsXCcm7AWwyNZkUnn5rwPkuPBi3Iwk5SQtN0mv+K65k7nKKjwNQ30wg3wLAQQ==",
+      "version": "5.1.4",
+      "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz",
+      "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==",
       "dependencies": {
-        "@sinonjs/commons": "^1.7.0",
-        "@sinonjs/fake-timers": "^7.0.4",
+        "@sinonjs/commons": "^2.0.0",
+        "@sinonjs/fake-timers": "^10.0.2",
         "@sinonjs/text-encoding": "^0.7.1",
         "just-extend": "^4.0.2",
         "path-to-regexp": "^1.7.0"
       }
     },
+    "node_modules/nise/node_modules/@sinonjs/commons": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
+      "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
+      "dependencies": {
+        "type-detect": "4.0.8"
+      }
+    },
+    "node_modules/nise/node_modules/@sinonjs/fake-timers": {
+      "version": "10.0.2",
+      "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz",
+      "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==",
+      "dependencies": {
+        "@sinonjs/commons": "^2.0.0"
+      }
+    },
     "node_modules/nise/node_modules/isarray": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
-      "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+      "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="
     },
     "node_modules/nise/node_modules/path-to-regexp": {
       "version": "1.8.0",
@@ -12677,15 +12703,15 @@
       }
     },
     "node_modules/sinon": {
-      "version": "11.1.2",
-      "resolved": "https://registry.npmjs.org/sinon/-/sinon-11.1.2.tgz",
-      "integrity": "sha512-59237HChms4kg7/sXhiRcUzdSkKuydDeTiamT/jesUVHshBgL8XAmhgFo0GfK6RruMDM/iRSij1EybmMog9cJw==",
+      "version": "15.0.1",
+      "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.1.tgz",
+      "integrity": "sha512-PZXKc08f/wcA/BMRGBze2Wmw50CWPiAH3E21EOi4B49vJ616vW4DQh4fQrqsYox2aNR/N3kCqLuB0PwwOucQrg==",
       "dependencies": {
-        "@sinonjs/commons": "^1.8.3",
-        "@sinonjs/fake-timers": "^7.1.2",
-        "@sinonjs/samsam": "^6.0.2",
+        "@sinonjs/commons": "^2.0.0",
+        "@sinonjs/fake-timers": "10.0.2",
+        "@sinonjs/samsam": "^7.0.1",
         "diff": "^5.0.0",
-        "nise": "^5.1.0",
+        "nise": "^5.1.2",
         "supports-color": "^7.2.0"
       },
       "funding": {
@@ -12693,6 +12719,22 @@
         "url": "https://opencollective.com/sinon"
       }
     },
+    "node_modules/sinon/node_modules/@sinonjs/commons": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
+      "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
+      "dependencies": {
+        "type-detect": "4.0.8"
+      }
+    },
+    "node_modules/sinon/node_modules/@sinonjs/fake-timers": {
+      "version": "10.0.2",
+      "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz",
+      "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==",
+      "dependencies": {
+        "@sinonjs/commons": "^2.0.0"
+      }
+    },
     "node_modules/slash": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
@@ -16795,6 +16837,7 @@
       "version": "1.8.3",
       "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz",
       "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==",
+      "dev": true,
       "requires": {
         "type-detect": "4.0.8"
       }
@@ -16803,24 +16846,35 @@
       "version": "7.1.2",
       "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz",
       "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==",
+      "dev": true,
       "requires": {
         "@sinonjs/commons": "^1.7.0"
       }
     },
     "@sinonjs/samsam": {
-      "version": "6.0.2",
-      "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.0.2.tgz",
-      "integrity": "sha512-jxPRPp9n93ci7b8hMfJOFDPRLFYadN6FSpeROFTR4UNF4i5b+EK6m4QXPO46BDhFgRy1JuS87zAnFOzCUwMJcQ==",
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz",
+      "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==",
       "requires": {
-        "@sinonjs/commons": "^1.6.0",
+        "@sinonjs/commons": "^2.0.0",
         "lodash.get": "^4.4.2",
         "type-detect": "^4.0.8"
+      },
+      "dependencies": {
+        "@sinonjs/commons": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
+          "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
+          "requires": {
+            "type-detect": "4.0.8"
+          }
+        }
       }
     },
     "@sinonjs/text-encoding": {
-      "version": "0.7.1",
-      "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz",
-      "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ=="
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz",
+      "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ=="
     },
     "@tootallnate/once": {
       "version": "1.1.2",
@@ -21829,7 +21883,7 @@
     "lodash.get": {
       "version": "4.4.2",
       "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
-      "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
+      "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="
     },
     "lodash.merge": {
       "version": "4.6.2",
@@ -22683,21 +22737,37 @@
       "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
     },
     "nise": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.0.tgz",
-      "integrity": "sha512-W5WlHu+wvo3PaKLsJJkgPup2LrsXCcm7AWwyNZkUnn5rwPkuPBi3Iwk5SQtN0mv+K65k7nKKjwNQ30wg3wLAQQ==",
+      "version": "5.1.4",
+      "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz",
+      "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==",
       "requires": {
-        "@sinonjs/commons": "^1.7.0",
-        "@sinonjs/fake-timers": "^7.0.4",
+        "@sinonjs/commons": "^2.0.0",
+        "@sinonjs/fake-timers": "^10.0.2",
         "@sinonjs/text-encoding": "^0.7.1",
         "just-extend": "^4.0.2",
         "path-to-regexp": "^1.7.0"
       },
       "dependencies": {
+        "@sinonjs/commons": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
+          "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
+          "requires": {
+            "type-detect": "4.0.8"
+          }
+        },
+        "@sinonjs/fake-timers": {
+          "version": "10.0.2",
+          "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz",
+          "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==",
+          "requires": {
+            "@sinonjs/commons": "^2.0.0"
+          }
+        },
         "isarray": {
           "version": "0.0.1",
           "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
-          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+          "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="
         },
         "path-to-regexp": {
           "version": "1.8.0",
@@ -24885,16 +24955,34 @@
       }
     },
     "sinon": {
-      "version": "11.1.2",
-      "resolved": "https://registry.npmjs.org/sinon/-/sinon-11.1.2.tgz",
-      "integrity": "sha512-59237HChms4kg7/sXhiRcUzdSkKuydDeTiamT/jesUVHshBgL8XAmhgFo0GfK6RruMDM/iRSij1EybmMog9cJw==",
+      "version": "15.0.1",
+      "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.1.tgz",
+      "integrity": "sha512-PZXKc08f/wcA/BMRGBze2Wmw50CWPiAH3E21EOi4B49vJ616vW4DQh4fQrqsYox2aNR/N3kCqLuB0PwwOucQrg==",
       "requires": {
-        "@sinonjs/commons": "^1.8.3",
-        "@sinonjs/fake-timers": "^7.1.2",
-        "@sinonjs/samsam": "^6.0.2",
+        "@sinonjs/commons": "^2.0.0",
+        "@sinonjs/fake-timers": "10.0.2",
+        "@sinonjs/samsam": "^7.0.1",
         "diff": "^5.0.0",
-        "nise": "^5.1.0",
+        "nise": "^5.1.2",
         "supports-color": "^7.2.0"
+      },
+      "dependencies": {
+        "@sinonjs/commons": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
+          "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
+          "requires": {
+            "type-detect": "4.0.8"
+          }
+        },
+        "@sinonjs/fake-timers": {
+          "version": "10.0.2",
+          "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz",
+          "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==",
+          "requires": {
+            "@sinonjs/commons": "^2.0.0"
+          }
+        }
       }
     },
     "slash": {
diff --git a/package.json b/package.json
index fb1a42fd..e89b3b43 100644
--- a/package.json
+++ b/package.json
@@ -105,7 +105,7 @@
     "pkg": "^5.6.0",
     "prom-client": "13.1.0",
     "redis": "^4.3.0",
-    "sinon": "^11.1.2",
+    "sinon": "^15.0.1",
     "smtp-server": "^3.8.0",
     "sqlite": "^4.0.21",
     "sqlite3": "^5.0.2",
diff --git a/src/commands/flush.ts b/src/commands/flush.ts
new file mode 100644
index 00000000..07bc57f0
--- /dev/null
+++ b/src/commands/flush.ts
@@ -0,0 +1,25 @@
+import { CliUx } from '@oclif/core'
+import { flushAllLogs } from '../components/logger/history'
+import { log } from '../utils/pino'
+
+export async function flush(isForce: boolean): Promise<void> {
+  if (isForce) {
+    await flushAllLogs()
+    log.info('Records flushed, thank you.')
+
+    return
+  }
+
+  const ans = await CliUx.ux.prompt(
+    'Are you sure you want to flush all logs in monika-logs.db (Y/n)?'
+  )
+
+  if (ans === 'Y') {
+    await flushAllLogs()
+    log.info('Records flushed, thank you.')
+
+    return
+  }
+
+  log.info('Cancelled. Thank you.')
+}
diff --git a/src/commands/help.ts b/src/commands/help.ts
new file mode 100644
index 00000000..01183fcd
--- /dev/null
+++ b/src/commands/help.ts
@@ -0,0 +1,10 @@
+import { loadHelpClass, toCached } from '@oclif/core'
+import { Command, Config } from '@oclif/core/lib/interfaces'
+
+export async function help(config: Config, ctor: Command.Class): Promise<void> {
+  const Help = await loadHelpClass(config)
+  const help = new Help(config)
+  const cmd = await toCached(ctor)
+
+  await help.showCommandHelp(cmd, [])
+}
diff --git a/src/commands/index.ts b/src/commands/index.ts
new file mode 100644
index 00000000..f7ac001d
--- /dev/null
+++ b/src/commands/index.ts
@@ -0,0 +1,2 @@
+export { help } from './help'
+export { flush } from './flush'
diff --git a/src/components/config/sort.ts b/src/components/config/sort.ts
new file mode 100644
index 00000000..b585ab0e
--- /dev/null
+++ b/src/components/config/sort.ts
@@ -0,0 +1,51 @@
+import type { Probe } from '../../interfaces/probe'
+
+export function sortProbes(probes: Probe[], idFlag?: string): Probe[] {
+  if (!idFlag) {
+    return probes
+  }
+
+  const selectedProbeIDs = getProbeIDsFromFlag(idFlag)
+
+  if (!validateProbeIds(probes, selectedProbeIDs)) {
+    throw new Error('Input error')
+  }
+
+  // doing custom sequences if list of ids is declared
+  const sortedProbes = getProbesByIds(probes, selectedProbeIDs)
+
+  return sortedProbes
+}
+
+function getProbeIDsFromFlag(idFlag: string) {
+  return idFlag.split(',').map((item: string) => item.trim())
+}
+
+function getProbesByIds(probes: Probe[], probeIDs: string[]) {
+  const selectedProbes: Probe[] = []
+
+  for (const probeID of probeIDs) {
+    const probe = probes.find(({ id }) => id === probeID)
+
+    if (probe) {
+      selectedProbes.push(probe)
+    }
+  }
+
+  return selectedProbes
+}
+
+function validateProbeIds(probes: Probe[], ids: string[]): boolean {
+  let found = false
+
+  for (const probe of probes) {
+    for (const id of ids) {
+      if (probe.id === id) {
+        found = true
+        break
+      }
+    }
+  }
+
+  return found
+}
diff --git a/src/components/logger/startup-message.ts b/src/components/logger/startup-message.ts
index f1e5fdb5..c26df2ae 100644
--- a/src/components/logger/startup-message.ts
+++ b/src/components/logger/startup-message.ts
@@ -1,9 +1,50 @@
+import path from 'path'
+import isUrl from 'is-url'
 import boxen from 'boxen'
 import chalk from 'chalk'
 import type { Config } from '../../interfaces/config'
 import type { Notification } from '../../interfaces/notification'
 import type { Probe, ProbeAlert } from '../../interfaces/probe'
 import type { RequestConfig } from '../../interfaces/request'
+import { log } from '../../utils/pino'
+
+type LogStartupMessage = {
+  config: Config
+  configFlag: string[]
+  isFirstRun: boolean
+  isSymonMode: boolean
+  isVerbose: boolean
+}
+
+export function logStartupMessage({
+  config,
+  configFlag,
+  isFirstRun,
+  isSymonMode,
+  isVerbose,
+}: LogStartupMessage): void {
+  const startupMessage = generateStartupMessage({
+    config,
+    isFirstRun,
+    isSymonMode,
+    isVerbose,
+  })
+
+  if (isSymonMode) {
+    log.info(startupMessage)
+    return
+  }
+
+  for (const x in configFlag) {
+    if (isUrl(configFlag[x])) {
+      log.info('Using remote config:', configFlag[x])
+    } else if (configFlag[x].length > 0) {
+      log.info('Using config file:', path.resolve(configFlag[x]))
+    }
+  }
+
+  console.log(startupMessage)
+}
 
 type GenerateStartupMessageParams = {
   config: Config
@@ -12,7 +53,7 @@ type GenerateStartupMessageParams = {
   isSymonMode: boolean
 }
 
-export function generateStartupMessage({
+function generateStartupMessage({
   config,
   isFirstRun,
   isVerbose,
diff --git a/src/components/notification/schedule-notification.ts b/src/components/notification/schedule-notification.ts
new file mode 100644
index 00000000..fd51ee77
--- /dev/null
+++ b/src/components/notification/schedule-notification.ts
@@ -0,0 +1,46 @@
+import cron from 'node-cron'
+import type { ScheduledTask } from 'node-cron'
+import { getSummaryAndSendNotif } from '../../jobs/summary-notification'
+
+type scheduleSummaryNotification = {
+  isSymonMode: boolean
+  statusNotificationConfig?: string
+  statusNotificationFlag?: string
+}
+
+let scheduledTasks: ScheduledTask[] = []
+
+// Stop and clear all previous cron tasks
+export function resetScheduledTasks(): void {
+  for (const task of scheduledTasks) {
+    task.stop()
+  }
+
+  scheduledTasks = []
+}
+
+export function scheduleSummaryNotification({
+  isSymonMode,
+  statusNotificationConfig,
+  statusNotificationFlag,
+}: scheduleSummaryNotification): void {
+  if (isSymonMode || statusNotificationFlag === 'false') {
+    return
+  }
+
+  // defaults to 6 AM
+  // default value is not defined in flag configuration,
+  // because the value can also come from config file
+  const DEFAULT_SCHEDULE_CRON_EXPRESSION = '0 6 * * *'
+  const schedule =
+    statusNotificationFlag ||
+    statusNotificationConfig ||
+    DEFAULT_SCHEDULE_CRON_EXPRESSION
+
+  const scheduledStatusUpdateTask = cron.schedule(
+    schedule,
+    getSummaryAndSendNotif
+  )
+
+  scheduledTasks.push(scheduledStatusUpdateTask)
+}
diff --git a/src/index.ts b/src/index.ts
index fada9b26..0d8f0f01 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -22,50 +22,31 @@
  * SOFTWARE.                                                                      *
  **********************************************************************************/
 
-import isUrl from 'is-url'
-import cron, { ScheduledTask } from 'node-cron'
-import path from 'path'
+import { Command, Errors, Flags, Interfaces } from '@oclif/core'
 
-import {
-  CliUx,
-  Command,
-  Errors,
-  Flags,
-  Interfaces,
-  loadHelpClass,
-  toCached,
-} from '@oclif/core'
-
-import {
-  createConfig,
-  getConfigIterator,
-  updateConfig,
-} from './components/config'
+import { flush, help } from './commands'
+import { createConfig, getConfigIterator } from './components/config'
 import { printAllLogs } from './components/logger'
-import {
-  closeLog,
-  flushAllLogs,
-  openLogfile,
-} from './components/logger/history'
-import { generateStartupMessage } from './components/logger/startup-message'
+import { closeLog, openLogfile } from './components/logger/history'
+import { logStartupMessage } from './components/logger/startup-message'
 import { notificationChecker } from './components/notification/checker'
+import {
+  resetScheduledTasks,
+  scheduleSummaryNotification,
+} from './components/notification/schedule-notification'
 import { setContext } from './context'
 import events from './events'
 import { Config } from './interfaces/config'
 import { Probe } from './interfaces/probe'
-import {
-  getSummaryAndSendNotif,
-  printSummary,
-  savePidFile,
-} from './jobs/summary-notification'
+import { printSummary, savePidFile } from './jobs/summary-notification'
 import initLoaders from './loaders'
 import { sanitizeProbe, startProbing } from './looper'
 import { monikaFlagsDefaultValue } from './context/monika-flags'
 import type { MonikaFlags } from './context/monika-flags'
 import SymonClient from './symon'
-import { checkProbeId } from './utils/check-probe-id'
 import { getEventEmitter } from './utils/events'
 import { log } from './utils/pino'
+import { sortProbes } from './components/config/sort'
 
 const em = getEventEmitter()
 let symonClient: SymonClient
@@ -271,7 +252,6 @@ class Monika extends Command {
     }),
   }
 
-  /* eslint-disable complexity */
   async run(): Promise<void> {
     const monika = await this.parse(Monika)
     const _flags: MonikaFlags = monika.flags
@@ -279,12 +259,10 @@ class Monika extends Command {
 
     try {
       if (_flags.help) {
-        const Help = await loadHelpClass(this.config)
-        const help = new Help(this.config)
-        const cmd = await toCached(
+        await help(
+          this.config,
           this.ctor as unknown as Interfaces.Command.Class
         )
-        await help.showCommandHelp(cmd, [])
         return
       }
 
@@ -302,28 +280,14 @@ class Monika extends Command {
       }
 
       if (_flags.flush) {
-        let ans
-
-        if (!_flags.force) {
-          ans = await CliUx.ux.prompt(
-            'Are you sure you want to flush all logs in monika-logs.db (Y/n)?'
-          )
-        }
-
-        if (ans === 'Y' || _flags.force) {
-          await flushAllLogs()
-          log.warn('Records flushed, thank you.')
-        } else {
-          log.info('Cancelled. Thank you.')
-        }
-
+        await flush(_flags.force)
         await closeLog()
-
         return
       }
 
       if (_flags.summary) {
         printSummary(this.config)
+        await closeLog()
         return
       }
 
@@ -340,111 +304,56 @@ class Monika extends Command {
           reportLimit: _flags.symonReportLimit,
         })
         await symonClient.initiate()
-        symonClient.onConfig((config) => updateConfig(config, false))
       }
 
-      let scheduledTasks: ScheduledTask[] = []
       let abortCurrentLooper: (() => void) | undefined
+      const isTestEnvironment = process.env.NODE_ENV === 'test'
 
       for await (const config of getConfigIterator(isSymonMode)) {
         if (!config) continue
+
+        const { notifications, probes } = config
+        const sortedProbes = sortProbes(probes, _flags.id)
+        const sanitizedProbe = sortedProbes.map((probe: Probe) =>
+          sanitizeProbe(isSymonMode, probe)
+        )
+
+        // emit the sanitized probe
+        em.emit(events.config.sanitized, sanitizedProbe)
+
         if (abortCurrentLooper) {
           abortCurrentLooper()
         }
 
-        // Stop, destroy, and clear all previous cron tasks
-        for (const task of scheduledTasks) {
-          task.stop()
-        }
-
-        scheduledTasks = []
+        resetScheduledTasks()
 
-        if (process.env.NODE_ENV !== 'test') {
-          await notificationChecker(config.notifications ?? [])
+        if (!isTestEnvironment) {
+          await notificationChecker(notifications ?? [])
         }
 
         await this.deprecationHandler(config)
 
-        const startupMessage = generateStartupMessage({
+        logStartupMessage({
           config,
+          configFlag: _flags.config,
           isFirstRun: !abortCurrentLooper,
           isSymonMode,
           isVerbose: _flags.verbose,
         })
 
-        // Display config files being used
-        if (isSymonMode) {
-          log.info(startupMessage)
-        } else {
-          for (const x in _flags.config) {
-            // eslint-disable-next-line max-depth
-            if (isUrl(_flags.config[x])) {
-              this.log('Using remote config:', _flags.config[x])
-            } else if (_flags.config[x].length > 0) {
-              this.log('Using config file:', path.resolve(_flags.config[x]))
-            }
-          }
-
-          this.log(startupMessage)
-        }
-
-        // config probes to be run by the looper
-        // default sequence for Each element
-        let probesToRun = config.probes
-        if (_flags.id) {
-          if (!checkProbeId(probesToRun, _flags.id)) {
-            throw new Error('Input error') // can't continue, exit from app
-          }
-
-          // doing custom sequences if list of ids is declared
-          const idSplit = new Set(
-            _flags.id.split(',').map((item: string) => item.trim())
-          )
-          probesToRun = config.probes.filter((probe) => idSplit.has(probe.id))
-        }
-
-        const sanitizedProbe = probesToRun.map((probe: Probe) => {
-          const sanitized = sanitizeProbe(probe, probe.id)
-          if (isSymonMode) {
-            sanitized.alerts = []
-          }
-
-          return sanitized
-        })
-
         // save some data into files for later
         savePidFile(_flags.config, config)
 
-        // emit the sanitized probe
-        if (sanitizedProbe.length > 0) {
-          em.emit(events.config.sanitized, sanitizedProbe)
-        }
-
         // schedule status update notification
-        if (
-          process.env.NODE_ENV !== 'test' &&
-          _flags['status-notification'] !== 'false' &&
-          !isSymonMode
-        ) {
-          // defaults to 6 AM
-          // default value is not defined in flag configuration,
-          // because the value can also come from config file
-          const schedule =
-            _flags['status-notification'] ||
-            config['status-notification'] ||
-            '0 6 * * *'
-
-          const scheduledStatusUpdateTask = cron.schedule(
-            schedule,
-            getSummaryAndSendNotif
-          )
-
-          scheduledTasks.push(scheduledStatusUpdateTask)
-        }
+        scheduleSummaryNotification({
+          isSymonMode,
+          statusNotificationConfig: config['status-notification'],
+          statusNotificationFlag: _flags['status-notification'],
+        })
 
         abortCurrentLooper = startProbing({
           probes: sanitizedProbe,
-          notifications: config.notifications ?? [],
+          notifications: notifications ?? [],
         })
       }
     } catch (error) {
@@ -452,7 +361,6 @@ class Monika extends Command {
       this.error((error as any)?.message, { exit: 1 })
     }
   }
-  /* eslint-enable complexity */
 
   async catch(error: Error): Promise<any> {
     super.catch(error)
diff --git a/src/looper/index.ts b/src/looper/index.ts
index b721cc79..84bbbe33 100644
--- a/src/looper/index.ts
+++ b/src/looper/index.ts
@@ -44,13 +44,13 @@ const DISABLE_STUN = -1 // -1 is disable stun checking
 
 /**
  * sanitizeProbe sanitize currently mapped probe name, alerts, and threshold
+ * @param {boolean} isSymonMode is running in Symon mode
  * @param {object} probe is the probe configuration
- * @param {array} id is the probe ID
  * @returns {object} as probe
  */
-export function sanitizeProbe(probe: Probe, id: string): Probe {
-  const { name, requests, incidentThreshold, recoveryThreshold, alerts } = probe
-  probe.id = `${id}`
+export function sanitizeProbe(isSymonMode: boolean, probe: Probe): Probe {
+  const { id, name, requests, incidentThreshold, recoveryThreshold, alerts } =
+    probe
   probe.alerts = alerts?.map((alert) => {
     if (alert.query) {
       return { ...alert, assertion: alert.query }
@@ -77,23 +77,23 @@ export function sanitizeProbe(probe: Probe, id: string): Probe {
   })
 
   if (!name) {
-    probe.name = `monika_${probe.id}`
+    probe.name = `monika_${id}`
     log.warn(
-      `Warning: Probe ${probe.id} has no name defined. Using the default name started by monika`
+      `Warning: Probe ${id} has no name defined. Using the default name started by monika`
     )
   }
 
   if (!incidentThreshold) {
     probe.incidentThreshold = DEFAULT_THRESHOLD
     log.warn(
-      `Warning: Probe ${probe.id} has no incidentThreshold configuration defined. Using the default threshold: 5`
+      `Warning: Probe ${id} has no incidentThreshold configuration defined. Using the default threshold: 5`
     )
   }
 
   if (!recoveryThreshold) {
     probe.recoveryThreshold = DEFAULT_THRESHOLD
     log.warn(
-      `Warning: Probe ${probe.id} has no recoveryThreshold configuration defined. Using the default threshold: 5`
+      `Warning: Probe ${id} has no recoveryThreshold configuration defined. Using the default threshold: 5`
     )
   }
 
@@ -110,10 +110,14 @@ export function sanitizeProbe(probe: Probe, id: string): Probe {
       },
     ]
     log.warn(
-      `Warning: Probe ${probe.id} has no Alerts configuration defined. Using the default status-not-2xx and response-time-greater-than-2-s`
+      `Warning: Probe ${id} has no Alerts configuration defined. Using the default status-not-2xx and response-time-greater-than-2-s`
     )
   }
 
+  if (isSymonMode) {
+    probe.alerts = []
+  }
+
   return probe
 }
 
diff --git a/src/symon/index.ts b/src/symon/index.ts
index 730ab3b7..2948e5b4 100644
--- a/src/symon/index.ts
+++ b/src/symon/index.ts
@@ -30,6 +30,7 @@ import { hostname } from 'os'
 import Bree from 'bree'
 import path from 'path'
 
+import { updateConfig } from '../components/config'
 import { getOSName } from '../components/notification/alert-message'
 import { getContext } from '../context'
 import events from '../events'
@@ -235,6 +236,7 @@ class SymonClient {
     }
 
     await this.report()
+    this.onConfig((config) => updateConfig(config, false))
   }
 
   async notifyEvent(event: SymonClientEvent): Promise<void> {
diff --git a/src/utils/check-probe-id.ts b/src/utils/check-probe-id.ts
deleted file mode 100644
index b3ab1f8a..00000000
--- a/src/utils/check-probe-id.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Probe } from '../interfaces/probe'
-
-export function checkProbeId(probes: Probe[], idsString: string): boolean {
-  let found = false
-  const ids = idsString.split(',').map((item) => item.trim())
-
-  for (const probe of probes) {
-    for (const id of ids) {
-      if (probe.id === id) {
-        found = true
-        break
-      }
-    }
-  }
-
-  return found
-}


Test patch:
diff --git a/src/commands/flush.test.ts b/src/commands/flush.test.ts
new file mode 100644
index 00000000..e604157f
--- /dev/null
+++ b/src/commands/flush.test.ts
@@ -0,0 +1,78 @@
+/**********************************************************************************
+ * MIT License                                                                    *
+ *                                                                                *
+ * Copyright (c) 2021 Hyperjump Technology                                        *
+ *                                                                                *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy   *
+ * of this software and associated documentation files (the "Software"), to deal  *
+ * in the Software without restriction, including without limitation the rights   *
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell      *
+ * copies of the Software, and to permit persons to whom the Software is          *
+ * furnished to do so, subject to the following conditions:                       *
+ *                                                                                *
+ * The above copyright notice and this permission notice shall be included in all *
+ * copies or substantial portions of the Software.                                *
+ *                                                                                *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR     *
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,       *
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE    *
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER         *
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  *
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  *
+ * SOFTWARE.                                                                      *
+ **********************************************************************************/
+
+import sinon from 'sinon'
+import { CliUx } from '@oclif/core'
+import { test } from '@oclif/test'
+import * as history from '../components/logger/history'
+import cmd from '../../src'
+import { flush } from './flush'
+
+let flushAllLogsStub: sinon.SinonStub
+
+beforeEach(() => {
+  flushAllLogsStub = sinon.stub(history, 'flushAllLogs').resolves()
+})
+
+afterEach(() => {
+  flushAllLogsStub.restore()
+})
+
+describe('Flush command', () => {
+  describe('Force', () => {
+    it('should flush records without asking for confirmation', async () => {
+      // act
+      await flush(true)
+
+      // assert
+      sinon.assert.calledOnce(flushAllLogsStub)
+    })
+  })
+
+  describe('Not force', () => {
+    test
+      // arrange
+      // eslint-disable-next-line unicorn/consistent-function-scoping
+      .stub(CliUx.ux, 'prompt', () => async () => 'n')
+      .stdout()
+      // act
+      .do(() => cmd.run(['--flush']))
+      .it('should cancel flush', () => {
+        // assert
+        sinon.assert.notCalled(flushAllLogsStub)
+      })
+
+    test
+      // arrange
+      // eslint-disable-next-line unicorn/consistent-function-scoping
+      .stub(CliUx.ux, 'prompt', () => async () => 'Y')
+      .stdout()
+      // act
+      .do(() => cmd.run(['--flush']))
+      .it('should flush', () => {
+        // assert
+        sinon.assert.calledOnce(flushAllLogsStub)
+      })
+  })
+})
diff --git a/src/components/config/sort.test.ts b/src/components/config/sort.test.ts
new file mode 100644
index 00000000..ebd8490f
--- /dev/null
+++ b/src/components/config/sort.test.ts
@@ -0,0 +1,97 @@
+/**********************************************************************************
+ * MIT License                                                                    *
+ *                                                                                *
+ * Copyright (c) 2021 Hyperjump Technology                                        *
+ *                                                                                *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy   *
+ * of this software and associated documentation files (the "Software"), to deal  *
+ * in the Software without restriction, including without limitation the rights   *
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell      *
+ * copies of the Software, and to permit persons to whom the Software is          *
+ * furnished to do so, subject to the following conditions:                       *
+ *                                                                                *
+ * The above copyright notice and this permission notice shall be included in all *
+ * copies or substantial portions of the Software.                                *
+ *                                                                                *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR     *
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,       *
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE    *
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER         *
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  *
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  *
+ * SOFTWARE.                                                                      *
+ **********************************************************************************/
+
+import { expect } from '@oclif/test'
+import { assert } from 'chai'
+import type { Probe } from '../../interfaces/probe'
+import { sortProbes } from './sort'
+
+// arrange
+const probes: Probe[] = [
+  {
+    id: '9t65v',
+    name: 'Example',
+    interval: 3000,
+    requests: [],
+    incidentThreshold: 0,
+    recoveryThreshold: 0,
+    alerts: [],
+  },
+  {
+    id: '5zm60',
+    name: 'Example 2',
+    interval: 3000,
+    requests: [],
+    incidentThreshold: 0,
+    recoveryThreshold: 0,
+    alerts: [],
+  },
+]
+
+describe('Sort probes', () => {
+  describe('Without id flag', () => {
+    it('should not change probe order', () => {
+      // act
+      const sortedProbes = sortProbes(probes)
+
+      // assert
+      expect(sortedProbes).deep.eq(probes)
+    })
+  })
+
+  describe('With id flag', () => {
+    it('should throw due to invalid id', () => {
+      // assert
+      assert.throws(() => sortProbes(probes, 'm91us'))
+    })
+
+    it('should order probes by id flag', () => {
+      // act
+      const sortedProbes = sortProbes(probes, '5zm60,9t65v')
+
+      // assert
+      expect(sortedProbes[0]).deep.eq(probes[1])
+      expect(sortedProbes[1]).deep.eq(probes[0])
+    })
+
+    it('should order probes by id flag (duplicate id)', () => {
+      // act
+      const sortedProbes = sortProbes(probes, '5zm60,9t65v,5zm60')
+
+      // assert
+      expect(sortedProbes[0]).deep.eq(probes[1])
+      expect(sortedProbes[1]).deep.eq(probes[0])
+      expect(sortedProbes[2]).deep.eq(probes[1])
+    })
+
+    it('should order probes by spaced id flag', () => {
+      // act
+      const sortedProbes = sortProbes(probes, '5zm60, 9t65v')
+
+      // assert
+      expect(sortedProbes[0]).deep.eq(probes[1])
+      expect(sortedProbes[1]).deep.eq(probes[0])
+    })
+  })
+})
diff --git a/src/components/logger/startup-message.test.ts b/src/components/logger/startup-message.test.ts
index e3eed767..6262af0b 100644
--- a/src/components/logger/startup-message.test.ts
+++ b/src/components/logger/startup-message.test.ts
@@ -22,10 +22,14 @@
  * SOFTWARE.                                                                      *
  **********************************************************************************/
 
-import { expect } from 'chai'
+import chai, { expect } from 'chai'
+import spies from 'chai-spies'
 import type { Config } from '../../interfaces/config'
 import type { MailgunData, SMTPData, WebhookData } from '../../interfaces/data'
-import { generateStartupMessage } from './startup-message'
+import { log } from '../../utils/pino'
+import { logStartupMessage } from './startup-message'
+
+chai.use(spies)
 
 const defaultConfig: Config = {
   probes: [
@@ -45,18 +49,38 @@ const defaultConfig: Config = {
 }
 
 describe('Startup message', () => {
+  // arrange
+  let logMessage = ''
+  let consoleLogMessage = ''
+
+  beforeEach(() => {
+    chai.spy.on(log, 'info', (message: string) => {
+      logMessage = message
+    })
+    chai.spy.on(console, 'log', (message: string) => {
+      consoleLogMessage = message
+    })
+  })
+
+  afterEach(() => {
+    chai.spy.restore()
+    logMessage = ''
+    consoleLogMessage = ''
+  })
+
   describe('Symon mode', () => {
     it('should show running in Symon mode', () => {
       // act
-      const message = generateStartupMessage({
+      logStartupMessage({
         config: { probes: [] },
+        configFlag: [],
         isFirstRun: false,
         isSymonMode: true,
         isVerbose: false,
       })
 
       // assert
-      expect(message).eq('Running in Symon mode')
+      expect(logMessage).eq('Running in Symon mode')
     })
   })
 
@@ -64,73 +88,78 @@ describe('Startup message', () => {
     describe('Notification message', () => {
       it('should show has no notification warning', () => {
         // arrange
-        const message = generateStartupMessage({
+        logStartupMessage({
           config: { probes: [] },
+          configFlag: [],
           isFirstRun: false,
           isSymonMode: false,
           isVerbose: false,
         })
 
         // assert
-        expect(message).include('Notifications has not been set')
+        expect(consoleLogMessage).include('Notifications has not been set')
       })
     })
 
     describe('Starting or Restarting', () => {
       it('should show starting', () => {
         // arrange
-        const message = generateStartupMessage({
+        logStartupMessage({
           config: defaultConfig,
+          configFlag: [],
           isFirstRun: true,
           isSymonMode: false,
           isVerbose: false,
         })
 
         // assert
-        expect(message).include('Starting')
+        expect(consoleLogMessage).include('Starting')
       })
 
       it('should show restarting', () => {
         // arrange
-        const message = generateStartupMessage({
+        logStartupMessage({
           config: defaultConfig,
+          configFlag: [],
           isFirstRun: false,
           isSymonMode: false,
           isVerbose: false,
         })
 
         // assert
-        expect(message).include('Restarting')
+        expect(consoleLogMessage).include('Restarting')
       })
 
       it('should show probe and notification total', () => {
         // arrange
-        const message = generateStartupMessage({
+        logStartupMessage({
           config: defaultConfig,
+          configFlag: [],
           isFirstRun: true,
           isSymonMode: false,
           isVerbose: false,
         })
 
         // assert
-        expect(message).include('Probes: 1')
-        expect(message).include('Notifications: 1')
+        expect(consoleLogMessage).include('Probes: 1')
+        expect(consoleLogMessage).include('Notifications: 1')
       })
 
       it('should show empty notification total', () => {
         // arrange
-        const message = generateStartupMessage({
+        logStartupMessage({
           config: {
             ...defaultConfig,
             notifications: [],
           },
+          configFlag: [],
           isFirstRun: true,
           isSymonMode: false,
           isVerbose: false,
         })
 
         // assert
-        expect(message).include('Notifications: 0')
+        expect(consoleLogMessage).include('Notifications: 0')
       })
     })
   })
@@ -139,7 +168,7 @@ describe('Startup message', () => {
     describe('Probe detail', () => {
       it('should show probe detail', () => {
         // arrange
-        const message = generateStartupMessage({
+        logStartupMessage({
           config: {
             ...defaultConfig,
             probes: [
@@ -149,22 +178,22 @@ describe('Startup message', () => {
               },
             ],
           },
+          configFlag: [],
           isFirstRun: true,
           isSymonMode: false,
           isVerbose: true,
         })
-
         // assert
-        expect(message).include('uYJaw')
-        expect(message).include('Acme')
-        expect(message).include("Acme's company profile")
-        expect(message).include('-')
-        expect(message).include('3000')
+        expect(consoleLogMessage).include('uYJaw')
+        expect(consoleLogMessage).include('Acme')
+        expect(consoleLogMessage).include("Acme's company profile")
+        expect(consoleLogMessage).include('-')
+        expect(consoleLogMessage).include('3000')
       })
 
       it('should show probe detail without description', () => {
         // arrange
-        const message = generateStartupMessage({
+        logStartupMessage({
           config: {
             probes: [
               {
@@ -178,37 +207,37 @@ describe('Startup message', () => {
               },
             ],
           },
+          configFlag: [],
           isFirstRun: true,
           isSymonMode: false,
           isVerbose: true,
         })
-
         // assert
-        expect(message).include('uYJaw')
-        expect(message).include('Acme Inc.')
-        expect(message).include('-')
-        expect(message).include('3000')
+        expect(consoleLogMessage).include('uYJaw')
+        expect(consoleLogMessage).include('Acme Inc.')
+        expect(consoleLogMessage).include('-')
+        expect(consoleLogMessage).include('3000')
       })
     })
 
     describe('Probe request detail', () => {
       it('should show probe request detail with default method', () => {
         // arrange
-        const message = generateStartupMessage({
+        logStartupMessage({
           config: defaultConfig,
+          configFlag: [],
           isFirstRun: true,
           isSymonMode: false,
           isVerbose: true,
         })
-
         // assert
-        expect(message).include('GET')
-        expect(message).include('https://example.com')
+        expect(consoleLogMessage).include('GET')
+        expect(consoleLogMessage).include('https://example.com')
       })
 
       it('should show probe detail', () => {
         // arrange
-        const message = generateStartupMessage({
+        logStartupMessage({
           config: {
             ...defaultConfig,
             probes: [
@@ -232,42 +261,41 @@ describe('Startup message', () => {
               },
             ],
           },
+          configFlag: [],
           isFirstRun: true,
           isSymonMode: false,
           isVerbose: true,
         })
-
         // assert
-        expect(message).include('POST')
-        expect(message).include('https://example.com')
+        expect(consoleLogMessage).include('POST')
+        expect(consoleLogMessage).include('https://example.com')
       })
     })
 
     describe('Alert detail', () => {
       it('should show default alert', () => {
         // arrange
-        const message = generateStartupMessage({
+        logStartupMessage({
           config: defaultConfig,
+          configFlag: [],
           isFirstRun: true,
           isSymonMode: false,
           isVerbose: true,
         })
-
         // assert
-        expect(message).include(
+        expect(consoleLogMessage).include(
           'response.status < 200 or response.status > 299'
         )
       })
 
       it('should show alert detail', () => {
         // arrange
-        const message = generateStartupMessage({
+        logStartupMessage({
           config: {
             ...defaultConfig,
             probes: [
               {
                 ...defaultConfig.probes[0],
-
                 alerts: [
                   {
                     assertion: 'response.status = 500',
@@ -277,13 +305,13 @@ describe('Startup message', () => {
               },
             ],
           },
+          configFlag: [],
           isFirstRun: true,
           isSymonMode: false,
           isVerbose: true,
         })
-
         // assert
-        expect(message).include(
+        expect(consoleLogMessage).include(
           '[{"assertion":"response.status = 500","message":"HTTP status is 500"}]'
         )
       })
@@ -292,25 +320,25 @@ describe('Startup message', () => {
     describe('Notification detail', () => {
       it('should show notification id and type', () => {
         // arrange
-        const message = generateStartupMessage({
+        logStartupMessage({
           config: {
             ...defaultConfig,
             notifications: [{ id: 'UVIsL', type: 'desktop', data: undefined }],
           },
+          configFlag: [],
           isFirstRun: true,
           isSymonMode: false,
           isVerbose: true,
         })
-
         // assert
-        expect(message).include('Notifications')
-        expect(message).include('UVIsL')
-        expect(message).include('desktop')
+        expect(consoleLogMessage).include('Notifications')
+        expect(consoleLogMessage).include('UVIsL')
+        expect(consoleLogMessage).include('desktop')
       })
 
       it('should show notification detail for SMTP', () => {
         // arrange
-        const message = generateStartupMessage({
+        logStartupMessage({
           config: {
             ...defaultConfig,
             notifications: [
@@ -326,21 +354,21 @@ describe('Startup message', () => {
               },
             ],
           },
+          configFlag: [],
           isFirstRun: true,
           isSymonMode: false,
           isVerbose: true,
         })
-
         // assert
-        expect(message).include('john@example.com, jane@example.com')
-        expect(message).include('example.com')
-        expect(message).include('25')
-        expect(message).include('name@example.com')
+        expect(consoleLogMessage).include('john@example.com, jane@example.com')
+        expect(consoleLogMessage).include('example.com')
+        expect(consoleLogMessage).include('25')
+        expect(consoleLogMessage).include('name@example.com')
       })
 
       it('should show notification detail with domain', () => {
         // arrange
-        const message = generateStartupMessage({
+        logStartupMessage({
           config: {
             ...defaultConfig,
             notifications: [
@@ -354,18 +382,18 @@ describe('Startup message', () => {
               },
             ],
           },
+          configFlag: [],
           isFirstRun: true,
           isSymonMode: false,
           isVerbose: true,
         })
-
         // assert
-        expect(message).include('https://example.com')
+        expect(consoleLogMessage).include('https://example.com')
       })
 
       it('should show notification detail with url', () => {
         // arrange
-        const message = generateStartupMessage({
+        logStartupMessage({
           config: {
             ...defaultConfig,
             notifications: [
@@ -378,14 +406,44 @@ describe('Startup message', () => {
               },
             ],
           },
+          configFlag: [],
           isFirstRun: true,
           isSymonMode: false,
           isVerbose: true,
         })
-
         // assert
-        expect(message).include('https://example.com')
+        expect(consoleLogMessage).include('https://example.com')
+      })
+    })
+  })
+
+  describe('Config file', () => {
+    it('should show remote config message', () => {
+      // act
+      logStartupMessage({
+        config: defaultConfig,
+        configFlag: ['https://example.com/monika.yaml'],
+        isFirstRun: false,
+        isSymonMode: false,
+        isVerbose: false,
+      })
+
+      // assert
+      expect(logMessage).include('remote config')
+    })
+
+    it('should show file config message', () => {
+      // act
+      logStartupMessage({
+        config: defaultConfig,
+        configFlag: ['./monika.yaml'],
+        isFirstRun: false,
+        isSymonMode: false,
+        isVerbose: false,
       })
+
+      // assert
+      expect(logMessage).include('config file')
     })
   })
 })
diff --git a/src/components/notification/schedule-notification.test.ts b/src/components/notification/schedule-notification.test.ts
new file mode 100644
index 00000000..7504737a
--- /dev/null
+++ b/src/components/notification/schedule-notification.test.ts
@@ -0,0 +1,112 @@
+import cron from 'node-cron'
+import sinon from 'sinon'
+import chai, { expect } from 'chai'
+import spies from 'chai-spies'
+import {
+  resetScheduledTasks,
+  scheduleSummaryNotification,
+} from './schedule-notification'
+
+chai.use(spies)
+
+let cronScheduleStub: sinon.SinonStub
+let cronExpression: string
+let taskStopCalledTotal = 0
+
+describe('Schedule notification', () => {
+  beforeEach(() => {
+    cronScheduleStub = sinon
+      .stub(cron, 'schedule')
+      .callsFake((ce, _callback, _options) => {
+        cronExpression = ce
+
+        return {
+          stop: () => {
+            taskStopCalledTotal++
+          },
+        } as cron.ScheduledTask
+      })
+  })
+
+  afterEach(() => {
+    sinon.restore()
+    cronExpression = ''
+    resetScheduledTasks()
+    taskStopCalledTotal = 0
+  })
+
+  describe('Schedule summary notification', () => {
+    it('should not schedule notification on Symon mode', () => {
+      // act
+      scheduleSummaryNotification({ isSymonMode: true })
+
+      // assert
+      sinon.assert.notCalled(cronScheduleStub)
+    })
+
+    it('should not schedule notification if status notification flag is false', () => {
+      // act
+      scheduleSummaryNotification({
+        isSymonMode: false,
+        statusNotificationFlag: 'false',
+      })
+
+      // assert
+      sinon.assert.notCalled(cronScheduleStub)
+    })
+
+    it('should schedule notification based on notification flag value', () => {
+      // act
+      scheduleSummaryNotification({
+        isSymonMode: false,
+        statusNotificationFlag: '* * * * *',
+        statusNotificationConfig: '0 0 0 0 0',
+      })
+
+      // assert
+      sinon.assert.calledOnce(cronScheduleStub)
+      expect(cronExpression).eq('* * * * *')
+    })
+
+    it('should schedule notification based on notification config value', () => {
+      // act
+      scheduleSummaryNotification({
+        isSymonMode: false,
+        statusNotificationConfig: '0 0 0 0 0',
+      })
+
+      // assert
+      sinon.assert.calledOnce(cronScheduleStub)
+      expect(cronExpression).eq('0 0 0 0 0')
+    })
+
+    it('should schedule notification use default schedule', () => {
+      // act
+      scheduleSummaryNotification({
+        isSymonMode: false,
+      })
+
+      // assert
+      sinon.assert.calledOnce(cronScheduleStub)
+      expect(cronExpression).eq('0 6 * * *')
+    })
+  })
+
+  describe('Reset schedule notification', () => {
+    it('should stop all running scheduled notification', () => {
+      // arrange
+      scheduleSummaryNotification({
+        isSymonMode: false,
+      })
+      scheduleSummaryNotification({
+        isSymonMode: false,
+      })
+
+      // act
+      resetScheduledTasks()
+
+      // arrange
+      expect(taskStopCalledTotal).eq(2)
+    })
+  })
+})