Repository: MaskingTechnology/jitar (ts)

Issue:
Run health checks in application space
Currently, health checks are registered at the server as follows.

```ts
import { startServer } from 'jitar';
import DatabaseHealthCheck from './health/DatabaseHealthCheck.js';

const server = await startServer(moduleImporter);
server.addHealthCheck('database', new DatabaseHealthCheck());
```

And activated in the service configuration like this:

```json
{
    "url": "http://127.0.0.1:3000",
    "healthChecks": ["database"],
    "standalone": {}
}
```

Although this works well for most checks, others can't perform their task. Health checks run in the service space, which is a separate space from the application space. Meaning that if, for example, the database gets connected from the application space, the database health check can't check the database health because it runs in the service space.

To solve this issue, we need to rethink the health check registration. The following solution is proposed.

Health checks are registered in the service configuration by importing modules containing a health check instance.

```json
{
    "url": "http://127.0.0.1:3000",
    "healthChecks": ["./databaseHealthCheck"],
    "standalone": {}
}
```

An example health check instance module looks like this.

```
// src/databaseHealthCheck.ts
import DatabaseHealthCheck from './health/DatabaseHealthCheck.js';

const instance = new DatabaseHealthCheck();
export default instance;
```

In this example the health check class and instance are separated. This is good practice to enable reusability of the health checks. For simple cases, the class and instance can be defined in the same module file.

This proposed solution also makes the bootstrapper file more generic by removing the registration from the server.

Pull request description:
394 run health checks in application space
Fixes #394 

Changes proposed in this pull request:
- Improved the health check registration strategy
- Updated the contact app to use a database health check
- Updated the documentation

@MaskingTechnology/jitar


Patch:
diff --git a/documentation/docs/deploy/health-checks.md b/documentation/docs/deploy/health-checks.md
index 00e3a6b..0c06878 100644
--- a/documentation/docs/deploy/health-checks.md
+++ b/documentation/docs/deploy/health-checks.md
@@ -17,55 +17,45 @@ The [gateway service](../fundamentals/runtime-services#gateway) determines if a
 
 ## Creating health checks
 
-Jitar provides an interface you have to implement in order to add a custom check to a service. Let's look at a simple database check example:
+Jitar provides an interface you have to implement in order to add a custom check to a service. Only health check instances can be registered. Each instance needs to be exported as default in a separate module file. Let's look at a simple database check example:
 
 ```ts
-// src/health/DatabaseHealthCheck.ts
+// src/databaseHealthCheck.ts
 import { HealthCheck } from 'jitar';
-import { pingDatabase } from '../common/utils';
 
 export default class DatabaseHealthCheck implements HealthCheck
 {
+    get name() { return 'database'; }
+
     get timeout() { return undefined; }
 
     async isHealthy(): Promise<boolean>
     {
-        return pingDatabase();
+        /* check status here */
     }
-} 
-```
-
-The health check interface requires you to implement the `timeout` getter. If a timeout is not required, return `undefined`. Otherwise set the time in milliseconds for the health check to be considered unhealthy.
-
-## Adding health checks
-
-A health check needs to be added to the service that has the connection to the database. We need to update the starter script to register the health check at the server level, and use configuration to determine the actual instance that will get the health check.
+}
 
-```ts
-// jitar.ts
-import { buildServer } from 'jitar';
+const instance = new DatabaseHealthCheck();
 
-import DatabaseHealthCheck from './health/DatabaseHealthCheck.js';
+export default instance;
+```
 
-const moduleImporter = async (specifier: string) => import(specifier);
+The health check interface requires you to implement the `name` and  `timeout` getter. The name is used for its registration, and needs to be unique. The timeout specifies the time in milliseconds that the service waits before checking for the health again. If a timeout is not required, return `undefined`. Otherwise set the time in milliseconds.
 
-const server = await buildServer(moduleImporter);
-server.registerHealthCheck('database', new DatabaseHealthCheck());
-server.start();
-```
+## Adding health checks
 
-The configuration of the gateway needs to be updated to include the health check.
+A health check needs to be added to the service that has the connection to the database. This is done by registering the health check module file in the service configuration.
 
 ```json
-// services/gateway.json
+// services/node.json
 {
     "url": "http://localhost:3000",
-    "healthChecks": ["database"],
-    "gateway":
+    "healthChecks": ["./databaseHealthCheck"],
+    "node":
     {
         // ...
     }
 }
 ```
 
-Once added, the gateway will trigger the check automatically. You can also check yourself using the health API. More information on this can be found in the [MONITOR section](../monitor/health).
+Once added, the node will trigger the check automatically. You can also check yourself using the health API. More information on this can be found in the [MONITOR section](../monitor/health).
diff --git a/documentation/docs/fundamentals/runtime-services.md b/documentation/docs/fundamentals/runtime-services.md
index c9b38f7..afbfcbb 100644
--- a/documentation/docs/fundamentals/runtime-services.md
+++ b/documentation/docs/fundamentals/runtime-services.md
@@ -28,6 +28,7 @@ Configurations are placed in JSON files. The basic structure looks like this.
     "url": "SERVICE_URL",
     "setUp": "SET_UP_SCRIPT",
     "tearDown": "TEAR_DOWN_SCRIPT",
+    "healthChecks": ["HEALTH_CHECK_SCRIPT"],
     "SERVICE_TYPE":
     {
         "PROPERTY_1": "…",
@@ -41,6 +42,7 @@ There are four properties at root level:
 * url - service url containing protocol, address and port (e.g. `http://service.example.com:3000`).
 * setUp - optional [set up script](../develop/setup-and-teardown.md) that gets executed on startup (e.g. `./setUp`).
 * tearDown - optional [tear down script](../develop/setup-and-teardown.md) that gets executed on shutdown (e.g. `./tearDown`).
+* healthChecks - optional list of [health check scripts](../deploy/health-checks.md) for checking the service health.
 * SERVICE_TYPE - configuration of the specific service (differs per type).
 
 An instance can only run one type of service. Each service has its own configuration properties. All types and their properties are explained next.
diff --git a/examples/apps/contact-list/services/node.json b/examples/apps/contact-list/services/node.json
index 010d4ad..d043a24 100644
--- a/examples/apps/contact-list/services/node.json
+++ b/examples/apps/contact-list/services/node.json
@@ -1,7 +1,8 @@
 {
     "url": "http://127.0.0.1:3001",
-    "setUp": "./setUpNode",
-    "tearDown": "./tearDownNode",
+    "setUp": "./integrations/jitar/setUpNode",
+    "tearDown": "./integrations/jitar/tearDownNode",
+    "healthChecks": ["./integrations/jitar/databaseHealthCheck"],
     "node":
     {
         "gateway": "http://127.0.0.1:3000",
diff --git a/examples/apps/contact-list/services/standalone.json b/examples/apps/contact-list/services/standalone.json
index 582f0a9..95bbe1a 100644
--- a/examples/apps/contact-list/services/standalone.json
+++ b/examples/apps/contact-list/services/standalone.json
@@ -1,7 +1,7 @@
 {
     "url": "http://127.0.0.1:3000",
-    "setUp": "./setUpNode",
-    "tearDown": "./tearDownNode",
+    "setUp": "./integrations/jitar/setUpNode",
+    "tearDown": "./integrations/jitar/tearDownNode",
     "standalone":
     {
         "assets": [ "index.html", "main.js", "App.js", "vite.svg", "assets/**/*" ]
diff --git a/examples/apps/contact-list/src/integrations/database/Database.ts b/examples/apps/contact-list/src/integrations/database/Database.ts
index bdf5909..58a0b0c 100644
--- a/examples/apps/contact-list/src/integrations/database/Database.ts
+++ b/examples/apps/contact-list/src/integrations/database/Database.ts
@@ -7,11 +7,11 @@ class Database
 {
     #client?: MongoClient;
     #database?: Db;
+    #connected = false;
 
     get connected(): boolean
     {
-        return this.#client !== undefined
-            && this.#database !== undefined;
+        return this.#connected;
     }
 
     connect(connectionString: string, database: string): void
@@ -19,6 +19,13 @@ class Database
         try
         {
             this.#client = new MongoClient(connectionString);
+
+            this.#client.on('open', () => { this.#connected = true; });
+            this.#client.on('close', () => { this.#connected = false; });
+            
+            this.#client.on('serverHeartbeatSucceeded', () => { this.#connected = true; });
+            this.#client.on('serverHeartbeatFailed', () => { this.#connected = false; });
+
             this.#database = this.#client.db(database);
         }
         catch (error: unknown)
@@ -38,6 +45,7 @@ class Database
         {
             await this.#client.close();
 
+            this.#connected = false;
             this.#database = undefined;
             this.#client = undefined;
         }
diff --git a/examples/apps/contact-list/src/integrations/jitar/databaseHealthCheck.ts b/examples/apps/contact-list/src/integrations/jitar/databaseHealthCheck.ts
new file mode 100644
index 0000000..5c674f4
--- /dev/null
+++ b/examples/apps/contact-list/src/integrations/jitar/databaseHealthCheck.ts
@@ -0,0 +1,18 @@
+
+import { HealthCheck } from 'jitar';
+
+import Database from '../database/Database';
+
+class DatabaseHealthCheck implements HealthCheck
+{
+    get name() { return 'database'; }
+
+    get timeout() { return undefined; }
+
+    async isHealthy(): Promise<boolean>
+    {
+        return Database.connected;
+    }
+}
+
+export default new DatabaseHealthCheck();
diff --git a/examples/apps/contact-list/src/setUpNode.ts b/examples/apps/contact-list/src/integrations/jitar/setUpNode.ts
similarity index 67%
rename from examples/apps/contact-list/src/setUpNode.ts
rename to examples/apps/contact-list/src/integrations/jitar/setUpNode.ts
index d266997..0b950d4 100644
--- a/examples/apps/contact-list/src/setUpNode.ts
+++ b/examples/apps/contact-list/src/integrations/jitar/setUpNode.ts
@@ -1,5 +1,5 @@
 
-import Database from './integrations/database/Database';
+import Database from '../database/Database';
 
 Database.connect('mongodb://root:example@localhost:27017', 'react-mongodb');
 
diff --git a/examples/apps/contact-list/src/tearDownNode.ts b/examples/apps/contact-list/src/integrations/jitar/tearDownNode.ts
similarity index 56%
rename from examples/apps/contact-list/src/tearDownNode.ts
rename to examples/apps/contact-list/src/integrations/jitar/tearDownNode.ts
index 8466b35..48d0599 100644
--- a/examples/apps/contact-list/src/tearDownNode.ts
+++ b/examples/apps/contact-list/src/integrations/jitar/tearDownNode.ts
@@ -1,5 +1,5 @@
 
-import Database from './integrations/database/Database';
+import Database from '../database/Database';
 
 await Database.disconnect();
 
diff --git a/examples/concepts/health-checks/services/standalone.json b/examples/concepts/health-checks/services/standalone.json
index 1133d65..b541842 100644
--- a/examples/concepts/health-checks/services/standalone.json
+++ b/examples/concepts/health-checks/services/standalone.json
@@ -1,6 +1,6 @@
 {
     "url": "http://127.0.0.1:3000",
-    "healthChecks": ["database"],
+    "healthChecks": ["./databaseHealthCheck"],
     "standalone":
     {
         
diff --git a/examples/concepts/health-checks/src/DatabaseHealthCheck.ts b/examples/concepts/health-checks/src/DatabaseHealthCheck.ts
deleted file mode 100644
index 60a13c2..0000000
--- a/examples/concepts/health-checks/src/DatabaseHealthCheck.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-
-/*
- * Health checks are used to determine if a node is healthy or not.
- * They are used by the gateway to determine if a node still can be used.
- * 
- * A health check is a class that implements the HealthCheck interface.
- * The interface has a single function called isHealthy() that returns a boolean.
- * 
- * Because the function is async it can perform any kind of check
- * (e.g. database avaialability).
- */
-
-import { HealthCheck } from 'jitar';
-
-export default class DatabaseHealthCheck implements HealthCheck
-{
-    get timeout() { return undefined; }
-
-    async isHealthy(): Promise<boolean>
-    {
-        // Check database connection
-        return true;
-    }
-}
diff --git a/examples/concepts/health-checks/src/databaseHealthCheck.ts b/examples/concepts/health-checks/src/databaseHealthCheck.ts
new file mode 100644
index 0000000..2c99b48
--- /dev/null
+++ b/examples/concepts/health-checks/src/databaseHealthCheck.ts
@@ -0,0 +1,19 @@
+
+import { HealthCheck } from 'jitar';
+
+class DatabaseHealthCheck implements HealthCheck
+{
+    get name() { return 'database'; }
+
+    get timeout() { return undefined; }
+
+    async isHealthy(): Promise<boolean>
+    {
+        // Check database connection
+        return true;
+    }
+}
+
+const instance = new DatabaseHealthCheck();
+
+export default instance;
diff --git a/examples/concepts/health-checks/src/jitar.ts b/examples/concepts/health-checks/src/jitar.ts
index a0a5f39..89260a3 100644
--- a/examples/concepts/health-checks/src/jitar.ts
+++ b/examples/concepts/health-checks/src/jitar.ts
@@ -1,10 +1,7 @@
 
 import { buildServer } from 'jitar';
 
-import DatabaseHealthCheck from './DatabaseHealthCheck.js';
-
 const moduleImporter = async (specifier: string) => import(specifier);
 
 const server = await buildServer(moduleImporter);
-server.registerHealthCheck('database', new DatabaseHealthCheck());
 server.start();
diff --git a/migrations/migrate-from-0.4.x-to-0.5.0.md b/migrations/migrate-from-0.4.x-to-0.5.0.md
index 3caf0db..f6af388 100644
--- a/migrations/migrate-from-0.4.x-to-0.5.0.md
+++ b/migrations/migrate-from-0.4.x-to-0.5.0.md
@@ -2,6 +2,85 @@
 
 The 0.5 version of Jitar introduces some breaking changes. All changes are described here, with instructions how to adopt them.
 
+## Health checks
+
+Working with health checks that need to access application resources had a few limitations.
+To overcome these, we've made breaking changes to the health check implementation.
+
+Let's look at the current implementation first. Health checks are instantiated and registered at the server.
+
+```ts
+// src/jitar.ts
+import { startServer, HealthCheck } from 'jitar';
+
+class DatabaseHealthCheck implements HealthCheck
+{
+    get timeout() { return undefined; }
+
+    async isHealthy(): Promise<boolean> { /* ... */ }
+}
+
+const server = await startServer(moduleImporter);
+server.addHealthCheck('database', new DatabaseHealthCheck()); // Registered here
+server.start();
+```
+
+Next, they are activated by name in the service configuration like this:
+
+```json
+{
+    "url": "http://127.0.0.1:3000",
+    "healthChecks": ["database"],
+    "standalone": {}
+}
+```
+
+This way, health checks can be registered once, en activated for multiple services.
+We still love that idea, but registering all health checks in a single bootstrap file wasn't pretty.
+
+So, we've changed the way of registering. There's no need for registering health checks at the server anymore.
+
+```ts
+// src/jitar.ts
+import { startServer, HealthCheck } from 'jitar';
+
+const server = await startServer(moduleImporter);
+server.start(); // Look ma, no health checks!
+```
+
+Instead, health checks are registered (and activated) in the service configuration.
+This is done by importing modules containing a health check instance.
+
+```json
+{
+    "url": "http://127.0.0.1:3000",
+    "healthChecks": ["./databaseHealthCheck"],
+    "standalone": {}
+}
+```
+
+The health check module must export an instance of the `HealthCheck` interface as default value.
+Because the health check's isn't registered by name in the configuration, a name property has been added to the `HealthCheck` interface. An example health check instance module looks like this.
+
+```ts
+// src/databaseHealthCheck.ts
+class DatabaseHealthCheck implements HealthCheck
+{
+    get name() { return 'database'; } // New required property!
+
+    get timeout() { return undefined; }
+    
+    async isHealthy(): Promise<boolean> { /* ... */ }
+}
+
+const instance = new DatabaseHealthCheck();
+export default instance;
+```
+
+In this example the health check class and instance are separated. This is good practice to enable reusability of the health checks. For simple cases, the class and instance can be defined in the same module file.
+
+More information on health checks can be found in the [documentation](https://docs.jitar.dev/deploy/health-checks.html).
+
 ## Middleware
 
 We've updated our middleware model to be more clean and extendable.
diff --git a/packages/runtime/src/errors/InvalidHealthCheck.ts b/packages/runtime/src/errors/InvalidHealthCheck.ts
new file mode 100644
index 0000000..36076bd
--- /dev/null
+++ b/packages/runtime/src/errors/InvalidHealthCheck.ts
@@ -0,0 +1,20 @@
+
+import { Loadable } from '@jitar/serialization';
+
+import ServerError from './generic/ServerError.js';
+
+export default class InvalidHealthCheck extends ServerError
+{
+    #url: string;
+
+    constructor(url: string)
+    {
+        super(`Module '${url}' does not export a valid health check`);
+
+        this.#url = url;
+    }
+
+    get url() { return this.#url; }
+}
+
+(InvalidHealthCheck as Loadable).source = 'RUNTIME_ERROR_LOCATION';
diff --git a/packages/runtime/src/interfaces/HealthCheck.ts b/packages/runtime/src/interfaces/HealthCheck.ts
index 0181e5b..b59c67b 100644
--- a/packages/runtime/src/interfaces/HealthCheck.ts
+++ b/packages/runtime/src/interfaces/HealthCheck.ts
@@ -1,6 +1,8 @@
 
 interface HealthCheck
 {
+    get name(): string;
+    
     get timeout(): number | undefined;
 
     isHealthy(): Promise<boolean>;
diff --git a/packages/runtime/src/services/Runtime.ts b/packages/runtime/src/services/Runtime.ts
index 5220636..20cf590 100644
--- a/packages/runtime/src/services/Runtime.ts
+++ b/packages/runtime/src/services/Runtime.ts
@@ -1,4 +1,5 @@
 
+import InvalidHealthCheck from '../errors/InvalidHealthCheck.js';
 import HealthCheck from '../interfaces/HealthCheck.js';
 import Module from '../types/Module.js';
 import ModuleLoader from '../utils/ModuleLoader.js';
@@ -20,9 +21,22 @@ export default abstract class Runtime
         return ModuleLoader.load(url);
     }
 
-    addHealthCheck(name: string, healthCheck: HealthCheck): void
+    async importHealthCheck(url: string): Promise<void>
     {
-        this.#healthChecks.set(name, healthCheck);
+        const module = await this.import(url);
+        const healthCheck = module.default as HealthCheck;
+
+        if (healthCheck?.isHealthy === undefined)
+        {
+            throw new InvalidHealthCheck(url);
+        }
+
+        this.addHealthCheck(healthCheck as HealthCheck);
+    }
+
+    addHealthCheck(healthCheck: HealthCheck): void
+    {
+        this.#healthChecks.set(healthCheck.name, healthCheck);
     }
 
     async isHealthy(): Promise<boolean>
diff --git a/packages/server-nodejs/src/JitarServer.ts b/packages/server-nodejs/src/JitarServer.ts
index 617d31a..61703dd 100644
--- a/packages/server-nodejs/src/JitarServer.ts
+++ b/packages/server-nodejs/src/JitarServer.ts
@@ -3,7 +3,7 @@ import express, { Express } from 'express';
 import { Server } from 'http';
 import { Logger } from 'tslog';
 
-import { HealthCheck, LocalGateway, LocalNode, LocalRepository, Middleware, ProcedureRuntime, Proxy, Runtime, RemoteClassLoader } from '@jitar/runtime';
+import { LocalGateway, LocalNode, LocalRepository, Middleware, ProcedureRuntime, Proxy, Runtime, RemoteClassLoader } from '@jitar/runtime';
 import { ClassLoader, Serializer, SerializerBuilder, ValueSerializer } from '@jitar/serialization';
 
 import ServerOptions from './configuration/ServerOptions.js';
@@ -24,8 +24,6 @@ import RPCController from './controllers/RPCController.js';
 import RuntimeConfiguration from './configuration/RuntimeConfiguration.js';
 import RuntimeDefaults from './definitions/RuntimeDefaults.js';
 
-import UnknownHealthCheck from './errors/UnknownHealthCheck.js';
-import DuplicateHealthCheck from './errors/DuplicateHealthCheck.js';
 import RuntimeNotAvailable from './errors/RuntimeNotAvailable.js';
 import MiddlewareNotSupported from './errors/MiddlewareNotSupported.js';
 import LogBuilder from './utils/LogBuilder.js';
@@ -53,8 +51,6 @@ export default class JitarServer
     #configuration: RuntimeConfiguration;
     #logger: Logger<unknown>;
 
-    #registeredHealthChecks: Map<string, HealthCheck> = new Map();
-
     constructor()
     {
         this.#classLoader = new RemoteClassLoader();
@@ -89,8 +85,6 @@ export default class JitarServer
     {
         const url = new URL(this.#configuration.url ?? RuntimeDefaults.URL);
 
-        this.#addHealthChecks();
-
         await this.#startApplication();
         await this.#startServer(url.port);
 
@@ -105,16 +99,6 @@ export default class JitarServer
         this.#logger.info('Server stopped');
     }
 
-    registerHealthCheck(name: string, healthCheck: HealthCheck): void
-    {
-        if (this.#registeredHealthChecks.has(name))
-        {
-            throw new DuplicateHealthCheck(name);
-        }
-
-        this.#registeredHealthChecks.set(name, healthCheck);
-    }
-
     addSerializer(serializer: ValueSerializer): void
     {
         this.#serializer.addSerializer(serializer);
@@ -132,28 +116,6 @@ export default class JitarServer
         runtime.addMiddleware(middleware);
     }
 
-    #addHealthChecks(): void
-    {
-        const runtime = this.#getRuntime();
-
-        if (this.#configuration.healthChecks === undefined)
-        {
-            return;
-        }
-
-        for (const name of this.#configuration.healthChecks)
-        {
-            const healthCheck = this.#registeredHealthChecks.get(name);
-
-            if (healthCheck === undefined)
-            {
-                throw new UnknownHealthCheck(name);
-            }
-
-            runtime.addHealthCheck(name, healthCheck);
-        }
-    }
-
     #getRuntime(): Runtime
     {
         if (this.#runtime === undefined)
diff --git a/packages/server-nodejs/src/errors/DuplicateHealthCheck.ts b/packages/server-nodejs/src/errors/DuplicateHealthCheck.ts
deleted file mode 100644
index f9d45a1..0000000
--- a/packages/server-nodejs/src/errors/DuplicateHealthCheck.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-
-export default class DuplicateHealthCheck extends Error
-{
-    constructor(name: string)
-    {
-        super(`Health check '${name}' is already registered.`);
-    }
-}
diff --git a/packages/server-nodejs/src/errors/UnknownHealthCheck.ts b/packages/server-nodejs/src/errors/UnknownHealthCheck.ts
deleted file mode 100644
index 74971d2..0000000
--- a/packages/server-nodejs/src/errors/UnknownHealthCheck.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-
-export default class UnknownHealthCheck extends Error
-{
-    constructor(name: string)
-    {
-        super(`Health check '${name}' is not registered.`);
-    }
-}
diff --git a/packages/server-nodejs/src/utils/RuntimeConfigurator.ts b/packages/server-nodejs/src/utils/RuntimeConfigurator.ts
index bbcc246..1bc45d2 100644
--- a/packages/server-nodejs/src/utils/RuntimeConfigurator.ts
+++ b/packages/server-nodejs/src/utils/RuntimeConfigurator.ts
@@ -18,6 +18,15 @@ import UnknownRuntimeMode from '../errors/UnknownRuntimeMode.js';
 export default class RuntimeConfigurator
 {
     static async configure(configuration: RuntimeConfiguration): Promise<Runtime>
+    {
+        const runtime = await this.configureService(configuration);
+
+        await this.#addHealthChecks(runtime, configuration);
+
+        return runtime;
+    }
+
+    static async configureService(configuration: RuntimeConfiguration): Promise<Runtime>
     {
         const url = configuration.url ?? RuntimeDefaults.URL;
 
@@ -197,4 +206,17 @@ export default class RuntimeConfigurator
             ? new RemoteGateway(url)
             : undefined;
     }
+
+    static async #addHealthChecks(runtime: Runtime, configuration: RuntimeConfiguration): Promise<void>
+    {
+        if (configuration.healthChecks === undefined)
+        {
+            return;
+        }
+
+        for (const url of configuration.healthChecks)
+        {
+            await runtime.importHealthCheck(url);
+        }
+    }
 }


Test patch:
diff --git a/packages/runtime/test/_fixtures/interfaces/HealthCheck.fixture.ts b/packages/runtime/test/_fixtures/interfaces/HealthCheck.fixture.ts
index 76cf30b..c438fb1 100644
--- a/packages/runtime/test/_fixtures/interfaces/HealthCheck.fixture.ts
+++ b/packages/runtime/test/_fixtures/interfaces/HealthCheck.fixture.ts
@@ -3,6 +3,8 @@ import HealthCheck from '../../../src/interfaces/HealthCheck';
 
 class HealthyCheck implements HealthCheck
 {
+    get name() { return 'good'; }
+
     get timeout() { return undefined; }
 
     async isHealthy(): Promise<boolean> { return true; }
@@ -10,6 +12,8 @@ class HealthyCheck implements HealthCheck
 
 class UnhealthyCheck implements HealthCheck
 {
+    get name() { return 'bad'; }
+
     get timeout() { return undefined; }
 
     async isHealthy(): Promise<boolean> { return false; }
@@ -17,6 +21,8 @@ class UnhealthyCheck implements HealthCheck
 
 class ErrorHealthCheck implements HealthCheck
 {
+    get name() { return 'error'; }
+
     get timeout() { return undefined; }
 
     async isHealthy(): Promise<boolean> { throw new Error('ErrorHealthCheck'); }
@@ -24,6 +30,8 @@ class ErrorHealthCheck implements HealthCheck
 
 class TimedOutHealthCheck implements HealthCheck
 {
+    get name() { return 'timedOut'; }
+
     get timeout() { return 50; }
 
     async isHealthy(): Promise<boolean>
@@ -37,6 +45,8 @@ class TimedOutHealthCheck implements HealthCheck
 
 class InTimeHealthCheck implements HealthCheck
 {
+    get name() { return 'inTime'; }
+    
     get timeout() { return 5000; }
 
     async isHealthy(): Promise<boolean>
diff --git a/packages/runtime/test/_fixtures/services/LocalNode.fixture.ts b/packages/runtime/test/_fixtures/services/LocalNode.fixture.ts
index 59f72df..255e620 100644
--- a/packages/runtime/test/_fixtures/services/LocalNode.fixture.ts
+++ b/packages/runtime/test/_fixtures/services/LocalNode.fixture.ts
@@ -17,10 +17,10 @@ const secondNode = new LocalNode();
 secondNode.addSegment(SEGMENTS.SECOND);
 
 const goodNode = new LocalNode();
-goodNode.addHealthCheck('good', HEALTH_CHECKS.GOOD);
+goodNode.addHealthCheck(HEALTH_CHECKS.GOOD);
 
 const badNode = new LocalNode();
-badNode.addHealthCheck('bad', HEALTH_CHECKS.BAD);
+badNode.addHealthCheck(HEALTH_CHECKS.BAD);
 
 const NODES =
 {
diff --git a/packages/runtime/test/_fixtures/services/Runtime.fixture.ts b/packages/runtime/test/_fixtures/services/Runtime.fixture.ts
index 8321443..6966dd5 100644
--- a/packages/runtime/test/_fixtures/services/Runtime.fixture.ts
+++ b/packages/runtime/test/_fixtures/services/Runtime.fixture.ts
@@ -8,19 +8,19 @@ class TestRuntime extends Runtime
 }
 
 const goodRuntime = new TestRuntime();
-goodRuntime.addHealthCheck('good', HEALTH_CHECKS.GOOD);
+goodRuntime.addHealthCheck(HEALTH_CHECKS.GOOD);
 
 const badRuntime = new TestRuntime();
-badRuntime.addHealthCheck('bad', HEALTH_CHECKS.BAD);
+badRuntime.addHealthCheck(HEALTH_CHECKS.BAD);
 
 const errorRuntime = new TestRuntime();
-errorRuntime.addHealthCheck('error', HEALTH_CHECKS.ERROR);
+errorRuntime.addHealthCheck(HEALTH_CHECKS.ERROR);
 
 const timeoutRuntime = new TestRuntime();
-timeoutRuntime.addHealthCheck('timedOut', HEALTH_CHECKS.TIMEDOUT);
+timeoutRuntime.addHealthCheck(HEALTH_CHECKS.TIMEDOUT);
 
 const inTimeRuntime = new TestRuntime();
-inTimeRuntime.addHealthCheck('inTime', HEALTH_CHECKS.INTIME);
+inTimeRuntime.addHealthCheck(HEALTH_CHECKS.INTIME);
 
 const RUNTIMES =
 {