Repository: hyperjumptech/monika
Pull request #1009 (2023-03-10 09:07:24)

Fix: Reduce doProbe Function Complexity
# Monika Pull Request (PR)  

## What feature/issue does this PR add  
Reduce doProbe function complexity. As a part of the effort to solve issue #990.

## How did you implement / how did you fix it  
1.  Re-enable Eslint complexity rule
2. Separate and centralized all prober functions
3. Add some tests

## How to test  
1. Run unit test
2. Run Monika as usual



codecov[bot]: ## [Codecov](https://codecov.io/gh/hyperjumptech/monika/pull/1009?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech) Report
> Merging [#1009](https://codecov.io/gh/hyperjumptech/monika/pull/1009?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech) (c9a7c94) into [main](https://codecov.io/gh/hyperjumptech/monika/commit/bbd11ae48f91346e0a8c0306de0c5900b92e2d80?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech) (bbd11ae) will **increase** coverage by `2.20%`.
> The diff coverage is `53.19%`.

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

```diff
@@            Coverage Diff             @@
##             main    #1009      +/-   ##
==========================================
+ Coverage   47.94%   50.14%   +2.20%     
==========================================
  Files         105      112       +7     
  Lines        3064     3095      +31     
  Branches      586      586              
==========================================
+ Hits         1469     1552      +83     
+ Misses       1406     1349      -57     
- Partials      189      194       +5     
```


| [Impacted Files](https://codecov.io/gh/hyperjumptech/monika/pull/1009?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech) | Coverage Δ | |
|---|---|---|
| [src/components/probe/prober/mariadb/request.ts](https://codecov.io/gh/hyperjumptech/monika/pull/1009?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2NvbXBvbmVudHMvcHJvYmUvcHJvYmVyL21hcmlhZGIvcmVxdWVzdC50cw==) | `11.11% <0.00%> (ø)` | |
| [src/components/probe/prober/mongo/request.ts](https://codecov.io/gh/hyperjumptech/monika/pull/1009?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2NvbXBvbmVudHMvcHJvYmUvcHJvYmVyL21vbmdvL3JlcXVlc3QudHM=) | `9.09% <0.00%> (ø)` | |
| [src/components/probe/prober/postgres/request.ts](https://codecov.io/gh/hyperjumptech/monika/pull/1009?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2NvbXBvbmVudHMvcHJvYmUvcHJvYmVyL3Bvc3RncmVzL3JlcXVlc3QudHM=) | `15.62% <ø> (ø)` | |
| [src/components/probe/prober/redis/request.ts](https://codecov.io/gh/hyperjumptech/monika/pull/1009?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2NvbXBvbmVudHMvcHJvYmUvcHJvYmVyL3JlZGlzL3JlcXVlc3QudHM=) | `13.33% <0.00%> (ø)` | |
| [src/components/probe/prober/socket/request.ts](https://codecov.io/gh/hyperjumptech/monika/pull/1009?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2NvbXBvbmVudHMvcHJvYmUvcHJvYmVyL3NvY2tldC9yZXF1ZXN0LnRz) | `11.11% <ø> (ø)` | |
| [src/components/probe/index.ts](https://codecov.io/gh/hyperjumptech/monika/pull/1009?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2NvbXBvbmVudHMvcHJvYmUvaW5kZXgudHM=) | `20.93% <5.40%> (+6.71%)` | :arrow_up: |
| [src/components/probe/prober/http/index.ts](https://codecov.io/gh/hyperjumptech/monika/pull/1009?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2NvbXBvbmVudHMvcHJvYmUvcHJvYmVyL2h0dHAvaW5kZXgudHM=) | `24.13% <24.13%> (ø)` | |
| [src/components/probe/prober/postgres/index.ts](https://codecov.io/gh/hyperjumptech/monika/pull/1009?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2NvbXBvbmVudHMvcHJvYmUvcHJvYmVyL3Bvc3RncmVzL2luZGV4LnRz) | `79.16% <79.16%> (ø)` | |
| [src/components/probe/prober/http/request.ts](https://codecov.io/gh/hyperjumptech/monika/pull/1009?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2NvbXBvbmVudHMvcHJvYmUvcHJvYmVyL2h0dHAvcmVxdWVzdC50cw==) | `73.25% <80.00%> (ø)` | |
| [src/components/probe/prober/icmp/request.ts](https://codecov.io/gh/hyperjumptech/monika/pull/1009?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2NvbXBvbmVudHMvcHJvYmUvcHJvYmVyL2ljbXAvcmVxdWVzdC50cw==) | `12.50% <100.00%> (ø)` | |
| ... and [5 more](https://codecov.io/gh/hyperjumptech/monika/pull/1009?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/src/components/probe/index.ts b/src/components/probe/index.ts
index f1f274e8..6ffe9135 100644
--- a/src/components/probe/index.ts
+++ b/src/components/probe/index.ts
@@ -22,9 +22,6 @@
  * SOFTWARE.                                                                      *
  **********************************************************************************/
 
-import * as mongodbURI from 'mongodb-uri'
-import { parse } from 'pg-connection-string'
-
 import { getContext } from '../../context'
 import events from '../../events'
 import { Notification } from '../../interfaces/notification'
@@ -37,16 +34,18 @@ import validateResponse, {
 import { getEventEmitter } from '../../utils/events'
 import { log } from '../../utils/pino'
 import { setProbeFinish, setProbeRunning } from '../../utils/probe-state'
-import { httpRequest } from '../http-request'
 import { RequestLog } from '../logger'
-import { logResponseTime } from '../logger/response-time-log'
-import { mariaRequest } from '../mariadb-request'
-import { mongoRequest } from '../mongodb-request'
 import { sendAlerts } from '../notification'
 import { processThresholds } from '../notification/process-server-status'
-import { PostgresParam, postgresRequest } from '../postgres-request'
-import { redisRequest } from '../redis-request'
-import { tcpRequest } from '../tcp-request'
+import {
+  type ProbeResult,
+  probeHTTP,
+  probeMariaDB,
+  probeMongo,
+  probePostgres,
+  probeRedis,
+  probeSocket,
+} from './prober'
 
 interface ProbeStatusProcessed {
   probe: Probe
@@ -61,9 +60,6 @@ interface ProbeSendNotification extends Omit<ProbeStatusProcessed, 'statuses'> {
   probeState?: ServerAlertState
 }
 
-const connectionRecoveryMsg = `Probe is accessible again`
-const connectionIncidentMsg = `Probe not accessible`
-
 const probeSendNotification = async (data: ProbeSendNotification) => {
   const eventEmitter = getEventEmitter()
 
@@ -104,10 +100,10 @@ const probeSendNotification = async (data: ProbeSendNotification) => {
 }
 
 // Probes Thresholds processed, Send out notifications/alerts.
-function checkThresholdsAndSendAlert(
+export function checkThresholdsAndSendAlert(
   data: ProbeStatusProcessed,
   requestLog: RequestLog
-) {
+): void {
   const {
     probe,
     statuses,
@@ -167,20 +163,18 @@ type respProsessingParams = {
  * @param {object} param is response Processing type
  * @returns void
  */
-async function responseProcessing({
+function responseProcessing({
   probe,
   probeResult,
   notifications,
   logMessage,
   isAlertTriggered,
   index,
-}: respProsessingParams): Promise<void> {
+}: respProsessingParams): void {
   const { flags } = getContext()
   const isSymonMode = Boolean(flags.symonUrl) && Boolean(flags.symonKey)
-  const verboseLogs = isSymonMode || flags['keep-verbose-logs']
-
-  isAlertTriggered ? log.warn(logMessage) : log.info(logMessage)
-
+  const eventEmitter = getEventEmitter()
+  const isVerbose = isSymonMode || flags['keep-verbose-logs']
   const { alerts } = probe
   const validatedResponse = validateResponse(
     alerts || [
@@ -192,19 +186,25 @@ async function responseProcessing({
     probeResult
   )
   const requestLog = new RequestLog(probe, index, 0)
-
-  requestLog.addAlerts(
-    validatedResponse
-      .filter((item) => item.isAlertTriggered)
-      .map((item) => item.alert)
-  )
   const statuses = processThresholds({
     probe,
     requestIndex: index,
     validatedResponse,
   })
 
+  eventEmitter.emit(events.probe.response.received, {
+    probe,
+    requestIndex: index,
+    response: probeResult,
+  })
+  isAlertTriggered ? log.warn(logMessage) : log.info(logMessage)
+  requestLog.addAlerts(
+    validatedResponse
+      .filter((item) => item.isAlertTriggered)
+      .map((item) => item.alert)
+  )
   requestLog.setResponse(probeResult)
+  // Done processing results, check if need to send out alerts
   checkThresholdsAndSendAlert(
     {
       probe,
@@ -215,14 +215,13 @@ async function responseProcessing({
     },
     requestLog
   )
+  requestLog.print()
 
-  if (verboseLogs || requestLog.hasIncidentOrRecovery) {
+  if (isVerbose || requestLog.hasIncidentOrRecovery) {
     requestLog.saveToDatabase().catch((error) => log.error(error.message))
   }
 }
 
-const isConnectionDown = new Map<string, boolean>()
-
 type doProbeParams = {
   checkOrder: number // the order of probe being processed
   probe: Probe // probe contains all the probes
@@ -238,312 +237,97 @@ export async function doProbe({
   probe,
   notifications,
 }: doProbeParams): Promise<void> {
-  const { flags } = getContext()
-  const isSymonMode = Boolean(flags.symonUrl) && Boolean(flags.symonKey)
-  const verboseLogs = isSymonMode || flags['keep-verbose-logs']
+  const randomTimeoutMilliseconds = getRandomTimeoutMilliseconds()
 
   setProbeRunning(probe.id)
 
-  const randomTimeout = [1000, 2000, 3000].sort(() => {
+  setTimeout(async () => {
+    await probeNonHTTP(probe, checkOrder, notifications)
+    await probeHTTP(probe, checkOrder, notifications)
+
+    setProbeFinish(probe.id)
+  }, randomTimeoutMilliseconds)
+}
+
+function processProbeResults(
+  probeResults: ProbeResult[],
+  probe: Probe,
+  notifications: Notification[]
+): void {
+  for (const index of probeResults.keys()) {
+    const { isAlertTriggered, logMessage, requestResponse } =
+      probeResults[index]
+
+    responseProcessing({
+      probe,
+      probeResult: requestResponse,
+      notifications,
+      logMessage,
+      isAlertTriggered,
+      index,
+    })
+  }
+}
+
+function getRandomTimeoutMilliseconds(): number {
+  return [1000, 2000, 3000].sort(() => {
     return Math.random() - 0.5
   })[0]
+}
 
-  // eslint-disable-next-line complexity
-  setTimeout(async () => {
-    const eventEmitter = getEventEmitter()
-    const responses = []
-
-    if (probe?.mongo) {
-      const { id, mongo } = probe
-      let mongoRequestIndex = 0
-      for await (const mongoDB of mongo) {
-        const { uri } = mongoDB
-        let host: string | undefined
-        let port: number | undefined
-        let username: string | undefined
-        let password: string | undefined
-
-        if (uri) {
-          const parsed = mongodbURI.parse(uri)
-          host = parsed.hosts[0].host
-          port = parsed.hosts[0].port
-          username = parsed.username
-          password = parsed.password
-        } else {
-          host = mongoDB.host
-          port = mongoDB.port
-          username = mongoDB.username
-          password = mongoDB.password
-        }
-
-        const mongoResult = await mongoRequest({
-          uri,
-          host,
-          port,
-          username,
-          password,
-        })
-        const timeNow = new Date().toISOString()
-        const logMessage = `${timeNow} ${checkOrder} id:${id} mongo:${host}:${port} ${mongoResult.responseTime}ms msg:${mongoResult.body}`
-        const isAlertTriggered = mongoResult.status !== 200
-        responseProcessing({
-          probe: probe,
-          probeResult: mongoResult,
-          notifications: notifications,
-          logMessage: logMessage,
-          isAlertTriggered: isAlertTriggered,
-          index: mongoRequestIndex,
-        })
-        mongoRequestIndex++
-      }
-    }
+async function probeNonHTTP(
+  probe: Probe,
+  checkOrder: number,
+  notifications: Notification[]
+) {
+  if (probe?.mongo) {
+    const probeResults = await probeMongo({
+      id: probe.id,
+      checkOrder,
+      mongo: probe.mongo,
+    })
 
-    if (probe?.mariadb || probe?.mysql) {
-      const { id, mariadb, mysql } = probe
-      let mariaReqIndex = 0
-      let logMessage = ''
-
-      const mydb = mariadb ?? mysql
-
-      if (mydb !== undefined) {
-        for await (const mariaIndex of mydb) {
-          const { host, port, database, username, password } = mariaIndex
-
-          const mariaResult = await mariaRequest({
-            host,
-            port,
-            database,
-            username,
-            password,
-          })
-          const timeNow = new Date().toISOString()
-
-          // eslint-disable-next-line unicorn/prefer-ternary
-          if (mariadb) {
-            logMessage = `${timeNow} ${checkOrder} id:${id} mariadb:${host}:${port} ${mariaResult.responseTime}ms msg:${mariaResult.body}`
-          } else {
-            logMessage = `${timeNow} ${checkOrder} id:${id} mysql:${host}:${port} ${mariaResult.responseTime}ms msg:${mariaResult.body}`
-          }
-
-          const isAlertTriggered = mariaResult.status !== 200
-
-          responseProcessing({
-            probe: probe,
-            probeResult: mariaResult,
-            notifications: notifications,
-            logMessage: logMessage,
-            isAlertTriggered: isAlertTriggered,
-            index: mariaReqIndex,
-          })
-          mariaReqIndex++
-        }
-      }
-    }
+    processProbeResults(probeResults, probe, notifications)
+  }
 
-    if (probe?.postgres) {
-      const { id, postgres } = probe
-      let pgReqIndex = 0
-      const postgresParams: PostgresParam = {
-        host: '',
-        port: 0,
-        database: '',
-        username: '',
-        password: '',
-      }
-
-      for await (const pgIndex of postgres) {
-        const { host, port, database, username, password, uri } = pgIndex
-
-        if (uri !== undefined) {
-          const config = parse(uri)
-
-          // If got uri format, parse and use that instead
-          postgresParams.host = config.host ?? '0.0.0.0'
-          postgresParams.port = Number(config.port) ?? 5432
-          postgresParams.database = config.database ?? ''
-          postgresParams.username = config.user ?? ''
-          postgresParams.password = config.password ?? ''
-        } else if (uri === undefined) {
-          postgresParams.host = host
-          postgresParams.port = port
-          postgresParams.database = database
-          postgresParams.username = username
-          postgresParams.password = password
-        }
-
-        const pgResult = await postgresRequest(postgresParams)
-
-        const timeNow = new Date().toISOString()
-        const logMessage = `${timeNow} ${checkOrder} id:${id} postgres:${postgresParams.host}:${postgresParams.port} ${pgResult.responseTime}ms msg:${pgResult.body}`
-        const isAlertTriggered = pgResult.status !== 200
-
-        responseProcessing({
-          probe: probe,
-          probeResult: pgResult,
-          notifications: notifications,
-          logMessage: logMessage,
-          isAlertTriggered: isAlertTriggered,
-          index: pgReqIndex,
-        })
-        pgReqIndex++
-      }
-    }
+  if (probe?.mariadb || probe?.mysql) {
+    const probeResults = await probeMariaDB({
+      id: probe.id,
+      checkOrder,
+      mysql: probe?.mysql,
+      mariaDB: probe?.mariadb,
+    })
 
-    if (probe?.redis) {
-      const { id, redis } = probe
-      let redisRequestIndex = 0
-      for await (const redisIndex of redis) {
-        const { host, port } = redisIndex
-        const redisRes = await redisRequest({ host: host, port: port })
-        const timeNow = new Date().toISOString()
-        const logMessage = `${timeNow} ${checkOrder} id:${id} redis:${host}:${port} ${redisRes.responseTime}ms msg:${redisRes.body}`
-        const isAlertTriggered = redisRes.status !== 200
-        responseProcessing({
-          probe: probe,
-          probeResult: redisRes,
-          notifications: notifications,
-          logMessage: logMessage,
-          isAlertTriggered: isAlertTriggered,
-          index: redisRequestIndex,
-        })
-        redisRequestIndex++
-      }
-    }
+    processProbeResults(probeResults, probe, notifications)
+  }
 
-    if (probe?.socket) {
-      const { id, socket } = probe
-      const { host, port, data } = socket
-      const url = `${host}:${port}`
-      const probeRes = await tcpRequest({ host, port, data })
-      const timeNow = new Date().toISOString()
-      const logMessage = `${timeNow} ${checkOrder} id:${id} tcp:${url} ${probeRes.responseTime}ms msg:${probeRes.body}`
-      const isAlertTriggered = probeRes.status !== 200
-      const TCPrequestIndex = 0
-      responseProcessing({
-        probe: probe,
-        probeResult: probeRes,
-        notifications: notifications,
-        logMessage: logMessage,
-        isAlertTriggered: isAlertTriggered,
-        index: TCPrequestIndex,
-      })
-    }
+  if (probe?.postgres) {
+    const probeResults = await probePostgres({
+      id: probe.id,
+      checkOrder,
+      postgres: probe.postgres,
+    })
 
-    // sending multiple http-type requests
-    for (
-      let requestIndex = 0;
-      requestIndex < probe?.requests?.length;
-      requestIndex++
-    ) {
-      const request = probe.requests?.[requestIndex]
-      const requestLog = new RequestLog(probe, requestIndex, checkOrder)
-
-      // create id-request
-      const id = `${probe?.id}:${request.url}:${requestIndex}:${request?.id} `
-
-      try {
-        // intentionally wait for a request to finish before processing next request in loop
-        // eslint-disable-next-line no-await-in-loop
-        const probeRes: ProbeRequestResponse = await httpRequest({
-          requestConfig: request,
-          responses,
-        })
-
-        logResponseTime(probeRes.responseTime)
-
-        eventEmitter.emit(events.probe.response.received, {
-          probe: probe,
-          requestIndex,
-          response: probeRes,
-        })
-
-        // Add to a response array to be accessed by another request for chaining later
-        responses.push(probeRes)
-        requestLog.setResponse(probeRes)
-
-        // combine global probe alerts with all individual request alerts
-        const probeAlerts = probe.alerts ?? []
-        const combinedAlerts = [...probeAlerts, ...(request.alerts || [])]
-
-        // Responses have been processed and validated
-        const validatedResponse = validateResponse(combinedAlerts, probeRes)
-
-        requestLog.addAlerts(
-          validatedResponse
-            .filter((item) => item.isAlertTriggered)
-            .map((item) => item.alert)
-        )
-
-        // done probing, got some result, process it, check for thresholds and notifications
-        const statuses = processThresholds({
-          probe: probe,
-          requestIndex,
-          validatedResponse,
-        })
-
-        // so we've got a status that need to be reported/alerted
-        // 1. check first, this connection is up, but was it ever down? if yes then use a specific connection recovery msg
-        // 2. if this connection is down, save to map and send specific connection incident msg
-        // 3. if event is not for connection failure, send user specified notification msg
-        if (statuses[0].shouldSendNotification) {
-          if (
-            probeRes.isProbeResponsive && // if connection is successful but
-            isConnectionDown.has(id) // if connection was down then send custom alert. Else use user's alert.
-          ) {
-            validatedResponse[0].alert = {
-              assertion: '',
-              message: `${connectionRecoveryMsg}`,
-            }
-            isConnectionDown.delete(id) // connection is up, so remove from entry
-          } else if (!probeRes.isProbeResponsive) {
-            // if connection has failed, then lets send out specific notification
-            validatedResponse[0].alert = {
-              assertion: '',
-              message: `${connectionIncidentMsg}`,
-            }
-            isConnectionDown.set(id, true) // connection is down, so add to map
-          }
-        }
-
-        // Done processing results, check if need to send out alerts
-        checkThresholdsAndSendAlert(
-          {
-            probe: probe,
-            statuses,
-            notifications: notifications,
-            requestIndex,
-            validatedResponseStatuses: validatedResponse,
-          },
-          requestLog
-        )
-
-        // Exit the chaining loop if there is any alert triggered
-        if (validatedResponse.some((item) => item.isAlertTriggered)) {
-          const triggeredAlertResponse = validatedResponse.find(
-            (item) => item.isAlertTriggered
-          )
-
-          if (triggeredAlertResponse) {
-            eventEmitter.emit(events.probe.alert.triggered, {
-              probe: probe,
-              requestIndex,
-              alertQuery: triggeredAlertResponse.alert.query,
-            })
-          }
-
-          break
-        }
-      } catch (error) {
-        requestLog.addError((error as any).message)
-        break
-      } finally {
-        requestLog.print()
-        if (verboseLogs || requestLog.hasIncidentOrRecovery) {
-          requestLog.saveToDatabase().catch((error) => log.error(error.message))
-        }
-      }
-    }
+    processProbeResults(probeResults, probe, notifications)
+  }
 
-    setProbeFinish(probe.id)
-  }, randomTimeout)
+  if (probe?.redis) {
+    const probeResults = await probeRedis({
+      id: probe.id,
+      checkOrder,
+      redis: probe.redis,
+    })
+
+    processProbeResults(probeResults, probe, notifications)
+  }
+
+  if (probe?.socket) {
+    const probeResults = await probeSocket({
+      id: probe.id,
+      checkOrder,
+      socket: probe.socket,
+    })
+
+    processProbeResults(probeResults, probe, notifications)
+  }
 }
diff --git a/src/components/probe/prober/http/index.ts b/src/components/probe/prober/http/index.ts
new file mode 100644
index 00000000..bdc4c420
--- /dev/null
+++ b/src/components/probe/prober/http/index.ts
@@ -0,0 +1,139 @@
+import { checkThresholdsAndSendAlert } from '../..'
+import { getContext } from '../../../../context'
+import events from '../../../../events'
+import type { Notification } from '../../../../interfaces/notification'
+import type { Probe } from '../../../../interfaces/probe'
+import type { ProbeRequestResponse } from '../../../../interfaces/request'
+import validateResponse from '../../../../plugins/validate-response'
+import { getEventEmitter } from '../../../../utils/events'
+import { log } from '../../../../utils/pino'
+import { RequestLog } from '../../../logger'
+import { logResponseTime } from '../../../logger/response-time-log'
+import { processThresholds } from '../../../notification/process-server-status'
+import { httpRequest } from './request'
+
+const CONNNECTION_RECOVERY_MESSAGE = 'Probe is accessible again'
+const CONNECTION_INCIDENT_MESSAGE = 'Probe not accessible'
+const isConnectionDown = new Map<string, boolean>()
+
+// sending multiple http-type requests
+export async function probeHTTP(
+  probe: Probe,
+  checkOrder: number,
+  notifications: Notification[]
+): Promise<void> {
+  const eventEmitter = getEventEmitter()
+  const { flags } = getContext()
+  const isSymonMode = Boolean(flags.symonUrl) && Boolean(flags.symonKey)
+  const isVerbose = isSymonMode || flags['keep-verbose-logs']
+  const responses = []
+
+  for (
+    let requestIndex = 0;
+    requestIndex < probe?.requests?.length;
+    requestIndex++
+  ) {
+    const request = probe.requests?.[requestIndex]
+    const requestLog = new RequestLog(probe, requestIndex, checkOrder)
+    // create id-request
+    const id = `${probe?.id}:${request.url}:${requestIndex}:${request?.id} `
+
+    try {
+      // intentionally wait for a request to finish before processing next request in loop
+      // eslint-disable-next-line no-await-in-loop
+      const probeRes: ProbeRequestResponse = await httpRequest({
+        requestConfig: request,
+        responses,
+      })
+      // combine global probe alerts with all individual request alerts
+      const combinedAlerts = [...probe.alerts, ...(request.alerts || [])]
+      // Responses have been processed and validated
+      const validatedResponse = validateResponse(combinedAlerts, probeRes)
+      // done probing, got some result, process it, check for thresholds and notifications
+      const statuses = processThresholds({
+        probe,
+        requestIndex,
+        validatedResponse,
+      })
+
+      eventEmitter.emit(events.probe.response.received, {
+        probe,
+        requestIndex,
+        response: probeRes,
+      })
+
+      logResponseTime(probeRes.responseTime)
+      // Add to a response array to be accessed by another request for chaining later
+      responses.push(probeRes)
+      requestLog.setResponse(probeRes)
+      requestLog.addAlerts(
+        validatedResponse
+          .filter((item) => item.isAlertTriggered)
+          .map((item) => item.alert)
+      )
+
+      // so we've got a status that need to be reported/alerted
+      // 1. check first, this connection is up, but was it ever down? if yes then use a specific connection recovery msg
+      // 2. if this connection is down, save to map and send specific connection incident msg
+      // 3. if event is not for connection failure, send user specified notification msg
+      if (statuses[0].shouldSendNotification) {
+        const { isProbeResponsive } = probeRes
+
+        if (
+          isProbeResponsive && // if connection is successful but
+          isConnectionDown.has(id) // if connection was down then send custom alert. Else use user's alert.
+        ) {
+          validatedResponse[0].alert = {
+            assertion: '',
+            message: CONNNECTION_RECOVERY_MESSAGE,
+          }
+          isConnectionDown.delete(id) // connection is up, so remove from entry
+        } else if (!isProbeResponsive) {
+          // if connection has failed, then lets send out specific notification
+          validatedResponse[0].alert = {
+            assertion: '',
+            message: CONNECTION_INCIDENT_MESSAGE,
+          }
+          isConnectionDown.set(id, true) // connection is down, so add to map
+        }
+      }
+
+      // Done processing results, check if need to send out alerts
+      checkThresholdsAndSendAlert(
+        {
+          probe,
+          statuses,
+          notifications,
+          requestIndex,
+          validatedResponseStatuses: validatedResponse,
+        },
+        requestLog
+      )
+
+      // Exit the chaining loop if there is any alert triggered
+      if (validatedResponse.some((item) => item.isAlertTriggered)) {
+        const triggeredAlertResponse = validatedResponse.find(
+          (item) => item.isAlertTriggered
+        )
+
+        if (triggeredAlertResponse) {
+          eventEmitter.emit(events.probe.alert.triggered, {
+            probe,
+            requestIndex,
+            alertQuery: triggeredAlertResponse.alert.query,
+          })
+        }
+
+        break
+      }
+    } catch (error) {
+      requestLog.addError((error as any).message)
+      break
+    } finally {
+      requestLog.print()
+      if (isVerbose || requestLog.hasIncidentOrRecovery) {
+        requestLog.saveToDatabase().catch((error) => log.error(error.message))
+      }
+    }
+  }
+}
diff --git a/src/components/http-request/index.ts b/src/components/probe/prober/http/request.ts
similarity index 96%
rename from src/components/http-request/index.ts
rename to src/components/probe/prober/http/request.ts
index a6594671..eb36f6ca 100644
--- a/src/components/http-request/index.ts
+++ b/src/components/probe/prober/http/request.ts
@@ -25,15 +25,18 @@
 import * as Handlebars from 'handlebars'
 import FormData from 'form-data'
 import YAML from 'yaml'
-import { ProbeRequestResponse, RequestConfig } from '../../interfaces/request'
+import {
+  ProbeRequestResponse,
+  RequestConfig,
+} from '../../../../interfaces/request'
 import * as qs from 'querystring'
 
 import http from 'http'
 import https from 'https'
-import { getContext } from '../../context'
-import { icmpRequest } from '../icmp-request'
-import registerFakes from '../../utils/fakes'
-import { sendHttpRequest } from '../../utils/http'
+import { getContext } from '../../../../context'
+import { icmpRequest } from '../icmp/request'
+import registerFakes from '../../../../utils/fakes'
+import { sendHttpRequest } from '../../../../utils/http'
 
 // Register Handlebars helpers
 registerFakes(Handlebars)
@@ -185,7 +188,7 @@ export async function httpRequest({
     // The request was made but no response was received
     // timeout is here, ECONNABORTED, ENOTFOUND, ECONNRESET, ECONNREFUSED
     if (error?.request) {
-      const status = errorRequestCodeToNumber(error?.code) // TODO: remove axios driver error mapping here
+      const status = errorRequestCodeToNumber(error?.code)
 
       return {
         data: '',
diff --git a/src/components/icmp-request/index.ts b/src/components/probe/prober/icmp/request.ts
similarity index 97%
rename from src/components/icmp-request/index.ts
rename to src/components/probe/prober/icmp/request.ts
index 694766ef..adf6020b 100644
--- a/src/components/icmp-request/index.ts
+++ b/src/components/probe/prober/icmp/request.ts
@@ -22,8 +22,8 @@
  * SOFTWARE.                                                                      *
  **********************************************************************************/
 
-import { sendPing } from '../../utils/ping'
-import { ProbeRequestResponse } from '../../interfaces/request'
+import { sendPing } from '../../../../utils/ping'
+import { ProbeRequestResponse } from '../../../../interfaces/request'
 
 type icmpParams = {
   host: string // target to ping
diff --git a/src/components/probe/prober/index.ts b/src/components/probe/prober/index.ts
new file mode 100644
index 00000000..e530aaab
--- /dev/null
+++ b/src/components/probe/prober/index.ts
@@ -0,0 +1,14 @@
+import type { ProbeRequestResponse } from '../../../interfaces/request'
+
+export { probeHTTP } from './http'
+export { probeMariaDB } from './mariadb'
+export { probeMongo } from './mongo'
+export { probePostgres } from './postgres'
+export { probeRedis } from './redis'
+export { probeSocket } from './socket'
+
+export type ProbeResult = {
+  isAlertTriggered: boolean
+  logMessage: string
+  requestResponse: ProbeRequestResponse
+}
diff --git a/src/components/probe/prober/mariadb/index.ts b/src/components/probe/prober/mariadb/index.ts
new file mode 100644
index 00000000..00fc0519
--- /dev/null
+++ b/src/components/probe/prober/mariadb/index.ts
@@ -0,0 +1,43 @@
+import type { ProbeResult } from '../'
+import type { MariaDB } from '../../../../interfaces/probe'
+import { mariaRequest } from './request'
+
+type ProbeMariaDBParams = {
+  id: string
+  checkOrder: number
+  mariaDB?: MariaDB[]
+  mysql?: MariaDB[]
+}
+
+export async function probeMariaDB({
+  id,
+  checkOrder,
+  mariaDB,
+  mysql,
+}: ProbeMariaDBParams): Promise<ProbeResult[]> {
+  const databases = mariaDB ?? mysql
+  const databaseText = mariaDB ? 'mariadb' : 'mysql'
+  const probeResults: ProbeResult[] = []
+
+  if (!databases) {
+    return probeResults
+  }
+
+  for await (const { host, port, database, username, password } of databases) {
+    const requestResponse = await mariaRequest({
+      host,
+      port,
+      database,
+      username,
+      password,
+    })
+    const { body, responseTime, status } = requestResponse
+    const isAlertTriggered = status !== 200
+    const timeNow = new Date().toISOString()
+    const logMessage = `${timeNow} ${checkOrder} id:${id} ${databaseText}:${host}:${port} ${responseTime}ms msg:${body}`
+
+    probeResults.push({ isAlertTriggered, logMessage, requestResponse })
+  }
+
+  return probeResults
+}
diff --git a/src/components/mariadb-request/index.ts b/src/components/probe/prober/mariadb/request.ts
similarity index 96%
rename from src/components/mariadb-request/index.ts
rename to src/components/probe/prober/mariadb/request.ts
index b066cd02..9be1805c 100644
--- a/src/components/mariadb-request/index.ts
+++ b/src/components/probe/prober/mariadb/request.ts
@@ -22,7 +22,7 @@
  * SOFTWARE.                                                                      *
  **********************************************************************************/
 
-import { ProbeRequestResponse } from '../../interfaces/request'
+import type { ProbeRequestResponse } from '../../../../interfaces/request'
 import { differenceInMilliseconds } from 'date-fns'
 import { createConnection } from 'mariadb'
 
@@ -70,7 +70,7 @@ export async function mariaRequest(
   if (isConnected) {
     baseResponse.responseTime = duration
     baseResponse.body = 'database ok'
-    baseResponse.status = 200 // TODO: Remove http mapping
+    baseResponse.status = 200
     baseResponse.isProbeResponsive = true
   }
 
diff --git a/src/components/probe/prober/mongo/index.ts b/src/components/probe/prober/mongo/index.ts
new file mode 100644
index 00000000..c821c9b5
--- /dev/null
+++ b/src/components/probe/prober/mongo/index.ts
@@ -0,0 +1,61 @@
+import * as mongodbURI from 'mongodb-uri'
+import type { ProbeResult } from '..'
+import type { Mongo } from '../../../../interfaces/probe'
+import { mongoRequest } from './request'
+
+type ProbeMongoParams = {
+  id: string
+  checkOrder: number
+  mongo: Mongo[]
+}
+
+export async function probeMongo({
+  id,
+  checkOrder,
+  mongo,
+}: ProbeMongoParams): Promise<ProbeResult[]> {
+  const probeResults: ProbeResult[] = []
+
+  for await (const mongoDB of mongo) {
+    const { host, password, port, uri, username } =
+      getMongoConnectionDetails(mongoDB)
+    const requestResponse = await mongoRequest({
+      uri,
+      host,
+      port,
+      username,
+      password,
+    })
+    const { body, responseTime, status } = requestResponse
+    const isAlertTriggered = status !== 200
+    const timeNow = new Date().toISOString()
+    const logMessage = `${timeNow} ${checkOrder} id:${id} mongo:${host}:${port} ${responseTime}ms msg:${body}`
+
+    probeResults.push({ isAlertTriggered, logMessage, requestResponse })
+  }
+
+  return probeResults
+}
+
+function getMongoConnectionDetails({
+  host,
+  password,
+  port,
+  uri,
+  username,
+}: Mongo): Mongo {
+  if (!uri) {
+    return { host, password, port, uri, username }
+  }
+
+  const parsed = mongodbURI.parse(uri)
+  const { hosts, password: parsedPassword, username: parsedUsername } = parsed
+
+  return {
+    host: hosts[0].host,
+    password: parsedPassword,
+    port: hosts[0].port,
+    uri,
+    username: parsedUsername,
+  }
+}
diff --git a/src/components/mongodb-request/index.ts b/src/components/probe/prober/mongo/request.ts
similarity index 97%
rename from src/components/mongodb-request/index.ts
rename to src/components/probe/prober/mongo/request.ts
index 52f60e01..4c7c05eb 100644
--- a/src/components/mongodb-request/index.ts
+++ b/src/components/probe/prober/mongo/request.ts
@@ -24,7 +24,7 @@
 
 import { MongoClient } from 'mongodb'
 import * as mongodbURI from 'mongodb-uri'
-import { ProbeRequestResponse } from '../../interfaces/request'
+import type { ProbeRequestResponse } from '../../../../interfaces/request'
 import { differenceInMilliseconds } from 'date-fns'
 
 type MongoRequest = {
@@ -66,7 +66,7 @@ export async function mongoRequest(
   if (result.isAlive) {
     baseResponse.responseTime = duration
     baseResponse.body = result.message
-    baseResponse.status = 200 // TODO: improve this up/down flag
+    baseResponse.status = 200
     baseResponse.isProbeResponsive = true
   } else {
     baseResponse.body = result.message
diff --git a/src/components/probe/prober/postgres/index.ts b/src/components/probe/prober/postgres/index.ts
new file mode 100644
index 00000000..4669fcc7
--- /dev/null
+++ b/src/components/probe/prober/postgres/index.ts
@@ -0,0 +1,51 @@
+import { parse } from 'pg-connection-string'
+import type { ProbeResult } from '..'
+import type { Postgres } from '../../../../interfaces/probe'
+import { postgresRequest } from './request'
+
+type ProbePostgresParams = {
+  id: string
+  checkOrder: number
+  postgres: Postgres[]
+}
+
+export async function probePostgres({
+  id,
+  checkOrder,
+  postgres,
+}: ProbePostgresParams): Promise<ProbeResult[]> {
+  const probeResults: ProbeResult[] = []
+
+  for await (const pg of postgres) {
+    const postgresConnectionDetails = getPostgresConnectionDetails(pg)
+    const { host, port } = postgresConnectionDetails
+    const requestResponse = await postgresRequest(postgresConnectionDetails)
+    const { body, responseTime, status } = requestResponse
+    const isAlertTriggered = status !== 200
+    const timeNow = new Date().toISOString()
+    const logMessage = `${timeNow} ${checkOrder} id:${id} postgres:${host}:${port} ${responseTime}ms msg:${body}`
+
+    probeResults.push({ isAlertTriggered, logMessage, requestResponse })
+  }
+
+  return probeResults
+}
+
+function getPostgresConnectionDetails(postgres: Postgres) {
+  const { uri } = postgres
+
+  if (!uri) {
+    return postgres
+  }
+
+  // If got uri format, parse and use that instead
+  const { database, host, password, port, user } = parse(uri)
+
+  return {
+    host: host ?? '0.0.0.0',
+    port: Number(port) ?? 5432,
+    database: database ?? '',
+    username: user ?? '',
+    password: password ?? '',
+  }
+}
diff --git a/src/components/postgres-request/index.ts b/src/components/probe/prober/postgres/request.ts
similarity index 98%
rename from src/components/postgres-request/index.ts
rename to src/components/probe/prober/postgres/request.ts
index bc624241..c93461b1 100644
--- a/src/components/postgres-request/index.ts
+++ b/src/components/probe/prober/postgres/request.ts
@@ -23,7 +23,7 @@
  **********************************************************************************/
 
 import { Pool } from 'pg'
-import { ProbeRequestResponse } from '../../interfaces/request'
+import type { ProbeRequestResponse } from '../../../../interfaces/request'
 import { differenceInMilliseconds } from 'date-fns'
 
 export type PostgresParam = {
diff --git a/src/components/probe/prober/redis/index.ts b/src/components/probe/prober/redis/index.ts
new file mode 100644
index 00000000..341b1810
--- /dev/null
+++ b/src/components/probe/prober/redis/index.ts
@@ -0,0 +1,32 @@
+import type { ProbeResult } from '..'
+import type { Redis } from '../../../../interfaces/probe'
+import { redisRequest } from './request'
+
+type ProbeRedisParams = {
+  id: string
+  checkOrder: number
+  redis: Redis[]
+}
+
+export async function probeRedis({
+  id,
+  checkOrder,
+  redis,
+}: ProbeRedisParams): Promise<ProbeResult[]> {
+  const probeResults: ProbeResult[] = []
+
+  for await (const { host, port } of redis) {
+    const requestResponse = await redisRequest({
+      host: host,
+      port: port,
+    })
+    const { body, responseTime, status } = requestResponse
+    const isAlertTriggered = status !== 200
+    const timeNow = new Date().toISOString()
+    const logMessage = `${timeNow} ${checkOrder} id:${id} redis:${host}:${port} ${responseTime}ms msg:${body}`
+
+    probeResults.push({ isAlertTriggered, logMessage, requestResponse })
+  }
+
+  return probeResults
+}
diff --git a/src/components/redis-request/index.ts b/src/components/probe/prober/redis/request.ts
similarity index 97%
rename from src/components/redis-request/index.ts
rename to src/components/probe/prober/redis/request.ts
index c0802473..7dac29de 100644
--- a/src/components/redis-request/index.ts
+++ b/src/components/probe/prober/redis/request.ts
@@ -23,7 +23,7 @@
  **********************************************************************************/
 
 import { createClient } from 'redis'
-import { ProbeRequestResponse } from '../../interfaces/request'
+import type { ProbeRequestResponse } from '../../../../interfaces/request'
 import { differenceInMilliseconds } from 'date-fns'
 
 type RedisRequest = {
@@ -65,7 +65,7 @@ export async function redisRequest(
   if (result.isAlive) {
     baseResponse.responseTime = duration
     baseResponse.body = result.message
-    baseResponse.status = 200 // TODO: improve this up/down flag
+    baseResponse.status = 200
     baseResponse.isProbeResponsive = true
   } else {
     baseResponse.body = result.message
diff --git a/src/components/probe/prober/socket/index.ts b/src/components/probe/prober/socket/index.ts
new file mode 100644
index 00000000..c4c5c39c
--- /dev/null
+++ b/src/components/probe/prober/socket/index.ts
@@ -0,0 +1,25 @@
+import type { ProbeResult } from '..'
+import type { Socket } from '../../../../interfaces/probe'
+import { tcpRequest } from './request'
+
+type ProbeSocketParams = {
+  id: string
+  checkOrder: number
+  socket: Socket
+}
+
+export async function probeSocket({
+  id,
+  checkOrder,
+  socket,
+}: ProbeSocketParams): Promise<ProbeResult[]> {
+  const { data, host, port } = socket
+  const url = `${host}:${port}`
+  const requestResponse = await tcpRequest({ host, port, data })
+  const { body, responseTime, status } = requestResponse
+  const isAlertTriggered = status !== 200
+  const timeNow = new Date().toISOString()
+  const logMessage = `${timeNow} ${checkOrder} id:${id} tcp:${url} ${responseTime}ms msg:${body}`
+
+  return [{ isAlertTriggered, logMessage, requestResponse }]
+}
diff --git a/src/components/tcp-request/index.ts b/src/components/probe/prober/socket/request.ts
similarity index 98%
rename from src/components/tcp-request/index.ts
rename to src/components/probe/prober/socket/request.ts
index 01ae7efc..52308037 100644
--- a/src/components/tcp-request/index.ts
+++ b/src/components/probe/prober/socket/request.ts
@@ -24,7 +24,7 @@
 
 import net from 'net'
 import { differenceInMilliseconds } from 'date-fns'
-import { ProbeRequestResponse } from '../../interfaces/request'
+import type { ProbeRequestResponse } from '../../../../interfaces/request'
 
 type TCPRequest = {
   host: string


Test patch:
diff --git a/src/components/http-request/http-request.test.ts b/src/components/probe/prober/http/request.test.ts
similarity index 98%
rename from src/components/http-request/http-request.test.ts
rename to src/components/probe/prober/http/request.test.ts
index 54d552cc..fdf05698 100644
--- a/src/components/http-request/http-request.test.ts
+++ b/src/components/probe/prober/http/request.test.ts
@@ -27,14 +27,14 @@ import { rest } from 'msw'
 import { setupServer } from 'msw/node'
 import { RequestInterceptor } from 'node-request-interceptor'
 import withDefaultInterceptors from 'node-request-interceptor/lib/presets/default'
-import { setContext } from '../../context'
-import type { MonikaFlags } from '../../context/monika-flags'
+import { setContext } from '../../../../context'
+import type { MonikaFlags } from '../../../../context/monika-flags'
 import type {
   ProbeRequestResponse,
   RequestConfig,
-} from '../../interfaces/request'
+} from '../../../../interfaces/request'
 
-import { generateRequestChainingBody, httpRequest } from '.'
+import { generateRequestChainingBody, httpRequest } from './request'
 
 describe('probingHTTP', () => {
   let interceptor: any
diff --git a/src/components/probe/prober/mariadb/index.test.ts b/src/components/probe/prober/mariadb/index.test.ts
new file mode 100644
index 00000000..945979bb
--- /dev/null
+++ b/src/components/probe/prober/mariadb/index.test.ts
@@ -0,0 +1,191 @@
+import { expect } from 'chai'
+import sinon from 'sinon'
+import { probeMariaDB } from '.'
+import { ProbeRequestResponse } from '../../../../interfaces/request'
+import * as request from './request'
+
+let mariaDBRequestStub: sinon.SinonStub
+
+describe('Maria DB/MySQL Prober', () => {
+  beforeEach(() => {
+    mariaDBRequestStub = sinon
+      .stub(request, 'mariaRequest')
+      .callsFake(async (_options): Promise<ProbeRequestResponse> => {
+        return {
+          requestType: 'mariadb',
+          data: '',
+          body: '',
+          status: 200,
+          headers: '',
+          responseTime: 0,
+          isProbeResponsive: false,
+        }
+      })
+  })
+
+  afterEach(() => {
+    sinon.restore()
+  })
+
+  it('should return empty result', async () => {
+    // act
+    const probeResults = await probeMariaDB({ id: 'FgYCA', checkOrder: 1 })
+
+    // assert
+    expect(probeResults).deep.eq([])
+    sinon.assert.notCalled(mariaDBRequestStub)
+  })
+
+  it('should probe using Maria DB', async () => {
+    // arrange
+    const probeParams = {
+      id: 'FgYCA',
+      checkOrder: 1,
+      mariaDB: [
+        {
+          host: 'localhost',
+          port: 3306,
+          database: 'monika_database',
+          username: 'monika_username',
+          password: 'monika_password',
+        },
+      ],
+    }
+
+    // act
+    const probeResults = await probeMariaDB(probeParams)
+
+    // assert
+    expect(probeResults.length).deep.eq(1)
+    expect(probeResults[0].logMessage).include('mariadb')
+    sinon.assert.calledOnce(mariaDBRequestStub)
+  })
+
+  it('should probe using Maria DB if both of the database is filled', async () => {
+    // arrange
+    const probeParams = {
+      id: 'FgYCA',
+      checkOrder: 1,
+      mariaDB: [
+        {
+          host: 'localhost',
+          port: 3306,
+          database: 'monika_database',
+          username: 'monika_username',
+          password: 'monika_password',
+        },
+      ],
+      mysql: [
+        {
+          host: 'localhost',
+          port: 3306,
+          database: 'monika_database',
+          username: 'monika_username',
+          password: 'monika_password',
+        },
+      ],
+    }
+
+    // act
+    const probeResults = await probeMariaDB(probeParams)
+
+    // assert
+    expect(probeResults.length).eq(1)
+    expect(probeResults[0].logMessage).include('mariadb')
+    sinon.assert.calledOnce(mariaDBRequestStub)
+  })
+
+  it('should probe using MySQL', async () => {
+    // arrange
+    const probeParams = {
+      id: 'wTBPV',
+      checkOrder: 1,
+      mysql: [
+        {
+          host: 'localhost',
+          port: 3306,
+          database: 'monika_database',
+          username: 'monika_username',
+          password: 'monika_password',
+        },
+      ],
+    }
+
+    // act
+    const probeResults = await probeMariaDB(probeParams)
+
+    // assert
+    expect(probeResults.length).eq(1)
+    expect(probeResults[0].logMessage).include('mysql')
+    sinon.assert.calledOnce(mariaDBRequestStub)
+  })
+
+  it('should probe multiple probes', async () => {
+    // arrange
+    const probeParams = {
+      id: 'wTBPV',
+      checkOrder: 1,
+      mariaDB: [
+        {
+          host: 'localhost',
+          port: 3306,
+          database: 'monika_database',
+          username: 'monika_username',
+          password: 'monika_password',
+        },
+        {
+          host: 'localhost',
+          port: 3306,
+          database: 'monika_database_staging',
+          username: 'monika_username_staging',
+          password: 'monika_password_staging',
+        },
+      ],
+    }
+
+    // act
+    const probeResults = await probeMariaDB(probeParams)
+
+    // assert
+    expect(probeResults.length).eq(2)
+    sinon.assert.calledTwice(mariaDBRequestStub)
+  })
+
+  it('should return alert triggered', async () => {
+    // arrange
+    sinon.restore()
+    mariaDBRequestStub = sinon
+      .stub(request, 'mariaRequest')
+      .callsFake(async (_options): Promise<ProbeRequestResponse> => {
+        return {
+          requestType: 'mariadb',
+          data: '',
+          body: '',
+          status: 0,
+          headers: '',
+          responseTime: 0,
+          isProbeResponsive: false,
+        }
+      })
+    const probeParams = {
+      id: 'wTBPV',
+      checkOrder: 1,
+      mariaDB: [
+        {
+          host: 'localhost',
+          port: 3306,
+          database: 'monika_database',
+          username: 'monika_username',
+          password: 'monika_password',
+        },
+      ],
+    }
+
+    // act
+    const probeResults = await probeMariaDB(probeParams)
+
+    // assert
+    expect(probeResults[0].isAlertTriggered).eq(true)
+    sinon.assert.calledOnce(mariaDBRequestStub)
+  })
+})
diff --git a/src/components/probe/prober/mongo/index.test.ts b/src/components/probe/prober/mongo/index.test.ts
new file mode 100644
index 00000000..a18f548f
--- /dev/null
+++ b/src/components/probe/prober/mongo/index.test.ts
@@ -0,0 +1,159 @@
+import { expect } from 'chai'
+import sinon from 'sinon'
+import { probeMongo } from '.'
+import { ProbeRequestResponse } from '../../../../interfaces/request'
+import * as request from './request'
+
+let mongoRequestStub: sinon.SinonStub
+
+describe('MongoDB Prober', () => {
+  beforeEach(() => {
+    mongoRequestStub = sinon
+      .stub(request, 'mongoRequest')
+      .callsFake(async (_options): Promise<ProbeRequestResponse> => {
+        return {
+          requestType: 'mongo',
+          data: '',
+          body: '',
+          status: 200,
+          headers: '',
+          responseTime: 0,
+          isProbeResponsive: false,
+        }
+      })
+  })
+
+  afterEach(() => {
+    sinon.restore()
+  })
+
+  it('should probe using MongoDB', async () => {
+    // arrange
+    const probeParams = {
+      id: 'FgYCA',
+      checkOrder: 1,
+      mongo: [
+        {
+          host: 'localhost',
+          port: 2701,
+          username: 'monika_username',
+          password: 'monika_password',
+        },
+      ],
+    }
+
+    // act
+    const probeResults = await probeMongo(probeParams)
+
+    // assert
+    expect(probeResults.length).deep.eq(1)
+    expect(probeResults[0].logMessage).include('mongo')
+    sinon.assert.calledOnce(mongoRequestStub)
+  })
+
+  it('should probe multiple probes', async () => {
+    // arrange
+    const probeParams = {
+      id: 'wTBPV',
+      checkOrder: 1,
+      mongo: [
+        {
+          host: 'localhost',
+          port: 2701,
+          database: 'monika_database',
+          username: 'monika_username',
+          password: 'monika_password',
+        },
+        {
+          host: 'localhost',
+          port: 2701,
+          database: 'monika_database_staging',
+          username: 'monika_username_staging',
+          password: 'monika_password_staging',
+        },
+      ],
+    }
+
+    // act
+    const probeResults = await probeMongo(probeParams)
+
+    // assert
+    expect(probeResults.length).eq(2)
+    sinon.assert.calledTwice(mongoRequestStub)
+  })
+
+  it('should return alert triggered', async () => {
+    // arrange
+    sinon.restore()
+    mongoRequestStub = sinon
+      .stub(request, 'mongoRequest')
+      .callsFake(async (_options): Promise<ProbeRequestResponse> => {
+        return {
+          requestType: 'mongo',
+          data: '',
+          body: '',
+          status: 0,
+          headers: '',
+          responseTime: 0,
+          isProbeResponsive: false,
+        }
+      })
+    const probeParams = {
+      id: 'wTBPV',
+      checkOrder: 1,
+      mongo: [
+        {
+          host: 'localhost',
+          port: 2701,
+          username: 'monika_username',
+          password: 'monika_password',
+        },
+      ],
+    }
+
+    // act
+    const probeResults = await probeMongo(probeParams)
+
+    // assert
+    expect(probeResults[0].isAlertTriggered).eq(true)
+    sinon.assert.calledOnce(mongoRequestStub)
+  })
+
+  it('should use mongoDB uri', async () => {
+    // arrange
+    sinon.restore()
+    mongoRequestStub = sinon
+      .stub(request, 'mongoRequest')
+      .callsFake(async (_options): Promise<ProbeRequestResponse> => {
+        return {
+          requestType: 'mongo',
+          data: '',
+          body: '',
+          status: 0,
+          headers: '',
+          responseTime: 0,
+          isProbeResponsive: false,
+        }
+      })
+    const probeParams = {
+      id: 'wTBPV',
+      checkOrder: 1,
+      mongo: [
+        {
+          host: 'localhost',
+          port: 2701,
+          username: 'monika_username',
+          password: 'monika_password',
+          uri: 'mongodb://monika_username:monika_password@localhost:2701',
+        },
+      ],
+    }
+
+    // act
+    const probeResults = await probeMongo(probeParams)
+
+    // assert
+    expect(probeResults[0].isAlertTriggered).eq(true)
+    sinon.assert.calledOnce(mongoRequestStub)
+  })
+})
diff --git a/src/components/probe/prober/postgres/index.test.ts b/src/components/probe/prober/postgres/index.test.ts
new file mode 100644
index 00000000..f4d80a4c
--- /dev/null
+++ b/src/components/probe/prober/postgres/index.test.ts
@@ -0,0 +1,166 @@
+import { expect } from 'chai'
+import sinon from 'sinon'
+import { probePostgres } from '.'
+import { ProbeRequestResponse } from '../../../../interfaces/request'
+import * as request from './request'
+
+let postgresRequestStub: sinon.SinonStub
+
+describe('PostgreSQL Prober', () => {
+  beforeEach(() => {
+    postgresRequestStub = sinon
+      .stub(request, 'postgresRequest')
+      .callsFake(async (_options): Promise<ProbeRequestResponse> => {
+        return {
+          requestType: 'postgres',
+          data: '',
+          body: '',
+          status: 200,
+          headers: '',
+          responseTime: 0,
+          isProbeResponsive: false,
+        }
+      })
+  })
+
+  afterEach(() => {
+    sinon.restore()
+  })
+
+  it('should probe using PostgresSQL', async () => {
+    // arrange
+    const probeParams = {
+      id: 'FgYCA',
+      checkOrder: 1,
+      postgres: [
+        {
+          uri: '',
+          host: 'localhost',
+          port: 5432,
+          database: 'monika_database',
+          username: 'monika_username',
+          password: 'monika_password',
+        },
+      ],
+    }
+
+    // act
+    const probeResults = await probePostgres(probeParams)
+
+    // assert
+    expect(probeResults.length).deep.eq(1)
+    expect(probeResults[0].logMessage).include('postgres')
+    sinon.assert.calledOnce(postgresRequestStub)
+  })
+
+  it('should probe multiple probes', async () => {
+    // arrange
+    const probeParams = {
+      id: 'wTBPV',
+      checkOrder: 1,
+      postgres: [
+        {
+          uri: '',
+          host: 'localhost',
+          port: 5432,
+          database: 'monika_database',
+          username: 'monika_username',
+          password: 'monika_password',
+        },
+        {
+          uri: '',
+          host: 'localhost',
+          port: 5432,
+          database: 'monika_database_staging',
+          username: 'monika_username_staging',
+          password: 'monika_password_staging',
+        },
+      ],
+    }
+
+    // act
+    const probeResults = await probePostgres(probeParams)
+
+    // assert
+    expect(probeResults.length).eq(2)
+    sinon.assert.calledTwice(postgresRequestStub)
+  })
+
+  it('should return alert triggered', async () => {
+    // arrange
+    sinon.restore()
+    postgresRequestStub = sinon
+      .stub(request, 'postgresRequest')
+      .callsFake(async (_options): Promise<ProbeRequestResponse> => {
+        return {
+          requestType: 'postgres',
+          data: '',
+          body: '',
+          status: 0,
+          headers: '',
+          responseTime: 0,
+          isProbeResponsive: false,
+        }
+      })
+    const probeParams = {
+      id: 'wTBPV',
+      checkOrder: 1,
+      postgres: [
+        {
+          uri: '',
+          host: 'localhost',
+          port: 5432,
+          username: 'monika_username',
+          password: 'monika_password',
+          database: 'monika_database',
+        },
+      ],
+    }
+
+    // act
+    const probeResults = await probePostgres(probeParams)
+
+    // assert
+    expect(probeResults[0].isAlertTriggered).eq(true)
+    sinon.assert.calledOnce(postgresRequestStub)
+  })
+
+  it('should use postgresDB uri', async () => {
+    // arrange
+    sinon.restore()
+    postgresRequestStub = sinon
+      .stub(request, 'postgresRequest')
+      .callsFake(async (_options): Promise<ProbeRequestResponse> => {
+        return {
+          requestType: 'postgres',
+          data: '',
+          body: '',
+          status: 0,
+          headers: '',
+          responseTime: 0,
+          isProbeResponsive: false,
+        }
+      })
+    const probeParams = {
+      id: 'wTBPV',
+      checkOrder: 1,
+      postgres: [
+        {
+          host: 'localhost',
+          port: 5432,
+          username: 'monika_username',
+          password: 'monika_password',
+          database: 'monika_database',
+          uri: 'postgresdb://monika_username:monika_password@localhost:5432',
+        },
+      ],
+    }
+
+    // act
+    const probeResults = await probePostgres(probeParams)
+
+    // assert
+    expect(probeResults[0].isAlertTriggered).eq(true)
+    sinon.assert.calledOnce(postgresRequestStub)
+  })
+})
diff --git a/src/components/probe/prober/redis/redis.test.ts b/src/components/probe/prober/redis/redis.test.ts
new file mode 100644
index 00000000..cbd5d3d3
--- /dev/null
+++ b/src/components/probe/prober/redis/redis.test.ts
@@ -0,0 +1,111 @@
+import { expect } from 'chai'
+import sinon from 'sinon'
+import { probeRedis } from '.'
+import { ProbeRequestResponse } from '../../../../interfaces/request'
+import * as request from './request'
+
+let redisPingStub: sinon.SinonStub
+
+describe('Redis Prober', () => {
+  beforeEach(() => {
+    redisPingStub = sinon
+      .stub(request, 'redisRequest')
+      .callsFake(async (_options): Promise<ProbeRequestResponse> => {
+        return {
+          requestType: 'redis',
+          data: '',
+          body: '',
+          status: 200,
+          headers: '',
+          responseTime: 0,
+          isProbeResponsive: false,
+        }
+      })
+  })
+
+  afterEach(() => {
+    sinon.restore()
+  })
+
+  it('should probe using Redis', async () => {
+    // arrange
+    const probeParams = {
+      id: 'FgYCA',
+      checkOrder: 1,
+      redis: [
+        {
+          host: 'localhost',
+          port: 6379,
+        },
+      ],
+    }
+
+    // act
+    const probeResults = await probeRedis(probeParams)
+
+    // assert
+    expect(probeResults.length).deep.eq(1)
+    expect(probeResults[0].logMessage).include('redis')
+    sinon.assert.calledOnce(redisPingStub)
+  })
+
+  it('should probe multiple probes', async () => {
+    // arrange
+    const probeParams = {
+      id: 'wTBPV',
+      checkOrder: 1,
+      redis: [
+        {
+          host: 'localhost',
+          port: 6379,
+        },
+        {
+          host: 'localhost',
+          port: 6379,
+        },
+      ],
+    }
+
+    // act
+    const probeResults = await probeRedis(probeParams)
+
+    // assert
+    expect(probeResults.length).eq(2)
+    sinon.assert.calledTwice(redisPingStub)
+  })
+
+  it('should return alert triggered', async () => {
+    // arrange
+    sinon.restore()
+    redisPingStub = sinon
+      .stub(request, 'redisRequest')
+      .callsFake(async (_options): Promise<ProbeRequestResponse> => {
+        return {
+          requestType: 'redis',
+          data: '',
+          body: '',
+          status: 0,
+          headers: '',
+          responseTime: 0,
+          isProbeResponsive: false,
+        }
+      })
+    const probeParams = {
+      id: 'wTBPV',
+      checkOrder: 1,
+      redis: [
+        {
+          host: 'localhost',
+          port: 6379,
+        },
+      ],
+    }
+
+    // act
+    const probeResults = await probeRedis(probeParams)
+
+    // assert
+    expect(probeResults[0].isAlertTriggered).eq(true)
+    sinon.assert.calledOnce(redisPingStub)
+  })
+})
diff --git a/src/components/probe/prober/socket/index.test.ts b/src/components/probe/prober/socket/index.test.ts
new file mode 100644
index 00000000..d1201a91
--- /dev/null
+++ b/src/components/probe/prober/socket/index.test.ts
@@ -0,0 +1,84 @@
+import { expect } from 'chai'
+import sinon from 'sinon'
+import { probeSocket } from '.'
+import { ProbeRequestResponse } from '../../../../interfaces/request'
+import * as request from './request'
+
+let tcpRequestStub: sinon.SinonStub
+
+describe('TCP Prober', () => {
+  beforeEach(() => {
+    tcpRequestStub = sinon
+      .stub(request, 'tcpRequest')
+      .callsFake(async (_options): Promise<ProbeRequestResponse> => {
+        return {
+          requestType: 'tcp',
+          data: '',
+          body: '',
+          status: 200,
+          headers: '',
+          responseTime: 0,
+          isProbeResponsive: false,
+        }
+      })
+  })
+
+  afterEach(() => {
+    sinon.restore()
+  })
+
+  it('should probe using TCP', async () => {
+    // arrange
+    const probeParams = {
+      id: 'FgYCA',
+      checkOrder: 1,
+      socket: {
+        host: 'localhost',
+        port: 22,
+        data: '',
+      },
+    }
+
+    // act
+    const probeResults = await probeSocket(probeParams)
+
+    // assert
+    expect(probeResults.length).deep.eq(1)
+    expect(probeResults[0].logMessage).include('tcp')
+    sinon.assert.calledOnce(tcpRequestStub)
+  })
+
+  it('should return alert triggered', async () => {
+    // arrange
+    sinon.restore()
+    tcpRequestStub = sinon
+      .stub(request, 'tcpRequest')
+      .callsFake(async (_options): Promise<ProbeRequestResponse> => {
+        return {
+          requestType: 'tcp',
+          data: '',
+          body: '',
+          status: 0,
+          headers: '',
+          responseTime: 0,
+          isProbeResponsive: false,
+        }
+      })
+    const probeParams = {
+      id: 'wTBPV',
+      checkOrder: 1,
+      socket: {
+        host: 'localhost',
+        port: 22,
+        data: '',
+      },
+    }
+
+    // act
+    const probeResults = await probeSocket(probeParams)
+
+    // assert
+    expect(probeResults[0].isAlertTriggered).eq(true)
+    sinon.assert.calledOnce(tcpRequestStub)
+  })
+})