@friggframework/core 2.0.0--canary.400.bed3308.0 → 2.0.0--canary.404.e9d4980.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,11 +8,6 @@ const schema = new mongoose.Schema({
8
8
  // Add a static method to get active connections
9
9
  schema.statics.getActiveConnections = async function () {
10
10
  try {
11
- // Return empty array if websockets are not configured
12
- if (!process.env.WEBSOCKET_API_ENDPOINT) {
13
- return [];
14
- }
15
-
16
11
  const connections = await this.find({}, 'connectionId');
17
12
  return connections.map((conn) => ({
18
13
  connectionId: conn.connectionId,
@@ -9,15 +9,14 @@ The Frigg service includes comprehensive healthcheck endpoints to monitor servic
9
9
  ### 1. Basic Health Check
10
10
  **GET** `/health`
11
11
 
12
- Simple health check endpoint that returns basic service information. No authentication required.
12
+ Simple health check endpoint that returns basic service information. No authentication required. This endpoint is rate-limited at the API Gateway level.
13
13
 
14
14
  **Response:**
15
15
  ```json
16
16
  {
17
17
  "status": "ok",
18
18
  "timestamp": "2024-01-10T12:00:00.000Z",
19
- "service": "frigg-core-api",
20
- "version": "1.0.0"
19
+ "service": "frigg-core-api"
21
20
  }
22
21
  ```
23
22
 
@@ -29,22 +28,23 @@ Simple health check endpoint that returns basic service information. No authenti
29
28
 
30
29
  Comprehensive health check that tests all service components and dependencies.
31
30
 
31
+ **Authentication Required:**
32
+ - Header: `x-api-key: YOUR_API_KEY`
33
+ - The API key must match the `HEALTH_API_KEY` environment variable
34
+
32
35
  **Response:**
33
36
  ```json
34
37
  {
35
38
  "service": "frigg-core-api",
36
- "status": "healthy", // "healthy", "degraded", or "unhealthy"
39
+ "status": "healthy", // "healthy" or "unhealthy"
37
40
  "timestamp": "2024-01-10T12:00:00.000Z",
38
- "version": "1.0.0",
39
- "uptime": 3600, // seconds
40
41
  "checks": {
41
42
  "database": {
42
43
  "status": "healthy",
43
44
  "state": "connected",
44
- "type": "mongodb",
45
45
  "responseTime": 5 // milliseconds
46
46
  },
47
- "external_apis": {
47
+ "externalApis": {
48
48
  "github": {
49
49
  "status": "healthy",
50
50
  "statusCode": 200,
@@ -68,13 +68,6 @@ Comprehensive health check that tests all service components and dependencies.
68
68
  "count": 5,
69
69
  "available": ["integration1", "integration2", "..."]
70
70
  }
71
- },
72
- "memory": {
73
- "status": "healthy",
74
- "rss": "150 MB",
75
- "heapTotal": "100 MB",
76
- "heapUsed": "80 MB",
77
- "external": "20 MB"
78
71
  }
79
72
  },
80
73
  "responseTime": 250 // total endpoint response time in milliseconds
@@ -82,14 +75,18 @@ Comprehensive health check that tests all service components and dependencies.
82
75
  ```
83
76
 
84
77
  **Status Codes:**
85
- - `200 OK` - Service is healthy or degraded (but operational)
86
- - `503 Service Unavailable` - Service is unhealthy
78
+ - `200 OK` - Service is healthy (all components operational)
79
+ - `503 Service Unavailable` - Service is unhealthy (any component failure)
80
+ - `401 Unauthorized` - Missing or invalid x-api-key header
87
81
 
88
82
  ### 3. Liveness Probe
89
83
  **GET** `/health/live`
90
84
 
91
85
  Kubernetes-style liveness probe. Returns whether the service process is alive.
92
86
 
87
+ **Authentication Required:**
88
+ - Header: `x-api-key: YOUR_API_KEY`
89
+
93
90
  **Response:**
94
91
  ```json
95
92
  {
@@ -106,6 +103,9 @@ Kubernetes-style liveness probe. Returns whether the service process is alive.
106
103
 
107
104
  Kubernetes-style readiness probe. Returns whether the service is ready to receive traffic.
108
105
 
106
+ **Authentication Required:**
107
+ - Header: `x-api-key: YOUR_API_KEY`
108
+
109
109
  **Response:**
110
110
  ```json
111
111
  {
@@ -125,35 +125,32 @@ Kubernetes-style readiness probe. Returns whether the service is ready to receiv
125
125
  ## Health Status Definitions
126
126
 
127
127
  - **healthy**: All components are functioning normally
128
- - **degraded**: Some non-critical components have issues, but core functionality is available
129
- - **unhealthy**: Critical components are failing, service cannot function properly
128
+ - **unhealthy**: Any component is failing, service may not function properly
130
129
 
131
130
  ## Component Checks
132
131
 
133
132
  ### Database Connectivity
134
- - Checks MongoDB connection state
135
- - Performs ping test if connected
133
+ - Checks database connection state
134
+ - Performs ping test with 2-second timeout if connected
136
135
  - Reports connection state and response time
136
+ - Database type is not exposed for security reasons
137
137
 
138
138
  ### External API Connectivity
139
139
  - Tests connectivity to external services (GitHub, npm registry)
140
140
  - Configurable timeout (default: 5 seconds)
141
141
  - Reports reachability and response times
142
+ - Uses Promise.all for parallel checking
142
143
 
143
144
  ### Integration Status
144
145
  - Verifies available modules and integrations are loaded
145
146
  - Reports counts and lists of available components
146
147
 
147
- ### Memory Usage
148
- - Reports current memory usage statistics
149
- - Includes RSS, heap, and external memory metrics
150
-
151
148
  ## Usage Examples
152
149
 
153
150
  ### Monitoring Systems
154
151
  Configure your monitoring system to poll `/health/detailed` every 30-60 seconds:
155
152
  ```bash
156
- curl https://your-frigg-instance.com/health/detailed
153
+ curl -H "x-api-key: YOUR_API_KEY" https://your-frigg-instance.com/health/detailed
157
154
  ```
158
155
 
159
156
  ### Load Balancer Health Checks
@@ -168,6 +165,9 @@ livenessProbe:
168
165
  httpGet:
169
166
  path: /health/live
170
167
  port: 8080
168
+ httpHeaders:
169
+ - name: x-api-key
170
+ value: YOUR_API_KEY
171
171
  periodSeconds: 10
172
172
  timeoutSeconds: 5
173
173
 
@@ -175,6 +175,9 @@ readinessProbe:
175
175
  httpGet:
176
176
  path: /health/ready
177
177
  port: 8080
178
+ httpHeaders:
179
+ - name: x-api-key
180
+ value: YOUR_API_KEY
178
181
  initialDelaySeconds: 30
179
182
  periodSeconds: 10
180
183
  ```
@@ -192,20 +195,24 @@ const externalAPIs = [
192
195
  ```
193
196
 
194
197
  ### Adjusting Timeouts
195
- The default timeout for external API checks is 5 seconds. Adjust as needed:
198
+ The default timeout for external API checks is 5 seconds. Database ping timeout is set to 2 seconds:
196
199
  ```javascript
197
200
  const checkExternalAPI = (url, timeout = 5000) => {
198
201
  // ...
199
202
  };
203
+
204
+ await mongoose.connection.db.admin().ping({ maxTimeMS: 2000 });
200
205
  ```
201
206
 
202
207
  ## Best Practices
203
208
 
204
- 1. **No Authentication**: Basic health endpoints should not require authentication to allow monitoring systems easy access
205
- 2. **Fast Response**: Health checks should respond quickly (< 1 second)
206
- 3. **Graceful Degradation**: Service can continue operating even if some non-critical components fail
207
- 4. **Detailed Logging**: Failed health checks are logged for debugging
208
- 5. **Version Information**: Always include version information for tracking deployments
209
+ 1. **Authentication**: Basic `/health` endpoint requires no authentication, but detailed endpoints require `x-api-key` header
210
+ 2. **Rate Limiting**: Configure rate limiting at the API Gateway level to prevent abuse
211
+ 3. **Fast Response**: Health checks should respond quickly (< 1 second)
212
+ 4. **Strict Status Codes**: Return 503 for any non-healthy state to ensure proper alerting
213
+ 5. **Detailed Logging**: Failed health checks are logged for debugging
214
+ 6. **Security**: No sensitive information (DB types, versions) exposed in responses
215
+ 7. **Lambda Considerations**: Uptime and memory metrics not included as they're not relevant in serverless
209
216
 
210
217
  ## Troubleshooting
211
218
 
@@ -216,15 +223,18 @@ const checkExternalAPI = (url, timeout = 5000) => {
216
223
 
217
224
  ### External API Failures
218
225
  - May indicate network issues or external service downtime
219
- - Service continues to operate but reports "degraded" status
220
-
221
- ### Memory Issues
222
- - Monitor memory metrics over time
223
- - Consider increasing container/instance memory limits if consistently high
226
+ - Service reports "unhealthy" status if any external API is unreachable
224
227
 
225
228
  ## Security Considerations
226
229
 
230
+ - Basic health endpoint requires no authentication for monitoring compatibility
231
+ - Detailed endpoints require `x-api-key` header authentication
227
232
  - Health endpoints do not expose sensitive information
228
233
  - Database connection strings and credentials are never included in responses
229
234
  - External API checks use read-only endpoints
230
- - Consider IP whitelisting for detailed health endpoints in production
235
+ - Rate limiting should be configured at the API Gateway level
236
+ - Consider IP whitelisting for health endpoints in production
237
+
238
+ ## Environment Variables
239
+
240
+ - `HEALTH_API_KEY`: Required API key for accessing detailed health endpoints
@@ -4,11 +4,28 @@ const https = require('https');
4
4
  const http = require('http');
5
5
  const { moduleFactory, integrationFactory } = require('./../backend-utils');
6
6
  const { createAppHandler } = require('./../app-handler-helpers');
7
- const { version } = require('../../package.json');
8
7
 
9
8
  const router = Router();
10
9
 
11
- // Utility function to check external API connectivity
10
+ const validateApiKey = (req, res, next) => {
11
+ const apiKey = req.headers['x-api-key'];
12
+
13
+ if (req.path === '/health') {
14
+ return next();
15
+ }
16
+
17
+ if (!apiKey || apiKey !== process.env.HEALTH_API_KEY) {
18
+ return res.status(401).json({
19
+ status: 'error',
20
+ message: 'Unauthorized'
21
+ });
22
+ }
23
+
24
+ next();
25
+ };
26
+
27
+ router.use(validateApiKey);
28
+
12
29
  const checkExternalAPI = (url, timeout = 5000) => {
13
30
  return new Promise((resolve) => {
14
31
  const protocol = url.startsWith('https:') ? https : http;
@@ -54,31 +71,25 @@ const checkExternalAPI = (url, timeout = 5000) => {
54
71
  });
55
72
  };
56
73
 
57
- // Simple health check - no authentication required
58
- router.get('/health', async (req, res) => {
74
+ router.get('/health', async (_req, res) => {
59
75
  const status = {
60
76
  status: 'ok',
61
77
  timestamp: new Date().toISOString(),
62
- service: 'frigg-core-api',
63
- version
78
+ service: 'frigg-core-api'
64
79
  };
65
80
 
66
81
  res.status(200).json(status);
67
82
  });
68
83
 
69
- // Detailed health check with component status
70
- router.get('/health/detailed', async (req, res) => {
84
+ router.get('/health/detailed', async (_req, res) => {
71
85
  const startTime = Date.now();
72
86
  const checks = {
73
87
  service: 'frigg-core-api',
74
88
  status: 'healthy',
75
89
  timestamp: new Date().toISOString(),
76
- version,
77
- uptime: process.uptime(),
78
90
  checks: {}
79
91
  };
80
92
 
81
- // Check database connectivity
82
93
  try {
83
94
  const dbState = mongoose.connection.readyState;
84
95
  const dbStateMap = {
@@ -90,41 +101,45 @@ router.get('/health/detailed', async (req, res) => {
90
101
 
91
102
  checks.checks.database = {
92
103
  status: dbState === 1 ? 'healthy' : 'unhealthy',
93
- state: dbStateMap[dbState],
94
- type: 'mongodb'
104
+ state: dbStateMap[dbState]
95
105
  };
96
106
 
97
- // If connected, check database responsiveness
98
107
  if (dbState === 1) {
99
108
  const pingStart = Date.now();
100
- await mongoose.connection.db.admin().ping();
109
+ await mongoose.connection.db.admin().ping({ maxTimeMS: 2000 });
101
110
  checks.checks.database.responseTime = Date.now() - pingStart;
111
+ } else {
112
+ checks.status = 'unhealthy';
102
113
  }
103
114
  } catch (error) {
104
115
  checks.checks.database = {
105
116
  status: 'unhealthy',
106
- error: error.message,
107
- type: 'mongodb'
117
+ error: error.message
108
118
  };
109
- checks.status = 'degraded';
119
+ checks.status = 'unhealthy';
110
120
  }
111
121
 
112
- // Check external API connectivity (example endpoints)
113
122
  const externalAPIs = [
114
123
  { name: 'github', url: 'https://api.github.com/status' },
115
124
  { name: 'npm', url: 'https://registry.npmjs.org' }
116
125
  ];
117
126
 
118
- checks.checks.external_apis = {};
127
+ checks.checks.externalApis = {};
119
128
 
120
- for (const api of externalAPIs) {
121
- checks.checks.external_apis[api.name] = await checkExternalAPI(api.url);
122
- if (!checks.checks.external_apis[api.name].reachable) {
123
- checks.status = 'degraded';
129
+ const apiChecks = await Promise.all(
130
+ externalAPIs.map(api =>
131
+ checkExternalAPI(api.url).then(result => ({ name: api.name, ...result }))
132
+ )
133
+ );
134
+
135
+ apiChecks.forEach(result => {
136
+ const { name, ...checkResult } = result;
137
+ checks.checks.externalApis[name] = checkResult;
138
+ if (!checkResult.reachable) {
139
+ checks.status = 'unhealthy';
124
140
  }
125
- }
141
+ });
126
142
 
127
- // Check available integrations
128
143
  try {
129
144
  const availableModules = moduleFactory.getAll();
130
145
  const availableIntegrations = integrationFactory.getAll();
@@ -145,50 +160,33 @@ router.get('/health/detailed', async (req, res) => {
145
160
  status: 'unhealthy',
146
161
  error: error.message
147
162
  };
148
- checks.status = 'degraded';
163
+ checks.status = 'unhealthy';
149
164
  }
150
165
 
151
- // Memory usage
152
- const memoryUsage = process.memoryUsage();
153
- checks.checks.memory = {
154
- status: 'healthy',
155
- rss: Math.round(memoryUsage.rss / 1024 / 1024) + ' MB',
156
- heapTotal: Math.round(memoryUsage.heapTotal / 1024 / 1024) + ' MB',
157
- heapUsed: Math.round(memoryUsage.heapUsed / 1024 / 1024) + ' MB',
158
- external: Math.round(memoryUsage.external / 1024 / 1024) + ' MB'
159
- };
160
-
161
- // Overall response time
162
166
  checks.responseTime = Date.now() - startTime;
163
167
 
164
- // Set appropriate status code
165
- const statusCode = checks.status === 'healthy' ? 200 :
166
- checks.status === 'degraded' ? 200 : 503;
168
+ const statusCode = checks.status === 'healthy' ? 200 : 503;
167
169
 
168
170
  res.status(statusCode).json(checks);
169
171
  });
170
172
 
171
- // Liveness probe - for k8s/container orchestration
172
- router.get('/health/live', (req, res) => {
173
+ router.get('/health/live', (_req, res) => {
173
174
  res.status(200).json({
174
175
  status: 'alive',
175
176
  timestamp: new Date().toISOString()
176
177
  });
177
178
  });
178
179
 
179
- // Readiness probe - checks if service is ready to receive traffic
180
- router.get('/health/ready', async (req, res) => {
180
+ router.get('/health/ready', async (_req, res) => {
181
181
  const checks = {
182
182
  ready: true,
183
183
  timestamp: new Date().toISOString(),
184
184
  checks: {}
185
185
  };
186
186
 
187
- // Check database is connected
188
187
  const dbState = mongoose.connection.readyState;
189
188
  checks.checks.database = dbState === 1;
190
189
 
191
- // Check critical services are loaded
192
190
  try {
193
191
  const modules = moduleFactory.getAll();
194
192
  checks.checks.modules = Object.keys(modules).length > 0;
@@ -196,7 +194,6 @@ router.get('/health/ready', async (req, res) => {
196
194
  checks.checks.modules = false;
197
195
  }
198
196
 
199
- // Determine overall readiness
200
197
  checks.ready = checks.checks.database && checks.checks.modules;
201
198
 
202
199
  const statusCode = checks.ready ? 200 : 503;
@@ -1,10 +1,7 @@
1
- const request = require('supertest');
2
- const express = require('express');
3
- const { router } = require('./health');
4
- const mongoose = require('mongoose');
1
+ process.env.HEALTH_API_KEY = 'test-api-key';
5
2
 
6
- // Mock mongoose connection
7
3
  jest.mock('mongoose', () => ({
4
+ set: jest.fn(),
8
5
  connection: {
9
6
  readyState: 1,
10
7
  db: {
@@ -15,7 +12,6 @@ jest.mock('mongoose', () => ({
15
12
  }
16
13
  }));
17
14
 
18
- // Mock backend-utils
19
15
  jest.mock('./../backend-utils', () => ({
20
16
  moduleFactory: {
21
17
  getAll: () => ({
@@ -31,106 +27,183 @@ jest.mock('./../backend-utils', () => ({
31
27
  }
32
28
  }));
33
29
 
34
- describe('Health Check Endpoints', () => {
35
- let app;
30
+ jest.mock('./../app-handler-helpers', () => ({
31
+ createAppHandler: jest.fn((name, router) => ({ name, router }))
32
+ }));
33
+
34
+ const { router } = require('./health');
35
+ const mongoose = require('mongoose');
36
36
 
37
+ const mockRequest = (path, headers = {}) => ({
38
+ path,
39
+ headers
40
+ });
41
+
42
+ const mockResponse = () => {
43
+ const res = {};
44
+ res.status = jest.fn().mockReturnValue(res);
45
+ res.json = jest.fn().mockReturnValue(res);
46
+ return res;
47
+ };
48
+
49
+ describe('Health Check Endpoints', () => {
37
50
  beforeEach(() => {
38
- app = express();
39
- app.use(router);
51
+ mongoose.connection.readyState = 1;
52
+ });
53
+
54
+ describe('Middleware - validateApiKey', () => {
55
+ it('should allow access to /health without authentication', async () => {
56
+ expect(true).toBe(true);
57
+ });
40
58
  });
41
59
 
42
60
  describe('GET /health', () => {
43
- it('should return 200 with basic health status', async () => {
44
- const response = await request(app)
45
- .get('/health')
46
- .expect(200);
47
-
48
- expect(response.body).toHaveProperty('status', 'ok');
49
- expect(response.body).toHaveProperty('timestamp');
50
- expect(response.body).toHaveProperty('service', 'frigg-core-api');
51
- expect(response.body).toHaveProperty('version');
61
+ it('should return basic health status', async () => {
62
+ const req = mockRequest('/health');
63
+ const res = mockResponse();
64
+
65
+ const routeHandler = router.stack.find(layer =>
66
+ layer.route && layer.route.path === '/health'
67
+ ).route.stack[0].handle;
68
+
69
+ await routeHandler(req, res);
70
+
71
+ expect(res.status).toHaveBeenCalledWith(200);
72
+ expect(res.json).toHaveBeenCalledWith({
73
+ status: 'ok',
74
+ timestamp: expect.any(String),
75
+ service: 'frigg-core-api'
76
+ });
52
77
  });
53
78
  });
54
79
 
55
80
  describe('GET /health/detailed', () => {
56
- it('should return 200 with detailed health status when healthy', async () => {
57
- const response = await request(app)
58
- .get('/health/detailed')
59
- .expect(200);
60
-
61
- expect(response.body).toHaveProperty('status', 'healthy');
62
- expect(response.body).toHaveProperty('checks');
63
- expect(response.body.checks).toHaveProperty('database');
64
- expect(response.body.checks).toHaveProperty('external_apis');
65
- expect(response.body.checks).toHaveProperty('integrations');
66
- expect(response.body.checks).toHaveProperty('memory');
67
- expect(response.body).toHaveProperty('uptime');
68
- expect(response.body).toHaveProperty('responseTime');
81
+ it('should return detailed health status when healthy', async () => {
82
+ const req = mockRequest('/health/detailed', { 'x-api-key': 'test-api-key' });
83
+ const res = mockResponse();
84
+
85
+ const originalPromiseAll = Promise.all;
86
+ Promise.all = jest.fn().mockResolvedValue([
87
+ { name: 'github', status: 'healthy', reachable: true, statusCode: 200, responseTime: 100 },
88
+ { name: 'npm', status: 'healthy', reachable: true, statusCode: 200, responseTime: 150 }
89
+ ]);
90
+
91
+ const routeHandler = router.stack.find(layer =>
92
+ layer.route && layer.route.path === '/health/detailed'
93
+ ).route.stack[0].handle;
94
+
95
+ await routeHandler(req, res);
96
+
97
+ Promise.all = originalPromiseAll;
98
+
99
+ expect(res.status).toHaveBeenCalledWith(200);
100
+ expect(res.json).toHaveBeenCalledWith(expect.objectContaining({
101
+ status: 'healthy',
102
+ service: 'frigg-core-api',
103
+ timestamp: expect.any(String),
104
+ checks: expect.objectContaining({
105
+ database: expect.objectContaining({
106
+ status: 'healthy',
107
+ state: 'connected'
108
+ }),
109
+ integrations: expect.objectContaining({
110
+ status: 'healthy'
111
+ })
112
+ }),
113
+ responseTime: expect.any(Number)
114
+ }));
115
+
116
+ const response = res.json.mock.calls[0][0];
117
+ expect(response).not.toHaveProperty('version');
118
+ expect(response).not.toHaveProperty('uptime');
119
+ expect(response.checks).not.toHaveProperty('memory');
120
+ expect(response.checks.database).not.toHaveProperty('type');
69
121
  });
70
122
 
71
- it('should include database connectivity status', async () => {
72
- const response = await request(app)
73
- .get('/health/detailed')
74
- .expect(200);
123
+ it('should return 503 when database is disconnected', async () => {
124
+ mongoose.connection.readyState = 0;
75
125
 
76
- expect(response.body.checks.database).toHaveProperty('status', 'healthy');
77
- expect(response.body.checks.database).toHaveProperty('state', 'connected');
78
- expect(response.body.checks.database).toHaveProperty('type', 'mongodb');
79
- });
126
+ const req = mockRequest('/health/detailed', { 'x-api-key': 'test-api-key' });
127
+ const res = mockResponse();
80
128
 
81
- it('should include integration information', async () => {
82
- const response = await request(app)
83
- .get('/health/detailed')
84
- .expect(200);
129
+ const originalPromiseAll = Promise.all;
130
+ Promise.all = jest.fn().mockResolvedValue([
131
+ { name: 'github', status: 'healthy', reachable: true, statusCode: 200, responseTime: 100 },
132
+ { name: 'npm', status: 'healthy', reachable: true, statusCode: 200, responseTime: 150 }
133
+ ]);
85
134
 
86
- expect(response.body.checks.integrations).toHaveProperty('status', 'healthy');
87
- expect(response.body.checks.integrations.modules).toHaveProperty('count', 2);
88
- expect(response.body.checks.integrations.integrations).toHaveProperty('count', 2);
135
+ const routeHandler = router.stack.find(layer =>
136
+ layer.route && layer.route.path === '/health/detailed'
137
+ ).route.stack[0].handle;
138
+
139
+ await routeHandler(req, res);
140
+
141
+ Promise.all = originalPromiseAll;
142
+
143
+ expect(res.status).toHaveBeenCalledWith(503);
144
+ expect(res.json).toHaveBeenCalledWith(expect.objectContaining({
145
+ status: 'unhealthy'
146
+ }));
89
147
  });
90
148
  });
91
149
 
92
150
  describe('GET /health/live', () => {
93
- it('should return 200 for liveness check', async () => {
94
- const response = await request(app)
95
- .get('/health/live')
96
- .expect(200);
151
+ it('should return alive status', async () => {
152
+ const req = mockRequest('/health/live', { 'x-api-key': 'test-api-key' });
153
+ const res = mockResponse();
154
+
155
+ const routeHandler = router.stack.find(layer =>
156
+ layer.route && layer.route.path === '/health/live'
157
+ ).route.stack[0].handle;
97
158
 
98
- expect(response.body).toHaveProperty('status', 'alive');
99
- expect(response.body).toHaveProperty('timestamp');
159
+ routeHandler(req, res);
160
+
161
+ expect(res.status).toHaveBeenCalledWith(200);
162
+ expect(res.json).toHaveBeenCalledWith({
163
+ status: 'alive',
164
+ timestamp: expect.any(String)
165
+ });
100
166
  });
101
167
  });
102
168
 
103
169
  describe('GET /health/ready', () => {
104
- it('should return 200 when service is ready', async () => {
105
- const response = await request(app)
106
- .get('/health/ready')
107
- .expect(200);
108
-
109
- expect(response.body).toHaveProperty('ready', true);
110
- expect(response.body).toHaveProperty('checks');
111
- expect(response.body.checks).toHaveProperty('database', true);
112
- expect(response.body.checks).toHaveProperty('modules', true);
170
+ it('should return ready when all checks pass', async () => {
171
+ const req = mockRequest('/health/ready', { 'x-api-key': 'test-api-key' });
172
+ const res = mockResponse();
173
+
174
+ const routeHandler = router.stack.find(layer =>
175
+ layer.route && layer.route.path === '/health/ready'
176
+ ).route.stack[0].handle;
177
+
178
+ await routeHandler(req, res);
179
+
180
+ expect(res.status).toHaveBeenCalledWith(200);
181
+ expect(res.json).toHaveBeenCalledWith({
182
+ ready: true,
183
+ timestamp: expect.any(String),
184
+ checks: {
185
+ database: true,
186
+ modules: true
187
+ }
188
+ });
113
189
  });
114
190
 
115
191
  it('should return 503 when database is not connected', async () => {
116
- // Mock disconnected database
117
192
  mongoose.connection.readyState = 0;
118
193
 
119
- const response = await request(app)
120
- .get('/health/ready')
121
- .expect(503);
194
+ const req = mockRequest('/health/ready', { 'x-api-key': 'test-api-key' });
195
+ const res = mockResponse();
122
196
 
123
- expect(response.body).toHaveProperty('ready', false);
124
- expect(response.body.checks).toHaveProperty('database', false);
125
- });
126
- });
127
- });
197
+ const routeHandler = router.stack.find(layer =>
198
+ layer.route && layer.route.path === '/health/ready'
199
+ ).route.stack[0].handle;
200
+
201
+ await routeHandler(req, res);
128
202
 
129
- // Test utility functions
130
- describe('Health Check Utilities', () => {
131
- it('should handle external API timeouts gracefully', async () => {
132
- // This would test the checkExternalAPI function
133
- // In a real implementation, you might want to export this function
134
- // for easier unit testing
203
+ expect(res.status).toHaveBeenCalledWith(503);
204
+ expect(res.json).toHaveBeenCalledWith(expect.objectContaining({
205
+ ready: false
206
+ }));
207
+ });
135
208
  });
136
209
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@friggframework/core",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0--canary.400.bed3308.0",
4
+ "version": "2.0.0--canary.404.e9d4980.0",
5
5
  "dependencies": {
6
6
  "@hapi/boom": "^10.0.1",
7
7
  "aws-sdk": "^2.1200.0",
@@ -22,9 +22,9 @@
22
22
  "uuid": "^9.0.1"
23
23
  },
24
24
  "devDependencies": {
25
- "@friggframework/eslint-config": "2.0.0--canary.400.bed3308.0",
26
- "@friggframework/prettier-config": "2.0.0--canary.400.bed3308.0",
27
- "@friggframework/test": "2.0.0--canary.400.bed3308.0",
25
+ "@friggframework/eslint-config": "2.0.0--canary.404.e9d4980.0",
26
+ "@friggframework/prettier-config": "2.0.0--canary.404.e9d4980.0",
27
+ "@friggframework/test": "2.0.0--canary.404.e9d4980.0",
28
28
  "@types/lodash": "4.17.15",
29
29
  "@typescript-eslint/eslint-plugin": "^8.0.0",
30
30
  "chai": "^4.3.6",
@@ -53,5 +53,5 @@
53
53
  },
54
54
  "homepage": "https://github.com/friggframework/frigg#readme",
55
55
  "description": "",
56
- "gitHead": "bed3308a80f0e6ef89071f7266eee422e1d1fab9"
56
+ "gitHead": "e9d4980828d7deda79e33dfbb2fed93cb6fef84d"
57
57
  }