@futdevpro/nts-dynamo 1.15.153 → 1.15.155
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.
- package/README.md +25 -6
- package/__documentations/2026-08-22-content-free-process-error-boundary.md +91 -0
- package/build/_models/control-models/app-params.control-model.d.ts.map +1 -1
- package/build/_models/control-models/app-params.control-model.js +1 -2
- package/build/_models/control-models/app-params.control-model.js.map +1 -1
- package/build/_modules/server/index.d.ts +4 -0
- package/build/_modules/server/index.d.ts.map +1 -1
- package/build/_modules/server/index.js +5 -0
- package/build/_modules/server/index.js.map +1 -1
- package/build/_modules/server/safe-diagnostic/safe-diagnostic-cause.type-enum.d.ts +9 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic-cause.type-enum.d.ts.map +1 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic-cause.type-enum.js +13 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic-cause.type-enum.js.map +1 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic-stage.type-enum.d.ts +20 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic-stage.type-enum.d.ts.map +1 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic-stage.type-enum.js +24 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic-stage.type-enum.js.map +1 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic.control-service.d.ts +19 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic.control-service.d.ts.map +1 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic.control-service.js +83 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic.control-service.js.map +1 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic.interface.d.ts +19 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic.interface.d.ts.map +1 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic.interface.js +3 -0
- package/build/_modules/server/safe-diagnostic/safe-diagnostic.interface.js.map +1 -0
- package/build/_services/core/global.service.d.ts +3 -3
- package/build/_services/core/global.service.d.ts.map +1 -1
- package/build/_services/core/global.service.js +24 -18
- package/build/_services/core/global.service.js.map +1 -1
- package/build/_services/server/app.server.d.ts +2 -0
- package/build/_services/server/app.server.d.ts.map +1 -1
- package/build/_services/server/app.server.js +71 -199
- package/build/_services/server/app.server.js.map +1 -1
- package/build/_services/shared.static-service.d.ts +11 -4
- package/build/_services/shared.static-service.d.ts.map +1 -1
- package/build/_services/shared.static-service.js +26 -6
- package/build/_services/shared.static-service.js.map +1 -1
- package/package.json +15 -9
- package/src/_models/control-models/app-params.control-model.spec.ts +9 -5
- package/src/_models/control-models/app-params.control-model.ts +4 -3
- package/src/_modules/server/index.ts +6 -0
- package/src/_modules/server/safe-diagnostic/safe-diagnostic-cause.type-enum.ts +8 -0
- package/src/_modules/server/safe-diagnostic/safe-diagnostic-stage.type-enum.ts +19 -0
- package/src/_modules/server/safe-diagnostic/safe-diagnostic.control-service.spec.ts +124 -0
- package/src/_modules/server/safe-diagnostic/safe-diagnostic.control-service.ts +99 -0
- package/src/_modules/server/safe-diagnostic/safe-diagnostic.interface.ts +20 -0
- package/src/_modules/server/server-status/db-readiness-probe.control-service.spec.ts +2 -2
- package/src/_services/core/global.service.ts +34 -33
- package/src/_services/server/app-safe-diagnostic.spec.ts +124 -0
- package/src/_services/server/app.server.ts +209 -262
- package/src/_services/shared.static-service.spec.ts +57 -23
- package/src/_services/shared.static-service.ts +48 -19
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
|
|
3
|
+
import { DyFM_Error, DyFM_Log } from '@futdevpro/fsm-dynamo';
|
|
4
|
+
|
|
5
|
+
import { DyNTS_SafeDiagnosticCause_Type } from './safe-diagnostic-cause.type-enum';
|
|
6
|
+
import {
|
|
7
|
+
DyNTS_SafeDiagnosticInput_Interface,
|
|
8
|
+
DyNTS_SafeDiagnostic_Interface
|
|
9
|
+
} from './safe-diagnostic.interface';
|
|
10
|
+
|
|
11
|
+
/** Projects arbitrary failures to a versioned, content-free public diagnostic. */
|
|
12
|
+
export class DyNTS_SafeDiagnostic_ControlService {
|
|
13
|
+
private static readonly contractVersion: 'dynts-safe-diagnostic/1' = 'dynts-safe-diagnostic/1';
|
|
14
|
+
private static readonly defaultCode: string = 'DYNTS_SAFE_DIAGNOSTIC_INVALID_CODE';
|
|
15
|
+
private static readonly defaultStatus: number = 500;
|
|
16
|
+
|
|
17
|
+
/** Returns an immutable exact allowlist and never reflects raw failure content. */
|
|
18
|
+
static project(input: DyNTS_SafeDiagnosticInput_Interface): DyNTS_SafeDiagnostic_Interface {
|
|
19
|
+
const code: string = this.getCode(input.code);
|
|
20
|
+
const causeType: DyNTS_SafeDiagnosticCause_Type = this.getCauseType(input.error);
|
|
21
|
+
const status: number = this.getStatus(input.status);
|
|
22
|
+
const fingerprint: string = this.getFingerprint([
|
|
23
|
+
this.contractVersion,
|
|
24
|
+
code,
|
|
25
|
+
input.stage,
|
|
26
|
+
causeType,
|
|
27
|
+
status.toString(),
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
return Object.freeze({
|
|
31
|
+
contractVersion: this.contractVersion,
|
|
32
|
+
code: code,
|
|
33
|
+
stage: input.stage,
|
|
34
|
+
causeType: causeType,
|
|
35
|
+
fingerprint: fingerprint,
|
|
36
|
+
status: status,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Logs only the projected allowlist and returns the same immutable diagnostic. */
|
|
41
|
+
static report(input: DyNTS_SafeDiagnosticInput_Interface): DyNTS_SafeDiagnostic_Interface {
|
|
42
|
+
const diagnostic: DyNTS_SafeDiagnostic_Interface = this.project(input);
|
|
43
|
+
|
|
44
|
+
DyFM_Log.error('[DyNTS safe diagnostic]', diagnostic);
|
|
45
|
+
|
|
46
|
+
return diagnostic;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Creates a framework error that carries only the safe diagnostic allowlist. */
|
|
50
|
+
static toError(diagnostic: DyNTS_SafeDiagnostic_Interface): DyFM_Error {
|
|
51
|
+
return new DyFM_Error({
|
|
52
|
+
errorCode: diagnostic.code,
|
|
53
|
+
message: `Framework failure at ${diagnostic.stage}. Correlation: ${diagnostic.fingerprint}.`,
|
|
54
|
+
userMessage: 'A server operation failed. Contact the responsible team with the correlation fingerprint.',
|
|
55
|
+
additionalContent: { diagnostic: diagnostic },
|
|
56
|
+
status: diagnostic.status,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private static getCode(code: string): string {
|
|
61
|
+
return /^[A-Z][A-Z0-9_|-]{2,127}$/.test(code) ? code : this.defaultCode;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private static getCauseType(error: unknown): DyNTS_SafeDiagnosticCause_Type {
|
|
65
|
+
try {
|
|
66
|
+
if (error instanceof DyFM_Error) {
|
|
67
|
+
return DyNTS_SafeDiagnosticCause_Type.dynamoError;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (error instanceof Error) {
|
|
71
|
+
return DyNTS_SafeDiagnosticCause_Type.nativeError;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (error !== null && typeof error === 'object') {
|
|
75
|
+
return DyNTS_SafeDiagnosticCause_Type.object;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (error !== undefined) {
|
|
79
|
+
return DyNTS_SafeDiagnosticCause_Type.primitive;
|
|
80
|
+
}
|
|
81
|
+
} catch {
|
|
82
|
+
return DyNTS_SafeDiagnosticCause_Type.unknown;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return DyNTS_SafeDiagnosticCause_Type.unknown;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private static getStatus(status?: number): number {
|
|
89
|
+
if (typeof status !== 'number' || !Number.isInteger(status) || status < 100 || status > 599) {
|
|
90
|
+
return this.defaultStatus;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return status;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private static getFingerprint(subject: string[]): string {
|
|
97
|
+
return createHash('sha256').update(subject.join('\u0000'), 'utf8').digest('hex');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { DyNTS_SafeDiagnosticCause_Type } from './safe-diagnostic-cause.type-enum';
|
|
2
|
+
import { DyNTS_SafeDiagnosticStage_Type } from './safe-diagnostic-stage.type-enum';
|
|
3
|
+
|
|
4
|
+
/** Caller-owned input whose raw failure content is never returned or logged. */
|
|
5
|
+
export interface DyNTS_SafeDiagnosticInput_Interface {
|
|
6
|
+
code: string;
|
|
7
|
+
stage: DyNTS_SafeDiagnosticStage_Type;
|
|
8
|
+
error?: unknown;
|
|
9
|
+
status?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Exact public allowlist emitted by the safe diagnostic boundary. */
|
|
13
|
+
export interface DyNTS_SafeDiagnostic_Interface {
|
|
14
|
+
contractVersion: 'dynts-safe-diagnostic/1';
|
|
15
|
+
code: string;
|
|
16
|
+
stage: DyNTS_SafeDiagnosticStage_Type;
|
|
17
|
+
causeType: DyNTS_SafeDiagnosticCause_Type;
|
|
18
|
+
fingerprint: string;
|
|
19
|
+
status: number;
|
|
20
|
+
}
|
|
@@ -189,7 +189,7 @@ describe('| DyNTS_DbReadinessProbe_ControlService', (): void => {
|
|
|
189
189
|
failure.self = failure;
|
|
190
190
|
const result: DyNTS_DbReadinessProbeResult_Interface = await probe(createConnection({
|
|
191
191
|
readyStateGetter: (): number => {
|
|
192
|
-
throw failure;
|
|
192
|
+
throw failure;
|
|
193
193
|
},
|
|
194
194
|
}));
|
|
195
195
|
|
|
@@ -203,7 +203,7 @@ describe('| DyNTS_DbReadinessProbe_ControlService', (): void => {
|
|
|
203
203
|
failure.self = failure;
|
|
204
204
|
const result: DyNTS_DbReadinessProbeResult_Interface = await probe(createConnection({
|
|
205
205
|
databaseGetter: (): mongoose.Connection['db'] => {
|
|
206
|
-
throw failure;
|
|
206
|
+
throw failure;
|
|
207
207
|
},
|
|
208
208
|
}));
|
|
209
209
|
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
1
|
import {
|
|
5
2
|
DyFM_AnyError,
|
|
6
3
|
DyFM_Array,
|
|
@@ -11,23 +8,26 @@ import {
|
|
|
11
8
|
DyFM_Log,
|
|
12
9
|
DyFM_Metadata
|
|
13
10
|
} from '@futdevpro/fsm-dynamo';
|
|
14
|
-
import { DyNTS_App_Params } from '../../_models/control-models/app-params.control-model';
|
|
15
|
-
|
|
16
|
-
import {
|
|
17
|
-
DyNTS_GlobalErrorHandlerFn,
|
|
18
|
-
DyNTS_GlobalService_Settings
|
|
19
|
-
} from '../../_models/interfaces/global-service-settings.interface';
|
|
20
11
|
|
|
21
12
|
import { DyNTS_AuthService } from './auth.service';
|
|
22
|
-
/* import { DyNTS_DBServiceCollection } from './db-service-collection.service'; */
|
|
23
|
-
import { DyNTS_DBService } from '../base/db.service';
|
|
24
13
|
import { DyNTS_EmailService } from './email.service';
|
|
25
14
|
import { DyNTS_Service_Collection } from './service-collection.service';
|
|
15
|
+
/* import { DyNTS_DBServiceCollection } from './db-service-collection.service'; */
|
|
26
16
|
/* import { DyNTS_EmailServiceCollection } from './email-service-collection.service'; */
|
|
27
|
-
import {
|
|
17
|
+
import { DyNTS_DBService } from '../base/db.service';
|
|
28
18
|
import { DyNTS_SingletonService } from '../base/singleton.service';
|
|
29
19
|
/* import { DyNTS_archiveFlag } from '../../_collections/archive-flag.const'; */
|
|
30
20
|
import { DyNTS_getArchivedDBName } from '../../_collections/archive.util';
|
|
21
|
+
import { DyNTS_global_settings } from '../../_collections/global-settings.const';
|
|
22
|
+
import { DyNTS_App_Params } from '../../_models/control-models/app-params.control-model';
|
|
23
|
+
import {
|
|
24
|
+
DyNTS_GlobalErrorHandlerFn,
|
|
25
|
+
DyNTS_GlobalService_Settings
|
|
26
|
+
} from '../../_models/interfaces/global-service-settings.interface';
|
|
27
|
+
import { DyNTS_SafeDiagnosticStage_Type } from '../../_modules/server/safe-diagnostic/safe-diagnostic-stage.type-enum';
|
|
28
|
+
import {
|
|
29
|
+
DyNTS_SafeDiagnostic_ControlService
|
|
30
|
+
} from '../../_modules/server/safe-diagnostic/safe-diagnostic.control-service';
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* This is the main Global/Core Service Collection used by DynamoNTS,
|
|
@@ -76,8 +76,12 @@ export class DyNTS_GlobalService extends DyNTS_SingletonService {
|
|
|
76
76
|
|
|
77
77
|
static globalErrorHandler: DyNTS_GlobalErrorHandlerFn =
|
|
78
78
|
async (error: DyFM_AnyError): Promise<void> => {
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
DyNTS_SafeDiagnostic_ControlService.report({
|
|
80
|
+
code: 'DYNTS|GLOBAL|HANDLER_NOT_SET',
|
|
81
|
+
stage: DyNTS_SafeDiagnosticStage_Type.globalErrorHandler,
|
|
82
|
+
error: error,
|
|
83
|
+
});
|
|
84
|
+
};
|
|
81
85
|
|
|
82
86
|
/**
|
|
83
87
|
* You need to setup global Services through this function
|
|
@@ -182,11 +186,11 @@ export class DyNTS_GlobalService extends DyNTS_SingletonService {
|
|
|
182
186
|
try {
|
|
183
187
|
this.instance.dbServiceCollection[dbModel.dataName] = new DyNTS_DBService(dbModel);
|
|
184
188
|
} catch (error) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
189
|
+
DyNTS_SafeDiagnostic_ControlService.report({
|
|
190
|
+
code: 'DYNTS|GLOBAL|DB_SERVICE_CREATE_FAILED',
|
|
191
|
+
stage: DyNTS_SafeDiagnosticStage_Type.bootstrap,
|
|
192
|
+
error: error,
|
|
193
|
+
});
|
|
190
194
|
|
|
191
195
|
throw new DyFM_Error({
|
|
192
196
|
...this.getDefaultErrorSettings('setDBServices', error, 'DyNTS_GlobalService'),
|
|
@@ -265,7 +269,7 @@ export class DyNTS_GlobalService extends DyNTS_SingletonService {
|
|
|
265
269
|
|
|
266
270
|
private static async setErrorHandler(errorHandler?: DyNTS_GlobalErrorHandlerFn): Promise<void> {
|
|
267
271
|
try {
|
|
268
|
-
DyNTS_GlobalService.globalErrorHandler = (async (error:
|
|
272
|
+
DyNTS_GlobalService.globalErrorHandler = (async (error: unknown): Promise<void> => {
|
|
269
273
|
try {
|
|
270
274
|
if (errorHandler) {
|
|
271
275
|
await errorHandler(error);
|
|
@@ -280,20 +284,17 @@ export class DyNTS_GlobalService extends DyNTS_SingletonService {
|
|
|
280
284
|
'please check if it is using the same node_modules as the app)'
|
|
281
285
|
);
|
|
282
286
|
|
|
283
|
-
|
|
287
|
+
DyNTS_SafeDiagnostic_ControlService.report({
|
|
288
|
+
code: 'DYNTS|GLOBAL|UNHANDLED_ERROR',
|
|
289
|
+
stage: DyNTS_SafeDiagnosticStage_Type.globalErrorHandler,
|
|
290
|
+
error: error,
|
|
291
|
+
});
|
|
284
292
|
} catch (error) {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
} else {
|
|
291
|
-
DyFM_Log.error(
|
|
292
|
-
`NTS GLOBAL MULTILEVEL ERROR: ` +
|
|
293
|
-
`\nFailed to handle error on globalErrorHandler.`,
|
|
294
|
-
(error as DyFM_Error)?.getErrorSimplified?.() ?? error
|
|
295
|
-
);
|
|
296
|
-
}
|
|
293
|
+
DyNTS_SafeDiagnostic_ControlService.report({
|
|
294
|
+
code: 'DYNTS|GLOBAL|HANDLER_FAILED',
|
|
295
|
+
stage: DyNTS_SafeDiagnosticStage_Type.globalErrorHandler,
|
|
296
|
+
error: error,
|
|
297
|
+
});
|
|
297
298
|
}
|
|
298
299
|
});
|
|
299
300
|
} catch (error) {
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { DyFM_Log } from '@futdevpro/fsm-dynamo';
|
|
2
|
+
|
|
3
|
+
import { DyNTS_App_Params } from '../../_models/control-models/app-params.control-model';
|
|
4
|
+
import { DyNTS_Http_Settings } from '../../_models/control-models/http-settings.control-model';
|
|
5
|
+
import { DyNTS_GlobalService_Settings } from '../../_models/interfaces/global-service-settings.interface';
|
|
6
|
+
import { DyNTS_GlobalService } from '../core/global.service';
|
|
7
|
+
import { DyNTS_App } from './app.server';
|
|
8
|
+
|
|
9
|
+
class DyNTS_SafeDiagnosticTestApp extends DyNTS_App {
|
|
10
|
+
protected override asyncConstruct(): Promise<void> {
|
|
11
|
+
return Promise.resolve();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Supplies the minimal valid application parameters for the boundary test. */
|
|
15
|
+
getAppParams(): DyNTS_App_Params {
|
|
16
|
+
return new DyNTS_App_Params({
|
|
17
|
+
name: 'SafeDiagnosticTestApp',
|
|
18
|
+
version: '1.0.0',
|
|
19
|
+
dbName: 'safe_diagnostic_test',
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Keeps the test application independent from concrete global services. */
|
|
24
|
+
getGlobalServiceCollection(): DyNTS_GlobalService_Settings {
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Keeps the test application independent from a listening HTTP server. */
|
|
29
|
+
getPortSettings(): DyNTS_Http_Settings {
|
|
30
|
+
return new DyNTS_Http_Settings();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
class DyNTS_FailingBootstrapTestApp extends DyNTS_SafeDiagnosticTestApp {
|
|
35
|
+
protected override asyncConstruct(): Promise<void> {
|
|
36
|
+
return Promise.reject(new Error(DyNTS_FailingBootstrapTestApp.canary));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static readonly canary: string = 'mongodb://bootstrap-user:bootstrap-secret@private-host.example/database';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
describe('| DyNTS_App safe process boundary', (): void => {
|
|
43
|
+
const rejectionCanary: string = 'mongodb://rejection-user:rejection-secret@private-host.example/database';
|
|
44
|
+
let originalHandler: typeof DyNTS_GlobalService.globalErrorHandler;
|
|
45
|
+
let initialListeners: NodeJS.UnhandledRejectionListener[];
|
|
46
|
+
|
|
47
|
+
beforeEach((): void => {
|
|
48
|
+
originalHandler = DyNTS_GlobalService.globalErrorHandler;
|
|
49
|
+
initialListeners = process.listeners('unhandledRejection');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
afterEach((): void => {
|
|
53
|
+
DyNTS_GlobalService.globalErrorHandler = originalHandler;
|
|
54
|
+
process.listeners('unhandledRejection').forEach((listener: NodeJS.UnhandledRejectionListener): void => {
|
|
55
|
+
if (!initialListeners.includes(listener)) {
|
|
56
|
+
process.removeListener('unhandledRejection', listener);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('| projects an unhandled rejection before logging or forwarding it', async (): Promise<void> => {
|
|
62
|
+
const logSpy: jasmine.Spy = spyOn(DyFM_Log, 'error');
|
|
63
|
+
const handlerSpy: jasmine.Spy = jasmine.createSpy('globalErrorHandler').and.resolveTo();
|
|
64
|
+
|
|
65
|
+
DyNTS_GlobalService.globalErrorHandler = handlerSpy;
|
|
66
|
+
new DyNTS_SafeDiagnosticTestApp();
|
|
67
|
+
|
|
68
|
+
const addedListeners: NodeJS.UnhandledRejectionListener[] = process
|
|
69
|
+
.listeners('unhandledRejection')
|
|
70
|
+
.filter((listener: NodeJS.UnhandledRejectionListener): boolean => !initialListeners.includes(listener));
|
|
71
|
+
|
|
72
|
+
expect(addedListeners.length).toBe(1);
|
|
73
|
+
addedListeners[0](new Error(rejectionCanary), Promise.resolve());
|
|
74
|
+
await Promise.resolve();
|
|
75
|
+
|
|
76
|
+
const serializedLogs: string = JSON.stringify(logSpy.calls.allArgs());
|
|
77
|
+
const serializedForwardedError: string = JSON.stringify(handlerSpy.calls.mostRecent().args[0]);
|
|
78
|
+
|
|
79
|
+
expect(serializedLogs).toContain('dynts-safe-diagnostic/1');
|
|
80
|
+
expect(serializedForwardedError).toContain('dynts-safe-diagnostic/1');
|
|
81
|
+
expect(serializedForwardedError).toContain('DYNTS-AS0-BASE-UR');
|
|
82
|
+
expect(handlerSpy.calls.mostRecent().args.length).toBe(1);
|
|
83
|
+
expect(serializedLogs).not.toContain(rejectionCanary);
|
|
84
|
+
expect(serializedLogs).not.toContain('rejection-secret');
|
|
85
|
+
expect(serializedForwardedError).not.toContain(rejectionCanary);
|
|
86
|
+
expect(serializedForwardedError).not.toContain('rejection-secret');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('| projects a bootstrap rejection before logging, forwarding and exiting', async (): Promise<void> => {
|
|
90
|
+
const logSpy: jasmine.Spy = spyOn(DyFM_Log, 'error');
|
|
91
|
+
const handlerSpy: jasmine.Spy = jasmine.createSpy('globalErrorHandler').and.resolveTo();
|
|
92
|
+
const exitSpy: jasmine.Spy = spyOn(process, 'exit');
|
|
93
|
+
|
|
94
|
+
DyNTS_GlobalService.globalErrorHandler = handlerSpy;
|
|
95
|
+
new DyNTS_FailingBootstrapTestApp();
|
|
96
|
+
await new Promise<void>((resolve: () => void): NodeJS.Immediate => setImmediate(resolve));
|
|
97
|
+
|
|
98
|
+
const serializedLogs: string = JSON.stringify(logSpy.calls.allArgs());
|
|
99
|
+
const serializedForwardedError: string = JSON.stringify(handlerSpy.calls.mostRecent().args[0]);
|
|
100
|
+
|
|
101
|
+
expect(exitSpy).toHaveBeenCalledOnceWith(1);
|
|
102
|
+
expect(serializedLogs).toContain('dynts-safe-diagnostic/1');
|
|
103
|
+
expect(serializedForwardedError).toContain('dynts-safe-diagnostic/1');
|
|
104
|
+
expect(serializedForwardedError).toContain('DYNTS-AS0-BOOTSTRAP-FAILED');
|
|
105
|
+
expect(handlerSpy.calls.mostRecent().args.length).toBe(1);
|
|
106
|
+
expect(serializedLogs).not.toContain(DyNTS_FailingBootstrapTestApp.canary);
|
|
107
|
+
expect(serializedLogs).not.toContain('bootstrap-secret');
|
|
108
|
+
expect(serializedForwardedError).not.toContain(DyNTS_FailingBootstrapTestApp.canary);
|
|
109
|
+
expect(serializedForwardedError).not.toContain('bootstrap-secret');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('| projects raw failures in the default global error handler', async (): Promise<void> => {
|
|
113
|
+
const logSpy: jasmine.Spy = spyOn(DyFM_Log, 'error');
|
|
114
|
+
|
|
115
|
+
await DyNTS_GlobalService.setServices({});
|
|
116
|
+
await DyNTS_GlobalService.globalErrorHandler(new Error(rejectionCanary));
|
|
117
|
+
|
|
118
|
+
const serializedLogs: string = JSON.stringify(logSpy.calls.allArgs());
|
|
119
|
+
|
|
120
|
+
expect(serializedLogs).toContain('dynts-safe-diagnostic/1');
|
|
121
|
+
expect(serializedLogs).not.toContain(rejectionCanary);
|
|
122
|
+
expect(serializedLogs).not.toContain('rejection-secret');
|
|
123
|
+
});
|
|
124
|
+
});
|