@fluidframework/telemetry-utils 2.0.0-rc.2.0.2 → 2.0.0-rc.3.0.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.
Files changed (132) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/api-report/telemetry-utils.api.md +6 -6
  3. package/dist/config.d.ts +2 -2
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/config.js +6 -2
  6. package/dist/config.js.map +1 -1
  7. package/dist/error.d.ts +4 -2
  8. package/dist/error.d.ts.map +1 -1
  9. package/dist/error.js +5 -5
  10. package/dist/error.js.map +1 -1
  11. package/dist/errorLogging.d.ts +3 -2
  12. package/dist/errorLogging.d.ts.map +1 -1
  13. package/dist/errorLogging.js.map +1 -1
  14. package/dist/eventEmitterWithErrorHandling.d.ts +1 -1
  15. package/dist/eventEmitterWithErrorHandling.d.ts.map +1 -1
  16. package/dist/eventEmitterWithErrorHandling.js +2 -2
  17. package/dist/eventEmitterWithErrorHandling.js.map +1 -1
  18. package/dist/events.d.ts +1 -1
  19. package/dist/events.d.ts.map +1 -1
  20. package/dist/events.js.map +1 -1
  21. package/dist/fluidErrorBase.d.ts.map +1 -1
  22. package/dist/fluidErrorBase.js.map +1 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +2 -2
  25. package/dist/index.js.map +1 -1
  26. package/dist/legacy.d.ts +28 -0
  27. package/dist/logger.d.ts +2 -2
  28. package/dist/logger.d.ts.map +1 -1
  29. package/dist/logger.js +1 -1
  30. package/dist/logger.js.map +1 -1
  31. package/dist/mockLogger.d.ts +1 -1
  32. package/dist/mockLogger.d.ts.map +1 -1
  33. package/dist/mockLogger.js +3 -3
  34. package/dist/mockLogger.js.map +1 -1
  35. package/dist/public.d.ts +20 -0
  36. package/dist/sampledTelemetryHelper.d.ts +2 -2
  37. package/dist/sampledTelemetryHelper.d.ts.map +1 -1
  38. package/dist/sampledTelemetryHelper.js.map +1 -1
  39. package/dist/utils.d.ts +4 -0
  40. package/dist/utils.d.ts.map +1 -1
  41. package/dist/utils.js +4 -0
  42. package/dist/utils.js.map +1 -1
  43. package/internal.d.ts +11 -0
  44. package/legacy.d.ts +11 -0
  45. package/lib/config.d.ts +2 -2
  46. package/lib/config.d.ts.map +1 -1
  47. package/lib/config.js +5 -1
  48. package/lib/config.js.map +1 -1
  49. package/lib/error.d.ts +4 -2
  50. package/lib/error.d.ts.map +1 -1
  51. package/lib/error.js +1 -1
  52. package/lib/error.js.map +1 -1
  53. package/lib/errorLogging.d.ts +3 -2
  54. package/lib/errorLogging.d.ts.map +1 -1
  55. package/lib/errorLogging.js.map +1 -1
  56. package/lib/eventEmitterWithErrorHandling.d.ts +1 -1
  57. package/lib/eventEmitterWithErrorHandling.d.ts.map +1 -1
  58. package/lib/eventEmitterWithErrorHandling.js.map +1 -1
  59. package/lib/events.d.ts +1 -1
  60. package/lib/events.d.ts.map +1 -1
  61. package/lib/events.js.map +1 -1
  62. package/lib/fluidErrorBase.d.ts.map +1 -1
  63. package/lib/fluidErrorBase.js.map +1 -1
  64. package/lib/index.d.ts.map +1 -1
  65. package/lib/index.js.map +1 -1
  66. package/lib/legacy.d.ts +28 -0
  67. package/lib/logger.d.ts +2 -2
  68. package/lib/logger.d.ts.map +1 -1
  69. package/lib/logger.js +2 -2
  70. package/lib/logger.js.map +1 -1
  71. package/lib/mockLogger.d.ts +1 -1
  72. package/lib/mockLogger.d.ts.map +1 -1
  73. package/lib/mockLogger.js +2 -2
  74. package/lib/mockLogger.js.map +1 -1
  75. package/lib/public.d.ts +20 -0
  76. package/lib/sampledTelemetryHelper.d.ts +2 -2
  77. package/lib/sampledTelemetryHelper.d.ts.map +1 -1
  78. package/lib/sampledTelemetryHelper.js.map +1 -1
  79. package/lib/utils.d.ts +4 -0
  80. package/lib/utils.d.ts.map +1 -1
  81. package/lib/utils.js +4 -0
  82. package/lib/utils.js.map +1 -1
  83. package/package.json +27 -48
  84. package/src/config.ts +5 -3
  85. package/src/error.ts +4 -4
  86. package/src/errorLogging.ts +6 -8
  87. package/src/eventEmitterWithErrorHandling.ts +2 -1
  88. package/src/events.ts +2 -1
  89. package/src/fluidErrorBase.ts +1 -0
  90. package/src/index.ts +1 -0
  91. package/src/logger.ts +7 -6
  92. package/src/mockLogger.ts +4 -3
  93. package/src/sampledTelemetryHelper.ts +3 -2
  94. package/src/utils.ts +2 -0
  95. package/api-extractor-cjs.json +0 -8
  96. package/dist/telemetry-utils-alpha.d.ts +0 -282
  97. package/dist/telemetry-utils-beta.d.ts +0 -239
  98. package/dist/telemetry-utils-public.d.ts +0 -239
  99. package/dist/telemetry-utils-untrimmed.d.ts +0 -1077
  100. package/lib/telemetry-utils-alpha.d.ts +0 -282
  101. package/lib/telemetry-utils-beta.d.ts +0 -239
  102. package/lib/telemetry-utils-public.d.ts +0 -239
  103. package/lib/telemetry-utils-untrimmed.d.ts +0 -1077
  104. package/lib/test/EventEmitterWithErrorHandling.spec.js +0 -86
  105. package/lib/test/EventEmitterWithErrorHandling.spec.js.map +0 -1
  106. package/lib/test/childLogger.spec.js +0 -233
  107. package/lib/test/childLogger.spec.js.map +0 -1
  108. package/lib/test/config.spec.js +0 -229
  109. package/lib/test/config.spec.js.map +0 -1
  110. package/lib/test/error.spec.js +0 -161
  111. package/lib/test/error.spec.js.map +0 -1
  112. package/lib/test/errorLogging.spec.js +0 -801
  113. package/lib/test/errorLogging.spec.js.map +0 -1
  114. package/lib/test/errorTypeLoggingTest.spec.js +0 -107
  115. package/lib/test/errorTypeLoggingTest.spec.js.map +0 -1
  116. package/lib/test/mockLogger.spec.js +0 -164
  117. package/lib/test/mockLogger.spec.js.map +0 -1
  118. package/lib/test/multiSinkLogger.spec.js +0 -84
  119. package/lib/test/multiSinkLogger.spec.js.map +0 -1
  120. package/lib/test/performanceEvent.spec.js +0 -86
  121. package/lib/test/performanceEvent.spec.js.map +0 -1
  122. package/lib/test/sampledTelemetryHelper.spec.js +0 -169
  123. package/lib/test/sampledTelemetryHelper.spec.js.map +0 -1
  124. package/lib/test/telemetryLogger.spec.js +0 -357
  125. package/lib/test/telemetryLogger.spec.js.map +0 -1
  126. package/lib/test/thresholdCounter.spec.js +0 -51
  127. package/lib/test/thresholdCounter.spec.js.map +0 -1
  128. package/lib/test/types/validateTelemetryUtilsPrevious.generated.js +0 -132
  129. package/lib/test/types/validateTelemetryUtilsPrevious.generated.js.map +0 -1
  130. package/lib/test/utils.spec.js +0 -284
  131. package/lib/test/utils.spec.js.map +0 -1
  132. /package/{dist → lib}/tsdoc-metadata.json +0 -0
@@ -1,801 +0,0 @@
1
- /*!
2
- * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
- * Licensed under the MIT License.
4
- */
5
- /* eslint-disable @typescript-eslint/no-unsafe-member-access */
6
- /* eslint-disable @typescript-eslint/no-explicit-any */
7
- /* eslint-disable @typescript-eslint/no-unsafe-argument */
8
- /* eslint-disable @typescript-eslint/no-unsafe-assignment */
9
- /* eslint-disable @typescript-eslint/no-unsafe-call */
10
- /* eslint-disable unicorn/consistent-function-scoping */
11
- /* eslint-disable unicorn/no-null */
12
- import { strict as assert } from "node:assert";
13
- import sinon from "sinon";
14
- import { v4 as uuid } from "uuid";
15
- import { TelemetryDataTag, TelemetryLogger, TaggedLoggerAdapter } from "../logger.js";
16
- import { LoggingError, isTaggedTelemetryPropertyValue, normalizeError, wrapError, wrapErrorAndLog, extractLogSafeErrorProperties, isExternalError, } from "../errorLogging.js";
17
- import { hasErrorInstanceId, isFluidError, isValidLegacyError, } from "../fluidErrorBase.js";
18
- import { MockLogger } from "../mockLogger.js";
19
- describe("Error Logging", () => {
20
- describe("TelemetryLogger.prepareErrorObject", () => {
21
- function freshEvent() {
22
- return { category: "cat1", eventName: "event1" };
23
- }
24
- function createILoggingError(props) {
25
- return { ...props, getTelemetryProperties: () => props };
26
- }
27
- it("non-object error added to event", () => {
28
- let event = freshEvent();
29
- TelemetryLogger.prepareErrorObject(event, "hello", false);
30
- assert.strictEqual(event.error, "hello", "string should work");
31
- event = freshEvent();
32
- TelemetryLogger.prepareErrorObject(event, 42, false);
33
- assert.strictEqual(event.error, "42", "number should work");
34
- event = freshEvent();
35
- TelemetryLogger.prepareErrorObject(event, true, false);
36
- assert.strictEqual(event.error, "true", "boolean should work");
37
- event = freshEvent();
38
- TelemetryLogger.prepareErrorObject(event, undefined, false);
39
- assert.strictEqual(event.error, "undefined", "undefined should work");
40
- // Technically this violates TelemetryBaseEventPropertyType's type constraint but it's actually supported
41
- event = freshEvent();
42
- TelemetryLogger.prepareErrorObject(event, null, false);
43
- assert.strictEqual(event.error, "null", "null should work");
44
- });
45
- it("stack and message added to event (stack should exclude message)", () => {
46
- const event = freshEvent();
47
- const error = new Error("boom");
48
- error.name = "MyErrorName";
49
- TelemetryLogger.prepareErrorObject(event, error, false);
50
- assert(event.error === "boom");
51
- assert(event.stack.includes("MyErrorName"));
52
- assert(!event.stack.includes("boom"));
53
- });
54
- it("containsPII (legacy) is ignored", () => {
55
- // Previously, setting containsPII = true on an error obj would (attempt to) redact its message
56
- const event = freshEvent();
57
- const error = new Error("boom");
58
- error.name = "MyErrorName";
59
- error.containsPII = true;
60
- TelemetryLogger.prepareErrorObject(event, error, false);
61
- assert(event.error === "boom");
62
- assert(event.stack.includes("MyErrorName"));
63
- });
64
- it("getTelemetryProperties - tags on overwritten Error base props", () => {
65
- const event = freshEvent();
66
- const error = createILoggingError({
67
- message: { value: "Mark Fields", tag: "UserData" },
68
- stack: { value: "tagged", tag: TelemetryDataTag.CodeArtifact },
69
- });
70
- TelemetryLogger.prepareErrorObject(event, error, false);
71
- assert.deepStrictEqual(event.message, { value: "Mark Fields", tag: "UserData" });
72
- assert.deepStrictEqual(event.error, "[object Object]"); // weird but ok
73
- assert.deepStrictEqual(event.stack, {
74
- value: "tagged",
75
- tag: TelemetryDataTag.CodeArtifact,
76
- });
77
- });
78
- it("getTelemetryProperties absent - no further props added", () => {
79
- const event = freshEvent();
80
- const error = { ...new Error("boom"), foo: "foo", bar: 2 };
81
- TelemetryLogger.prepareErrorObject(event, error, false);
82
- assert(event.foo === undefined && event.bar === undefined);
83
- });
84
- it("getTelemetryProperties overlaps event - do not overwrite", () => {
85
- const event = { ...freshEvent(), foo: "event_foo", bar: 42 };
86
- const error = createILoggingError({ foo: "error_foo", bar: -1 });
87
- TelemetryLogger.prepareErrorObject(event, error, false);
88
- assert(event.foo === "event_foo" && event.bar === 42);
89
- });
90
- it("getTelemetryProperties present - add additional props", () => {
91
- const event = freshEvent();
92
- const error = createILoggingError({ foo: "foo", bar: 2 });
93
- TelemetryLogger.prepareErrorObject(event, error, false);
94
- assert(event.foo === "foo" && event.bar === 2);
95
- });
96
- it("fetchStack false - Don't add a stack if missing", () => {
97
- const event = freshEvent();
98
- const error = { message: "I have no stack" };
99
- TelemetryLogger.prepareErrorObject(event, error, false);
100
- assert.strictEqual(event.stack, undefined);
101
- });
102
- it("fetchStack true - Don't add a stack if present", () => {
103
- const event = freshEvent();
104
- const error = new Error("boom");
105
- error.name = "MyName";
106
- TelemetryLogger.prepareErrorObject(event, error, false);
107
- assert(event.stack.includes("MyName"));
108
- });
109
- it("fetchStack true - Add a stack if missing", () => {
110
- const event = freshEvent();
111
- const error = { message: "I have no stack - boom", name: "MyName" };
112
- TelemetryLogger.prepareErrorObject(event, error, true);
113
- assert.strictEqual(typeof event.stack, "string");
114
- assert(!event.stack.includes("MyName"));
115
- });
116
- });
117
- describe("TaggedLoggerAdapter", () => {
118
- const events = [];
119
- class TestTelemetryLogger extends TelemetryLogger {
120
- constructor() {
121
- super(...arguments);
122
- this.events = [];
123
- }
124
- send(event) {
125
- events.push(this.prepareEvent(event));
126
- }
127
- }
128
- const adaptedLogger = new TaggedLoggerAdapter(new TestTelemetryLogger("namespace"));
129
- it("TaggedLoggerAdapter - tagged UserData is removed", () => {
130
- const event = {
131
- category: "cat",
132
- eventName: "event",
133
- userDataObject: {
134
- tag: TelemetryDataTag.UserData,
135
- value: "someUserData",
136
- },
137
- };
138
- adaptedLogger.send(event);
139
- assert.strictEqual(events[0].userDataObject, "REDACTED (UserData)", "someUserData should be redacted");
140
- events.pop();
141
- });
142
- it("TaggedLoggerAdapter - tagged CodeArtifact are preserved", () => {
143
- const event = {
144
- category: "cat",
145
- eventName: "event",
146
- packageDataObject: {
147
- tag: TelemetryDataTag.CodeArtifact,
148
- value: "somePackageData",
149
- },
150
- };
151
- adaptedLogger.send(event);
152
- assert.strictEqual(events[0].packageDataObject, "somePackageData", "somePackageData should be preserved");
153
- events.pop();
154
- });
155
- it("TaggedLoggerAdapter - tagged [unrecognized tag] are removed", () => {
156
- const event = {
157
- category: "cat",
158
- eventName: "event",
159
- unknownTaggedObject: {
160
- tag: "someUnknownTag",
161
- value: "someEvilData",
162
- },
163
- };
164
- adaptedLogger.send(event);
165
- assert.strictEqual(events[0].unknownTaggedObject, "REDACTED (unknown tag)", "someUnknownTag should be redacted");
166
- events.pop();
167
- });
168
- });
169
- describe("TaggedTelemetryData", () => {
170
- it("Ensure backwards compatibility", () => {
171
- // The values of the enum should never change (even if the keys are renamed)
172
- assert(TelemetryDataTag.CodeArtifact === "CodeArtifact");
173
- assert(TelemetryDataTag.UserData === "UserData");
174
- });
175
- });
176
- describe("isTaggedTelemetryPropertyValue", () => {
177
- it("non-object input not ok", () => {
178
- assert.strictEqual(isTaggedTelemetryPropertyValue("hello"), false);
179
- assert.strictEqual(isTaggedTelemetryPropertyValue(123), false);
180
- assert.strictEqual(isTaggedTelemetryPropertyValue(false), false);
181
- assert.strictEqual(isTaggedTelemetryPropertyValue(undefined), false);
182
- assert.strictEqual(isTaggedTelemetryPropertyValue(null), false);
183
- assert.strictEqual(isTaggedTelemetryPropertyValue(function x() {
184
- return 54;
185
- }), false);
186
- assert.strictEqual(isTaggedTelemetryPropertyValue(Symbol("okay")), false);
187
- });
188
- it("non-object value ok", () => {
189
- assert.strictEqual(isTaggedTelemetryPropertyValue({ value: "hello", tag: "any string" }), true);
190
- assert.strictEqual(isTaggedTelemetryPropertyValue({ value: 123, tag: "any string" }), true);
191
- assert.strictEqual(isTaggedTelemetryPropertyValue({ value: false, tag: "any string" }), true);
192
- assert.strictEqual(isTaggedTelemetryPropertyValue({ value: undefined, tag: "any string" }), true);
193
- });
194
- it("Check result for various invalid inputs (per typings)", () => {
195
- assert.strictEqual(isTaggedTelemetryPropertyValue({ tag: "any string" }), true, "value prop may be absent");
196
- // The type guard used is a bit imprecise. Here is proof (these "shouldn't" be ok)
197
- assert.strictEqual(isTaggedTelemetryPropertyValue({
198
- value: function x() {
199
- return 54;
200
- },
201
- tag: "any string",
202
- }), true);
203
- assert.strictEqual(isTaggedTelemetryPropertyValue({ value: Symbol("okay"), tag: "any string" }), true);
204
- assert.strictEqual(isTaggedTelemetryPropertyValue({ value: "hello", tag: 1 }), false, "number tag is bad");
205
- assert.strictEqual(isTaggedTelemetryPropertyValue({ value: "hello", tag: false }), false, "boolean tag is bad");
206
- assert.strictEqual(isTaggedTelemetryPropertyValue({ value: "hello", tag: {} }), false, "object tag is bad");
207
- assert.strictEqual(isTaggedTelemetryPropertyValue({ value: "hello", tag: null }), false, "null tag is bad");
208
- assert.strictEqual(isTaggedTelemetryPropertyValue({ value: "hello" }), false, "undefined (missing) tag is bad");
209
- });
210
- });
211
- describe("LoggingError", () => {
212
- it("ctor props are assigned to the object", () => {
213
- const loggingError = new LoggingError("myMessage", {
214
- p1: 1,
215
- p2: "two",
216
- p3: true,
217
- tagged: { value: 4, tag: "CodeArtifact" },
218
- });
219
- const errorAsAny = loggingError;
220
- assert.strictEqual(errorAsAny.message, "myMessage");
221
- assert.strictEqual(errorAsAny.p1, 1);
222
- assert.strictEqual(errorAsAny.p2, "two");
223
- assert.strictEqual(errorAsAny.p3, true);
224
- assert.deepStrictEqual(errorAsAny.tagged, { value: 4, tag: "CodeArtifact" });
225
- });
226
- it("errorInstanceId unique each time", () => {
227
- const e1 = new LoggingError("1");
228
- const e2 = new LoggingError("2");
229
- assert.equal(e1.errorInstanceId.length, 36, "should be guid-length");
230
- assert.equal(e2.errorInstanceId.length, 36, "should be guid-length");
231
- assert.notEqual(e1.errorInstanceId, e2.errorInstanceId, "each error instance should get unique id");
232
- });
233
- it("getTelemetryProperties extracts all untagged ctor props", () => {
234
- const loggingError = new LoggingError("myMessage", { p1: 1, p2: "two", p3: true });
235
- const props = loggingError.getTelemetryProperties();
236
- assert.strictEqual(props.message, "myMessage");
237
- assert.strictEqual(typeof props.stack, "string");
238
- assert.strictEqual(props.name, undefined); // Error.name is not logged
239
- assert.strictEqual(props.p1, 1);
240
- assert.strictEqual(props.p2, "two");
241
- assert.strictEqual(props.p3, true);
242
- });
243
- it("getTelemetryProperties respects omitPropsFromLogging", () => {
244
- const loggingError = new LoggingError("myMessage", {}, new Set(["foo"]));
245
- loggingError.foo = "secrets!";
246
- loggingError.bar = "normal";
247
- const props = loggingError.getTelemetryProperties();
248
- assert.strictEqual(props.omitPropsFromLogging, undefined, "omitPropsFromLogging itself should be omitted");
249
- assert.strictEqual(props.foo, undefined, "foo should have been omitted");
250
- assert.strictEqual(props.bar, "normal", "bar should not be omitted");
251
- });
252
- it("addTelemetryProperties - adds to object, returned from getTelemetryProperties, doesn't overwrite", () => {
253
- const loggingError = new LoggingError("myMessage", { p1: 1, p2: "two", p3: true });
254
- loggingError.p1 = "should not be overwritten";
255
- loggingError.addTelemetryProperties({
256
- p1: "ignored",
257
- p4: 4,
258
- p5: true,
259
- p6: { value: 5, tag: "CodeArtifact" },
260
- p7: ["a", "b", "c"],
261
- p8: [1, 2, 3],
262
- p9: [true, true, false],
263
- p10: { one: "1" },
264
- p11: undefined,
265
- p12: { value: ["1", 2, true], tag: "CodeArtifact" },
266
- });
267
- const props = loggingError.getTelemetryProperties();
268
- assert.strictEqual(props.p1, "should not be overwritten");
269
- assert.strictEqual(props.p4, 4);
270
- assert.strictEqual(props.p5, true);
271
- assert.deepStrictEqual(props.p6, { value: 5, tag: "CodeArtifact" });
272
- assert.strictEqual(props.p7, '["a","b","c"]');
273
- assert.strictEqual(props.p8, "[1,2,3]");
274
- assert.strictEqual(props.p9, "[true,true,false]");
275
- assert.strictEqual(props.p10, `{"one":"1"}`);
276
- assert.strictEqual(props.p11, undefined);
277
- assert.deepStrictEqual(props.p12, { value: `["1",2,true]`, tag: "CodeArtifact" });
278
- const errorAsAny = loggingError;
279
- assert.strictEqual(errorAsAny.p1, "should not be overwritten");
280
- assert.strictEqual(errorAsAny.p4, 4);
281
- assert.strictEqual(errorAsAny.p5, true);
282
- assert.deepStrictEqual(errorAsAny.p6, { value: 5, tag: "CodeArtifact" });
283
- assert.deepStrictEqual(errorAsAny.p7, ["a", "b", "c"]);
284
- assert.deepStrictEqual(errorAsAny.p8, [1, 2, 3]);
285
- assert.deepStrictEqual(errorAsAny.p9, [true, true, false]);
286
- assert.deepStrictEqual(errorAsAny.p10, { one: "1" });
287
- assert.strictEqual(errorAsAny.p11, undefined);
288
- assert.deepStrictEqual(errorAsAny.p12, { value: ["1", 2, true], tag: "CodeArtifact" });
289
- });
290
- it("Set valid props via 'as any' - returned from getTelemetryProperties, overwrites", () => {
291
- const loggingError = new LoggingError("myMessage", { p1: 1, p2: "two", p3: true });
292
- loggingError.addTelemetryProperties({ p1: "should be overwritten" });
293
- const errorAsAny = loggingError;
294
- // Things that could be set with addTelemetryProperties
295
- errorAsAny.p1 = "one";
296
- errorAsAny.p4 = 4;
297
- errorAsAny.p5 = true;
298
- errorAsAny.p6 = { value: 5, tag: "CodeArtifact" };
299
- errorAsAny.userData6 = { value: 5, tag: "UserData" };
300
- errorAsAny.p7 = ["a", "b", "c"];
301
- errorAsAny.p8 = [1, 2, 3];
302
- errorAsAny.p9 = [true, true, false];
303
- errorAsAny.p10 = { one: "1" };
304
- errorAsAny.p11 = undefined;
305
- errorAsAny.p12 = { value: ["1", 2, true], tag: "CodeArtifact" };
306
- // Things that can't be set with addTelemetryProperties
307
- errorAsAny.p13 = null; // Null
308
- errorAsAny.p14 = ["a", "b", "c", null]; // Array with nulls
309
- errorAsAny.p15 = [[1, 2]]; // Nested array
310
- const props = loggingError.getTelemetryProperties();
311
- assert.strictEqual(props.p1, "one");
312
- assert.strictEqual(props.p4, 4);
313
- assert.strictEqual(props.p5, true);
314
- assert.deepStrictEqual(props.p6, { value: 5, tag: "CodeArtifact" });
315
- assert.deepStrictEqual(props.userData6, { value: 5, tag: "UserData" });
316
- assert.strictEqual(props.p7, `["a","b","c"]`);
317
- assert.strictEqual(props.p8, `[1,2,3]`);
318
- assert.strictEqual(props.p9, `[true,true,false]`);
319
- assert.strictEqual(props.p10, `{"one":"1"}`);
320
- assert.strictEqual(props.p11, undefined);
321
- assert.deepStrictEqual(props.p12, { value: `["1",2,true]`, tag: "CodeArtifact" });
322
- assert.strictEqual(props.p13, "null");
323
- assert.strictEqual(props.p14, `["a","b","c",null]`);
324
- assert.strictEqual(props.p15, "[[1,2]]");
325
- });
326
- it("addTelemetryProperties - Does not overwrite base class Error fields (untagged)", () => {
327
- const loggingError = new LoggingError("myMessage");
328
- const propsWillBeIgnored = { message: "surprise1", stack: "surprise2" };
329
- loggingError.addTelemetryProperties(propsWillBeIgnored);
330
- const props = loggingError.getTelemetryProperties();
331
- delete props.fluidErrorCode; // It's on there for back compat, not trying to test it here
332
- const { message, stack, errorInstanceId } = loggingError;
333
- assert.deepStrictEqual(props, { message, stack, errorInstanceId }, "addTelemetryProperties should not overwrite existing props");
334
- });
335
- it("addTelemetryProperties - Does not overwrite base class Error fields (tagged)", () => {
336
- const loggingError = new LoggingError("myMessage");
337
- const propsWillBeIgnored = {
338
- message: { value: "Mark Fields", tag: "UserData" },
339
- stack: { value: "surprise2", tag: "CodeArtifact" },
340
- };
341
- loggingError.addTelemetryProperties(propsWillBeIgnored);
342
- const props = loggingError.getTelemetryProperties();
343
- delete props.fluidErrorCode; // It's on there for back compat, not trying to test it here
344
- const { message, stack, errorInstanceId } = loggingError;
345
- assert.deepStrictEqual(props, { message, stack, errorInstanceId }, "addTelemetryProperties should not overwrite existing props");
346
- });
347
- it("addTelemetryProperties - Does not overwrite existing telemetry props", () => {
348
- const loggingError = new LoggingError("myMessage", { p1: 1 });
349
- loggingError.addTelemetryProperties({ p1: "one" });
350
- assert(loggingError.getTelemetryProperties().p1 === 1);
351
- loggingError.addTelemetryProperties({ p1: "uno" });
352
- assert(loggingError.getTelemetryProperties().p1 === 1);
353
- });
354
- it("typeCheck - Correctly type checks an instace of LoggingError", () => {
355
- const loggingError = new LoggingError("myMessage", { p1: 1 });
356
- const normalizedLoggingError = normalizeError(loggingError);
357
- const basicError = new Error("basicErrorMessage");
358
- assert.strictEqual(LoggingError.typeCheck(loggingError), true, "LoggingError is a LoggingError");
359
- assert.strictEqual(LoggingError.typeCheck(normalizedLoggingError), true, "Normalized Error is a LoggingError");
360
- assert.strictEqual(LoggingError.typeCheck(basicError), false, "Error is not a LoggingError");
361
- });
362
- });
363
- describe("extractLogSafeErrorProperties", () => {
364
- function createSampleError() {
365
- try {
366
- const error = new Error("asdf");
367
- error.name = "FooError";
368
- throw error;
369
- }
370
- catch (error) {
371
- return error;
372
- }
373
- }
374
- it("non-object error yields correct message", () => {
375
- assert.strictEqual(extractLogSafeErrorProperties("hello", false /* sanitizeStack */).message, "hello");
376
- assert.strictEqual(extractLogSafeErrorProperties(42, false /* sanitizeStack */).message, "42");
377
- assert.strictEqual(extractLogSafeErrorProperties(true, false /* sanitizeStack */).message, "true");
378
- assert.strictEqual(extractLogSafeErrorProperties(undefined, false /* sanitizeStack */).message, "undefined");
379
- });
380
- it("object error yields correct message", () => {
381
- assert.strictEqual(extractLogSafeErrorProperties({ message: "hello" }, false /* sanitizeStack */)
382
- .message, "hello");
383
- assert.strictEqual(extractLogSafeErrorProperties({ message: 42 }, false /* sanitizeStack */).message, "[object Object]");
384
- assert.strictEqual(extractLogSafeErrorProperties({ foo: 42 }, false /* sanitizeStack */).message, "[object Object]");
385
- assert.strictEqual(extractLogSafeErrorProperties([1, 2, 3], false /* sanitizeStack */).message, "1,2,3");
386
- assert.strictEqual(extractLogSafeErrorProperties(null, false /* sanitizeStack */).message, "null");
387
- });
388
- it("extract errorType", () => {
389
- assert.strictEqual(extractLogSafeErrorProperties({ errorType: "hello" }, false /* sanitizeStack */)
390
- .errorType, "hello");
391
- assert.strictEqual(extractLogSafeErrorProperties({ foo: "hello" }, false /* sanitizeStack */)
392
- .errorType, undefined);
393
- assert.strictEqual(extractLogSafeErrorProperties({ errorType: 42 }, false /* sanitizeStack */)
394
- .errorType, undefined);
395
- assert.strictEqual(extractLogSafeErrorProperties(42, false /* sanitizeStack */).errorType, undefined);
396
- });
397
- it("extract stack", () => {
398
- const e1 = createSampleError();
399
- const stack = extractLogSafeErrorProperties(e1, false /* sanitizeStack */).stack;
400
- assert(typeof stack === "string");
401
- assert(stack?.includes("asdf"), "stack is expected to contain the message");
402
- assert(stack?.includes("FooError"), "stack is expected to contain the name");
403
- const sanitizedStack = extractLogSafeErrorProperties(e1, true /* sanitizeStack */).stack;
404
- assert(typeof sanitizedStack === "string");
405
- assert(!sanitizedStack?.includes("asdf"), "message should have been removed from sanitized stack");
406
- assert(sanitizedStack?.includes("FooError"), "name should still be in the sanitized stack");
407
- });
408
- it("extract stack non-standard values", () => {
409
- // sanitizeStack true
410
- assert.strictEqual(extractLogSafeErrorProperties({ stack: "hello" }, true /* sanitizeStack */).stack, "");
411
- assert.strictEqual(extractLogSafeErrorProperties({ stack: "hello", name: "name" }, true /* sanitizeStack */).stack, "name");
412
- // sanitizeStack false
413
- assert.strictEqual(extractLogSafeErrorProperties({ stack: "hello" }, false /* sanitizeStack */).stack, "hello");
414
- assert.strictEqual(extractLogSafeErrorProperties({ stack: "hello", name: "name" }, false /* sanitizeStack */).stack, "hello");
415
- assert.strictEqual(extractLogSafeErrorProperties({ foo: "hello" }, false /* sanitizeStack */).stack, undefined);
416
- assert.strictEqual(extractLogSafeErrorProperties({ stack: 42 }, false /* sanitizeStack */).stack, undefined);
417
- assert.strictEqual(extractLogSafeErrorProperties(42, false /* sanitizeStack */).stack, undefined);
418
- });
419
- });
420
- describe("normalizeError", () => {
421
- describe("preserves properties", () => {
422
- it("missing properties are not set", () => {
423
- // eslint-disable-next-line unicorn/error-message
424
- const unknownError = new Error();
425
- const newError = normalizeError(unknownError);
426
- assert.strictEqual(newError.canRetry, undefined, "canRetry not undefined");
427
- assert.strictEqual(newError.retryAfterSeconds, undefined, "retryAfterSeconds not undefined");
428
- });
429
- it("existing retry properties are present in normalized error", () => {
430
- const unknownError =
431
- // eslint-disable-next-line unicorn/error-message
432
- new Error();
433
- unknownError.canRetry = true;
434
- unknownError.retryAfterSeconds = 100;
435
- const newError = normalizeError(unknownError);
436
- assert.strictEqual(newError.canRetry, true, "canRetry not true");
437
- assert.strictEqual(newError.retryAfterSeconds, 100, "retryAfterSeconds not 100");
438
- });
439
- });
440
- });
441
- });
442
- class TestFluidError {
443
- constructor(errorProps) {
444
- this.name = "Error";
445
- this.errorType = errorProps.errorType;
446
- this.message = errorProps.message;
447
- this.stack = errorProps.stack;
448
- this.errorInstanceId = uuid();
449
- this.atpStub = sinon.stub(this, "addTelemetryProperties");
450
- this.gtpSpy = sinon.spy(this, "getTelemetryProperties");
451
- this.expectedTelemetryProps = { ...errorProps };
452
- }
453
- getTelemetryProperties() {
454
- // Don't actually return any props. We'll use the spy to ensure it was called
455
- return {};
456
- }
457
- addTelemetryProperties(props) {
458
- throw new Error("Not Implemented - Expected to be Stubbed via Sinon");
459
- }
460
- withoutProperty(propName) {
461
- const objectWithoutProp = {};
462
- objectWithoutProp[propName] = undefined;
463
- Object.assign(this, objectWithoutProp);
464
- return this;
465
- }
466
- withExpectedTelemetryProps(props) {
467
- Object.assign(this.expectedTelemetryProps, props);
468
- return this;
469
- }
470
- }
471
- const annotationCases = {
472
- noAnnotations: {},
473
- justProps: { props: { foo: "bar", one: 1, u: undefined, t: true } },
474
- };
475
- describe("normalizeError", () => {
476
- describe("Valid Errors (Legacy and Current)", () => {
477
- for (const annotationCase of Object.keys(annotationCases)) {
478
- const annotations = annotationCases[annotationCase];
479
- it(`Valid legacy error - Patch and return (annotations: ${annotationCase})`, () => {
480
- // Arrange
481
- const errorProps = { errorType: "et1", message: "m1" };
482
- const legacyError = new TestFluidError(errorProps).withoutProperty("errorInstanceId");
483
- // Act
484
- const normalizedError = normalizeError(legacyError, annotations);
485
- // Assert
486
- assert.equal(normalizedError, legacyError, "normalize should yield the same error as passed in");
487
- assert.equal(normalizedError.errorType, "et1", "errorType should be unchanged");
488
- assert.equal(normalizedError.message, "m1", "message should be unchanged");
489
- assert.equal(normalizedError.errorInstanceId.length, 36, "should be guid-length");
490
- if (annotations.props !== undefined) {
491
- assert(legacyError.atpStub.calledWith(annotations.props), "addTelemetryProperties should have been called");
492
- }
493
- });
494
- it(`Valid Fluid Error - untouched (annotations: ${annotationCase})`, () => {
495
- // Arrange
496
- const fluidError = new TestFluidError({ errorType: "et1", message: "m1" });
497
- // We don't expect legacyError to be modified itself at all
498
- Object.freeze(fluidError);
499
- // Act
500
- const normalizedError = normalizeError(fluidError, annotations);
501
- // Assert
502
- assert(normalizedError === fluidError);
503
- if (annotations.props !== undefined) {
504
- assert(fluidError.atpStub.calledWith(annotations.props), "addTelemetryProperties should have been called");
505
- }
506
- });
507
- }
508
- it("Valid Fluid Error - stack not added if missing", () => {
509
- // Arrange
510
- const fluidError = new TestFluidError({
511
- errorType: "et1",
512
- message: "m1",
513
- }).withoutProperty("stack");
514
- // We don't expect legacyError to be modified itself at all
515
- Object.freeze(fluidError);
516
- // Act
517
- const normalizedError = normalizeError(fluidError, {});
518
- // Assert
519
- assert(normalizedError === fluidError);
520
- assert(normalizedError.stack === undefined);
521
- });
522
- it("Frozen legacy error - Throws", () => {
523
- // Arrange
524
- const errorProps = { errorType: "et1", message: "m1" };
525
- const legacyError = new TestFluidError(errorProps).withoutProperty("errorInstanceId");
526
- Object.freeze(legacyError);
527
- // Act/Assert
528
- assert.throws(() => normalizeError(legacyError, {}), /Cannot assign to read only property/);
529
- });
530
- });
531
- describe("Errors Needing Normalization", () => {
532
- class NamedError extends Error {
533
- constructor() {
534
- super(...arguments);
535
- this.name = "CoolErrorName";
536
- }
537
- }
538
- const sampleFluidError = () => new TestFluidError({
539
- errorType: "someType",
540
- message: "Hello",
541
- stack: "cool stack trace",
542
- });
543
- const typicalOutput = (message, stackHint) => new TestFluidError({
544
- errorType: "genericError",
545
- message,
546
- stack: stackHint,
547
- });
548
- const testCases = {
549
- "Fluid Error minus errorType": () => ({
550
- input: sampleFluidError().withoutProperty("errorType"),
551
- expectedOutput: typicalOutput("Hello", "<<stack from input>>"),
552
- }),
553
- "Fluid Error minus message": () => ({
554
- input: sampleFluidError().withoutProperty("message"),
555
- expectedOutput: typicalOutput("[object Object]", "<<stack from input>>"),
556
- }),
557
- "Fluid Error minus getTelemetryProperties": () => ({
558
- input: sampleFluidError().withoutProperty("getTelemetryProperties"),
559
- expectedOutput: typicalOutput("Hello", "<<stack from input>>"),
560
- }),
561
- "Fluid Error minus addTelemetryProperties": () => ({
562
- input: sampleFluidError().withoutProperty("addTelemetryProperties"),
563
- expectedOutput: typicalOutput("Hello", "<<stack from input>>"),
564
- }),
565
- "Fluid Error minus errorType (no stack)": () => ({
566
- input: sampleFluidError().withoutProperty("errorType").withoutProperty("stack"),
567
- expectedOutput: typicalOutput("Hello", "<<natural stack>>"),
568
- }),
569
- "Fluid Error minus message (no stack)": () => ({
570
- input: sampleFluidError().withoutProperty("message").withoutProperty("stack"),
571
- expectedOutput: typicalOutput("[object Object]", "<<natural stack>>"),
572
- }),
573
- "Error object": () => ({
574
- input: new NamedError("boom"),
575
- expectedOutput: typicalOutput("boom", "<<stack from input>>").withExpectedTelemetryProps({ untrustedOrigin: 1 }),
576
- }),
577
- "LoggingError": () => ({
578
- input: new LoggingError("boom"),
579
- expectedOutput: typicalOutput("boom", "<<stack from input>>"),
580
- }),
581
- "Empty object": () => ({
582
- input: {},
583
- expectedOutput: typicalOutput("[object Object]", "<<natural stack>>").withExpectedTelemetryProps({ untrustedOrigin: 1 }),
584
- }),
585
- "object with stack": () => ({
586
- input: { message: "whatever", stack: "fake stack goes here" },
587
- expectedOutput: typicalOutput("whatever", "<<stack from input>>").withExpectedTelemetryProps({ untrustedOrigin: 1 }),
588
- }),
589
- "object with non-string message and name": () => ({
590
- input: { message: 42, name: true },
591
- expectedOutput: typicalOutput("[object Object]", "<<natural stack>>").withExpectedTelemetryProps({ untrustedOrigin: 1 }),
592
- }),
593
- "nullValue": () => ({
594
- input: null,
595
- expectedOutput: typicalOutput("null", "<<natural stack>>").withExpectedTelemetryProps({ untrustedOrigin: 1 }),
596
- }),
597
- "undef": () => ({
598
- input: undefined,
599
- expectedOutput: typicalOutput("undefined", "<<natural stack>>").withExpectedTelemetryProps({ typeofError: "undefined", untrustedOrigin: 1 }),
600
- }),
601
- "false": () => ({
602
- input: false,
603
- expectedOutput: typicalOutput("false", "<<natural stack>>").withExpectedTelemetryProps({ typeofError: "boolean", untrustedOrigin: 1 }),
604
- }),
605
- "true": () => ({
606
- input: true,
607
- expectedOutput: typicalOutput("true", "<<natural stack>>").withExpectedTelemetryProps({ typeofError: "boolean", untrustedOrigin: 1 }),
608
- }),
609
- "number": () => ({
610
- input: 3.14,
611
- expectedOutput: typicalOutput("3.14", "<<natural stack>>").withExpectedTelemetryProps({ typeofError: "number", untrustedOrigin: 1 }),
612
- }),
613
- "symbol": () => ({
614
- input: Symbol("Unique"),
615
- expectedOutput: typicalOutput("Symbol(Unique)", "<<natural stack>>").withExpectedTelemetryProps({ typeofError: "symbol", untrustedOrigin: 1 }),
616
- }),
617
- "function": () => ({
618
- input: () => { },
619
- expectedOutput: typicalOutput("() => { }", "<<natural stack>>").withExpectedTelemetryProps({ typeofError: "function", untrustedOrigin: 1 }),
620
- }),
621
- "emptyArray": () => ({
622
- input: [],
623
- expectedOutput: typicalOutput("", "<<natural stack>>").withExpectedTelemetryProps({ untrustedOrigin: 1 }),
624
- }),
625
- "array": () => ({
626
- input: [1, 2, 3],
627
- expectedOutput: typicalOutput("1,2,3", "<<natural stack>>").withExpectedTelemetryProps({ untrustedOrigin: 1 }),
628
- }),
629
- };
630
- function assertMatching(actual, expected, annotations = {}, inputStack) {
631
- expected.withExpectedTelemetryProps({
632
- ...annotations.props,
633
- errorInstanceId: actual.errorInstanceId,
634
- fluidErrorCode: "-", // Present for back-compat
635
- });
636
- assertMatchingMessageAndStack(actual, expected, inputStack);
637
- assert.equal(actual.errorType, expected.errorType, "errorType should match");
638
- assert.equal(actual.name, expected.name, "name should match");
639
- assert.equal(actual.errorInstanceId.length, 36, "should be guid-length");
640
- assert.deepStrictEqual(actual.getTelemetryProperties(), expected.expectedTelemetryProps, "telemetry props should match");
641
- }
642
- function assertMatchingMessageAndStack(actual, expected, inputStack) {
643
- assert.equal(actual.message, expected.message, "message should match");
644
- const actualStack = actual.stack;
645
- assert(actualStack !== undefined, "stack should be present as a string");
646
- if (actualStack.includes("at normalizeError")) {
647
- // This indicates the stack was populated naturally by new SimpleFluidError
648
- assert.equal(expected.stack, "<<natural stack>>", "<<natural stack>> hint should be used if not overwritten");
649
- expected.withExpectedTelemetryProps({ stack: actualStack });
650
- }
651
- else {
652
- assert.equal(actualStack, inputStack, "If stack wasn't generated, it should match input stack");
653
- assert.equal(expected.stack, "<<stack from input>>", "<<stack from input>> hint should be used if using stack from input error object");
654
- expected.withExpectedTelemetryProps({ stack: inputStack });
655
- }
656
- }
657
- for (const annotationCase of Object.keys(annotationCases)) {
658
- const annotations = annotationCases[annotationCase];
659
- let doneOnceForThisAnnotationCase = false;
660
- for (const caseName of Object.keys(testCases)) {
661
- const getTestCase = testCases[caseName];
662
- if (!doneOnceForThisAnnotationCase) {
663
- doneOnceForThisAnnotationCase = true;
664
- // Each test case only differs by what stack/error are. Test the rest only once per annotation case.
665
- it(`Normalize untrusted error full validation: (${annotationCase})`, () => {
666
- // Arrange
667
- const { input, expectedOutput } = getTestCase();
668
- // Act
669
- const normalized = normalizeError(input, annotations);
670
- // Assert
671
- assert.notEqual(input, normalized, "input should have yielded a new error object");
672
- assertMatching(normalized, expectedOutput, annotations, input?.stack);
673
- if (input instanceof TestFluidError &&
674
- input.getTelemetryProperties !== undefined) {
675
- assert(input.gtpSpy.calledOnce, "input.getTelemetryProperties should have been called by normalizeError");
676
- }
677
- // Bonus
678
- normalized.addTelemetryProperties({ foo: "bar" });
679
- assert.equal(normalized.getTelemetryProperties().foo, "bar", "can add telemetry props after normalization");
680
- });
681
- }
682
- it(`Normalize untrusted error message/stack: ${caseName} (${annotationCase})`, () => {
683
- // Arrange
684
- const { input, expectedOutput } = getTestCase();
685
- // Act
686
- const normalized = normalizeError(input, annotations);
687
- // Assert
688
- assert.notEqual(input, normalized, "input should have yielded a new error object");
689
- assertMatchingMessageAndStack(normalized, expectedOutput, input?.stack);
690
- });
691
- }
692
- }
693
- });
694
- });
695
- /**
696
- * Create an error missing errorType that will not be recognized as a valid Fluid error
697
- */
698
- const createExternalError = (m) => new Error(m);
699
- /**
700
- * Create a simple valid Fluid error
701
- */
702
- const createTestError = (m) => Object.assign(new LoggingError(m), {
703
- errorType: "someErrorType",
704
- });
705
- describe("wrapError", () => {
706
- it("Copy message, stack, and props", () => {
707
- const innerError = new LoggingError("hello", { someProp: 123 });
708
- innerError.stack = "extra special stack";
709
- const newError = wrapError(innerError, createTestError);
710
- assert.equal(newError.message, innerError.message, "messages should match");
711
- assert.equal(newError.stack, innerError.stack, "stacks should match");
712
- assert.equal(newError.getTelemetryProperties().someProp, 123, "Props should be preserved");
713
- });
714
- it("Include matching errorInstanceId and innerErrorInstanceId in telemetry props", () => {
715
- const innerError = new LoggingError("hello");
716
- const newError = wrapError(innerError, createTestError);
717
- assert(newError.errorInstanceId === innerError.errorInstanceId);
718
- assert(newError.getTelemetryProperties().innerErrorInstanceId === innerError.errorInstanceId);
719
- });
720
- it("Properly set untrustedOrigin", () => {
721
- const untrustedError = createExternalError("untrusted");
722
- const singleWrapped = wrapError(untrustedError, createTestError);
723
- assert(singleWrapped.getTelemetryProperties().untrustedOrigin === 1, "wrapped external error should be 'untrustedOrigin'");
724
- const doubleWrapped = wrapError(singleWrapped, createTestError);
725
- assert(doubleWrapped.getTelemetryProperties().untrustedOrigin === 1, "doubly-wrapped external error should be 'untrustedOrigin'");
726
- const normalizedError = normalizeError(untrustedError);
727
- const wrappedNormalized = wrapError(normalizedError, createTestError);
728
- assert(wrappedNormalized.getTelemetryProperties().untrustedOrigin === 1, "normalized-then-wrapped external error should be 'untrustedOrigin'");
729
- const trustedError = createTestError("trusted");
730
- const wrappedTrusted = wrapError(trustedError, createTestError);
731
- assert(wrappedTrusted.getTelemetryProperties().untrustedOrigin === undefined, "wrapped Fluid error should not be 'untrustedOrigin'");
732
- });
733
- });
734
- describe("wrapErrorAndLog", () => {
735
- const mockLogger = new MockLogger();
736
- const innerError = new LoggingError("hello");
737
- const newError = wrapErrorAndLog(innerError, createTestError, mockLogger.toTelemetryLogger());
738
- assert(mockLogger.matchEvents([
739
- {
740
- eventName: "WrapError",
741
- wrappedByErrorInstanceId: newError.errorInstanceId,
742
- errorInstanceId: newError.errorInstanceId,
743
- error: "hello",
744
- },
745
- ]), "Expected the 'WrapError' event to be logged");
746
- });
747
- describe("Error Discovery", () => {
748
- it("isExternalError", () => {
749
- assert(isExternalError("some string"));
750
- assert(isExternalError(createExternalError("error message")));
751
- assert(isExternalError(normalizeError("normalize me but I'm still external")));
752
- assert(isExternalError(normalizeError(createExternalError("normalize me but I'm still external"))));
753
- assert(!isExternalError(createTestError("hello")));
754
- const wrappedError = wrapError("wrap me", createTestError);
755
- assert(!isExternalError(wrappedError));
756
- assert(wrappedError.getTelemetryProperties().untrustedOrigin === 1); // But it should still say untrustedOrigin
757
- assert(!isExternalError(new LoggingError("testLoggingError")));
758
- });
759
- it("isValidLegacyError", () => {
760
- const validLegacyError = {
761
- message: "testMessage",
762
- errorType: "someErrorType",
763
- getTelemetryProperties: () => { },
764
- addTelemetryProperties: () => { },
765
- };
766
- assert.strictEqual(isValidLegacyError(validLegacyError), true);
767
- assert.strictEqual(isValidLegacyError({ ...validLegacyError, message: undefined }), false);
768
- assert.strictEqual(isValidLegacyError({ ...validLegacyError, errorType: undefined }), false);
769
- assert.strictEqual(isValidLegacyError({ ...validLegacyError, getTelemetryProperties: undefined }), false);
770
- assert.strictEqual(isValidLegacyError({ ...validLegacyError, addTelemetryProperties: undefined }), false);
771
- });
772
- // I copied the old version of isFluidError here, it depends on fluidErrorCode.
773
- // I want to make sure that an error built on LoggingError that otherwise matches isFluidError
774
- // will match isFluidError in old code (e.g. when an error flows across layers)
775
- function isFluidError_old(e) {
776
- const hasTelemetryPropFunctions = (x) => typeof x?.getTelemetryProperties === "function" &&
777
- typeof x?.addTelemetryProperties === "function";
778
- return (typeof e?.errorType === "string" &&
779
- typeof e?.fluidErrorCode === "string" &&
780
- typeof e?.message === "string" &&
781
- hasErrorInstanceId(e) &&
782
- hasTelemetryPropFunctions(e));
783
- }
784
- function testFluidError(isFluidErrorImpl, isOld) {
785
- it(`isFluidError${isOld ? "_old" : ""}`, () => {
786
- assert(!isFluidErrorImpl(new Error("hello")), "Plain Error object is not a Fluid Error");
787
- assert(!isFluidErrorImpl(new LoggingError("hello")), "LoggingError is not a Fluid Error (no errorType)");
788
- assert(!isFluidErrorImpl(Object.assign(new Error("hello"), {
789
- errorType: "someErrorType",
790
- _errorInstanceId: "12345",
791
- })), "Error with errorType and errorInstanceId but without telemetry prop fns is not a Fluid Error");
792
- assert(!isFluidErrorImpl(createExternalError("hello")), "Error without errorType is not a Fluid Error");
793
- assert(!isFluidErrorImpl(Object.assign(createTestError("hello"), { _errorInstanceId: undefined })), "Valid Fluid Error with errorInstanceId removed is not a Fluid Error");
794
- assert(isFluidErrorImpl(createTestError("hello")), "Valid Fluid Error is a Fluid Error");
795
- assert.equal(!isOld, isFluidErrorImpl(Object.assign(createTestError("hello"), { fluidErrorCode: undefined })), "Old isFluidError impl should require fluidErrorCode but New should not");
796
- });
797
- }
798
- testFluidError(isFluidError, false /* isOld */);
799
- testFluidError(isFluidError_old, true /* isOld */);
800
- });
801
- //# sourceMappingURL=errorLogging.spec.js.map