node-avo-inspector 1.1.0 → 1.2.0-alpha.1
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/.claude/scheduled_tasks.lock +1 -0
- package/.claude/settings.local.json +11 -0
- package/.claude/worktrees/release-1.1.1/LICENSE +21 -0
- package/.claude/worktrees/release-1.1.1/README.md +69 -0
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-js/index.js +30 -0
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-js/package.json +15 -0
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-js/yarn.lock +6 -0
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-ts/dist/index.js +34 -0
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-ts/index.ts +30 -0
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-ts/package.json +18 -0
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-ts/tsconfig.json +15 -0
- package/.claude/worktrees/release-1.1.1/examples/node-inspector-ts/yarn.lock +11 -0
- package/.claude/worktrees/release-1.1.1/package-lock.json +4039 -0
- package/.claude/worktrees/release-1.1.1/package.json +48 -0
- package/.claude/worktrees/release-1.1.1/reason/AvoInspector.rei +11 -0
- package/.claude/worktrees/release-1.1.1/reason/NodeAvoInspector.re +27 -0
- package/.claude/worktrees/release-1.1.1/src/AvoDeduplicator.ts +146 -0
- package/.claude/worktrees/release-1.1.1/src/AvoEncryption.ts +90 -0
- package/.claude/worktrees/release-1.1.1/src/AvoGuid.ts +9 -0
- package/.claude/worktrees/release-1.1.1/src/AvoInspector.ts +383 -0
- package/.claude/worktrees/release-1.1.1/src/AvoInspectorEnv.ts +8 -0
- package/.claude/worktrees/release-1.1.1/src/AvoNetworkCallsHandler.ts +267 -0
- package/.claude/worktrees/release-1.1.1/src/AvoSchemaParser.ts +115 -0
- package/.claude/worktrees/release-1.1.1/src/AvoStreamId.ts +16 -0
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestAvoEncryption.ts +515 -0
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestAvoEventSpecCache.ts +242 -0
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestAvoEventSpecFetcher.ts +241 -0
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestAvoInspector.ts +360 -0
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestAvoStreamId.ts +72 -0
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestDeduplicator.ts +191 -0
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestEventValidator.ts +231 -0
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestNetworkCallsHandler.ts +179 -0
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestNetworkCallsHandlerPublic.ts +208 -0
- package/.claude/worktrees/release-1.1.1/src/__tests__/TestParsing.ts +153 -0
- package/.claude/worktrees/release-1.1.1/src/__tests__/constants.ts +63 -0
- package/.claude/worktrees/release-1.1.1/src/eventSpec/AvoEventSpecCache.ts +109 -0
- package/.claude/worktrees/release-1.1.1/src/eventSpec/AvoEventSpecFetchTypes.ts +47 -0
- package/.claude/worktrees/release-1.1.1/src/eventSpec/AvoEventSpecFetcher.ts +106 -0
- package/.claude/worktrees/release-1.1.1/src/eventSpec/EventValidator.ts +74 -0
- package/.claude/worktrees/release-1.1.1/src/index.ts +2 -0
- package/.claude/worktrees/release-1.1.1/src/utils.ts +49 -0
- package/.claude/worktrees/release-1.1.1/tsconfig.json +13 -0
- package/.claude/worktrees/release-1.1.1/yarn.lock +2310 -0
- package/.github/workflows/claude.yml +55 -0
- package/dist/AvoDeduplicator.d.ts +4 -3
- package/dist/AvoDeduplicator.js +25 -24
- package/dist/AvoEncryption.d.ts +26 -0
- package/dist/AvoEncryption.js +77 -0
- package/dist/AvoInspector.d.ts +34 -3
- package/dist/AvoInspector.js +196 -40
- package/dist/AvoNetworkCallsHandler.d.ts +39 -13
- package/dist/AvoNetworkCallsHandler.js +127 -33
- package/dist/AvoStreamId.d.ts +5 -0
- package/dist/AvoStreamId.js +15 -0
- package/dist/__tests__/TestAvoEncryption.d.ts +1 -0
- package/dist/__tests__/TestAvoEncryption.js +288 -0
- package/dist/__tests__/TestAvoEventSpecCache.d.ts +1 -0
- package/dist/__tests__/TestAvoEventSpecCache.js +194 -0
- package/dist/__tests__/TestAvoEventSpecFetcher.d.ts +1 -0
- package/dist/__tests__/TestAvoEventSpecFetcher.js +219 -0
- package/dist/__tests__/TestAvoInspector.js +76 -2
- package/dist/__tests__/TestAvoStreamId.d.ts +1 -0
- package/dist/__tests__/TestAvoStreamId.js +55 -0
- package/dist/__tests__/TestDeduplicator.js +3 -0
- package/dist/__tests__/TestEventValidator.d.ts +1 -0
- package/dist/__tests__/TestEventValidator.js +196 -0
- package/dist/__tests__/TestNetworkCallsHandler.js +37 -10
- package/dist/__tests__/TestNetworkCallsHandlerPublic.js +93 -58
- package/dist/eventSpec/AvoEventSpecCache.d.ts +19 -0
- package/dist/eventSpec/AvoEventSpecCache.js +100 -0
- package/dist/eventSpec/AvoEventSpecFetchTypes.d.ts +41 -0
- package/dist/eventSpec/AvoEventSpecFetchTypes.js +5 -0
- package/dist/eventSpec/AvoEventSpecFetcher.d.ts +25 -0
- package/dist/eventSpec/AvoEventSpecFetcher.js +159 -0
- package/dist/eventSpec/EventValidator.d.ts +15 -0
- package/dist/eventSpec/EventValidator.js +53 -0
- package/package.json +4 -2
- package/src/AvoDeduplicator.ts +31 -27
- package/src/AvoEncryption.ts +90 -0
- package/src/AvoInspector.ts +278 -54
- package/src/AvoNetworkCallsHandler.ts +205 -45
- package/src/AvoStreamId.ts +16 -0
- package/src/__tests__/TestAvoEncryption.ts +515 -0
- package/src/__tests__/TestAvoEventSpecCache.ts +242 -0
- package/src/__tests__/TestAvoEventSpecFetcher.ts +279 -0
- package/src/__tests__/TestAvoInspector.ts +114 -2
- package/src/__tests__/TestAvoStreamId.ts +72 -0
- package/src/__tests__/TestDeduplicator.ts +4 -0
- package/src/__tests__/TestEventValidator.ts +231 -0
- package/src/__tests__/TestNetworkCallsHandler.ts +82 -15
- package/src/__tests__/TestNetworkCallsHandlerPublic.ts +134 -63
- package/src/eventSpec/AvoEventSpecCache.ts +119 -0
- package/src/eventSpec/AvoEventSpecFetchTypes.ts +47 -0
- package/src/eventSpec/AvoEventSpecFetcher.ts +186 -0
- package/src/eventSpec/EventValidator.ts +74 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { AvoStreamId } from "../AvoStreamId";
|
|
2
|
+
|
|
3
|
+
describe("AvoStreamId", () => {
|
|
4
|
+
let consoleWarnSpy: jest.SpyInstance;
|
|
5
|
+
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation(() => {});
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
jest.restoreAllMocks();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("streamId getter returns empty string by default", () => {
|
|
15
|
+
const avoStreamId = new AvoStreamId();
|
|
16
|
+
expect(avoStreamId.streamId).toBe("");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("streamId getter returns the value passed to constructor", () => {
|
|
20
|
+
const avoStreamId = new AvoStreamId("my-stream-id");
|
|
21
|
+
expect(avoStreamId.streamId).toBe("my-stream-id");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("streamId containing ':' logs a warning", () => {
|
|
25
|
+
const avoStreamId = new AvoStreamId("invalid:stream:id");
|
|
26
|
+
const id = avoStreamId.streamId;
|
|
27
|
+
|
|
28
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
|
29
|
+
"[Avo Inspector] Warning: streamId contains ':' which is not supported"
|
|
30
|
+
);
|
|
31
|
+
expect(id).toBe("invalid:stream:id");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("streamId without ':' does not log a warning", () => {
|
|
35
|
+
const avoStreamId = new AvoStreamId("valid-stream-id");
|
|
36
|
+
const id = avoStreamId.streamId;
|
|
37
|
+
|
|
38
|
+
expect(consoleWarnSpy).not.toHaveBeenCalled();
|
|
39
|
+
expect(id).toBe("valid-stream-id");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("empty streamId does not log a warning", () => {
|
|
43
|
+
const avoStreamId = new AvoStreamId("");
|
|
44
|
+
const id = avoStreamId.streamId;
|
|
45
|
+
|
|
46
|
+
expect(consoleWarnSpy).not.toHaveBeenCalled();
|
|
47
|
+
expect(id).toBe("");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("explicit undefined arg returns empty string and does not warn", () => {
|
|
51
|
+
const avoStreamId = new AvoStreamId(undefined);
|
|
52
|
+
const id = avoStreamId.streamId;
|
|
53
|
+
|
|
54
|
+
expect(consoleWarnSpy).not.toHaveBeenCalled();
|
|
55
|
+
expect(id).toBe("");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("warning is only logged once per instance, not on every .streamId access", () => {
|
|
59
|
+
const avoStreamId = new AvoStreamId("bad:id");
|
|
60
|
+
|
|
61
|
+
// The warning is logged in the constructor, so it's already been called once
|
|
62
|
+
expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
|
|
63
|
+
|
|
64
|
+
// Access streamId multiple times
|
|
65
|
+
avoStreamId.streamId;
|
|
66
|
+
avoStreamId.streamId;
|
|
67
|
+
avoStreamId.streamId;
|
|
68
|
+
|
|
69
|
+
// Should still be only one warning total
|
|
70
|
+
expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -3,6 +3,10 @@ import { deepEquals } from "../utils";
|
|
|
3
3
|
import { AvoInspector } from "../AvoInspector";
|
|
4
4
|
import { defaultOptions } from "./constants";
|
|
5
5
|
|
|
6
|
+
jest
|
|
7
|
+
.useFakeTimers()
|
|
8
|
+
.setSystemTime(new Date('2020-01-01'));
|
|
9
|
+
|
|
6
10
|
describe("Deduplicator", () => {
|
|
7
11
|
const deduplicator = new AvoDeduplicator();
|
|
8
12
|
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { EventValidator } from "../eventSpec/EventValidator";
|
|
2
|
+
import { EventSpec, PropertyConstraint, PropertyValidationResult } from "../eventSpec/AvoEventSpecFetchTypes";
|
|
3
|
+
|
|
4
|
+
// We need to mock safe-regex2 for some tests
|
|
5
|
+
jest.mock("safe-regex2", () => {
|
|
6
|
+
const actual = jest.requireActual("safe-regex2");
|
|
7
|
+
return {
|
|
8
|
+
__esModule: true,
|
|
9
|
+
default: actual.default || actual,
|
|
10
|
+
_mockable: true,
|
|
11
|
+
};
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe("EventValidator", () => {
|
|
15
|
+
let validator: EventValidator;
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
validator = new EventValidator();
|
|
19
|
+
jest.spyOn(console, "warn").mockImplementation(() => {});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
jest.restoreAllMocks();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe("type validation", () => {
|
|
27
|
+
test("passes when property types match spec", () => {
|
|
28
|
+
const spec: EventSpec = {
|
|
29
|
+
eventName: "click",
|
|
30
|
+
properties: [
|
|
31
|
+
{ propertyName: "target", propertyType: "string" },
|
|
32
|
+
{ propertyName: "count", propertyType: "int" },
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
const eventProperties = [
|
|
36
|
+
{ propertyName: "target", propertyType: "string" },
|
|
37
|
+
{ propertyName: "count", propertyType: "int" },
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
const results = validator.validate(spec, eventProperties, "evt-1");
|
|
41
|
+
// All properties passed type check, so failedEventIds should be empty
|
|
42
|
+
expect(results.every((r: PropertyValidationResult) => r.failedEventIds.length === 0)).toBe(true);
|
|
43
|
+
// Bandwidth optimization: passedEventIds cleared because passed >= failed for each property
|
|
44
|
+
expect(results.every((r: PropertyValidationResult) => r.passedEventIds.length === 0)).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("fails when property type does not match spec", () => {
|
|
48
|
+
const spec: EventSpec = {
|
|
49
|
+
eventName: "click",
|
|
50
|
+
properties: [{ propertyName: "count", propertyType: "int" }],
|
|
51
|
+
};
|
|
52
|
+
const eventProperties = [
|
|
53
|
+
{ propertyName: "count", propertyType: "string" },
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
const results = validator.validate(spec, eventProperties, "evt-2");
|
|
57
|
+
const countResult = results.find((r: PropertyValidationResult) => r.propertyName === "count");
|
|
58
|
+
expect(countResult?.failedEventIds).toContain("evt-2");
|
|
59
|
+
expect(countResult?.passedEventIds).not.toContain("evt-2");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("fails for missing required properties", () => {
|
|
63
|
+
const spec: EventSpec = {
|
|
64
|
+
eventName: "click",
|
|
65
|
+
properties: [
|
|
66
|
+
{ propertyName: "target", propertyType: "string" },
|
|
67
|
+
{ propertyName: "count", propertyType: "int" },
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
const eventProperties = [
|
|
71
|
+
{ propertyName: "target", propertyType: "string" },
|
|
72
|
+
// count is missing
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
const results = validator.validate(spec, eventProperties, "evt-3");
|
|
76
|
+
const countResult = results.find((r: PropertyValidationResult) => r.propertyName === "count");
|
|
77
|
+
expect(countResult?.failedEventIds).toContain("evt-3");
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe("regex validation", () => {
|
|
82
|
+
test("passes when value matches safe regex pattern (type match)", () => {
|
|
83
|
+
const spec: EventSpec = {
|
|
84
|
+
eventName: "navigate",
|
|
85
|
+
properties: [
|
|
86
|
+
{
|
|
87
|
+
propertyName: "url",
|
|
88
|
+
propertyType: "string",
|
|
89
|
+
regex: "^https://",
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
};
|
|
93
|
+
const eventProperties = [
|
|
94
|
+
{ propertyName: "url", propertyType: "string", propertyValue: "https://example.com" },
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
const results = validator.validate(spec, eventProperties, "evt-4");
|
|
98
|
+
const urlResult = results.find((r: PropertyValidationResult) => r.propertyName === "url");
|
|
99
|
+
// Type matches and regex matches, property passed. Bandwidth optimization: passedEventIds cleared
|
|
100
|
+
// because passed count (1) >= failed count (0)
|
|
101
|
+
expect(urlResult?.failedEventIds).toEqual([]);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("fails when value does not match safe regex pattern", () => {
|
|
105
|
+
const spec: EventSpec = {
|
|
106
|
+
eventName: "navigate",
|
|
107
|
+
properties: [
|
|
108
|
+
{
|
|
109
|
+
propertyName: "url",
|
|
110
|
+
propertyType: "string",
|
|
111
|
+
regex: "^https://",
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
const eventProperties = [
|
|
116
|
+
{ propertyName: "url", propertyType: "string", propertyValue: "http://example.com" },
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
const results = validator.validate(spec, eventProperties, "evt-regex-fail");
|
|
120
|
+
const urlResult = results.find((r: PropertyValidationResult) => r.propertyName === "url");
|
|
121
|
+
expect(urlResult?.failedEventIds).toContain("evt-regex-fail");
|
|
122
|
+
expect(urlResult?.passedEventIds).not.toContain("evt-regex-fail");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("logs warning and passes type check when regex syntax is invalid", () => {
|
|
126
|
+
const spec: EventSpec = {
|
|
127
|
+
eventName: "filter",
|
|
128
|
+
properties: [
|
|
129
|
+
{
|
|
130
|
+
propertyName: "pattern",
|
|
131
|
+
propertyType: "string",
|
|
132
|
+
regex: "[invalid",
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
};
|
|
136
|
+
const eventProperties = [
|
|
137
|
+
{ propertyName: "pattern", propertyType: "string", propertyValue: "anything" },
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
const results = validator.validate(spec, eventProperties, "evt-bad-regex");
|
|
141
|
+
const patternResult = results.find((r: PropertyValidationResult) => r.propertyName === "pattern");
|
|
142
|
+
// Invalid regex is caught, property still passes type check
|
|
143
|
+
expect(patternResult?.failedEventIds).toEqual([]);
|
|
144
|
+
expect(console.warn).toHaveBeenCalledWith(
|
|
145
|
+
expect.stringContaining("[Avo Inspector] Warning")
|
|
146
|
+
);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("skips unsafe regex patterns with warning", () => {
|
|
150
|
+
const spec: EventSpec = {
|
|
151
|
+
eventName: "search",
|
|
152
|
+
properties: [
|
|
153
|
+
{
|
|
154
|
+
propertyName: "query",
|
|
155
|
+
propertyType: "string",
|
|
156
|
+
// Catastrophic backtracking pattern
|
|
157
|
+
regex: "(a+)+$",
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
};
|
|
161
|
+
const eventProperties = [
|
|
162
|
+
{ propertyName: "query", propertyType: "string", propertyValue: "aaa" },
|
|
163
|
+
];
|
|
164
|
+
|
|
165
|
+
const results = validator.validate(spec, eventProperties, "evt-5");
|
|
166
|
+
// Unsafe regex should be skipped, property still passes type check
|
|
167
|
+
const queryResult = results.find((r: PropertyValidationResult) => r.propertyName === "query");
|
|
168
|
+
// Bandwidth optimization: passedEventIds cleared because passed count >= failed count
|
|
169
|
+
expect(queryResult?.failedEventIds).toEqual([]);
|
|
170
|
+
expect(console.warn).toHaveBeenCalledWith(
|
|
171
|
+
expect.stringContaining("[Avo Inspector] Warning")
|
|
172
|
+
);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
describe("extra/unexpected properties", () => {
|
|
177
|
+
test("ignores properties present in event but not in the spec", () => {
|
|
178
|
+
const spec: EventSpec = {
|
|
179
|
+
eventName: "click",
|
|
180
|
+
properties: [
|
|
181
|
+
{ propertyName: "target", propertyType: "string" },
|
|
182
|
+
],
|
|
183
|
+
};
|
|
184
|
+
const eventProperties = [
|
|
185
|
+
{ propertyName: "target", propertyType: "string" },
|
|
186
|
+
{ propertyName: "extraProp", propertyType: "int" },
|
|
187
|
+
{ propertyName: "anotherExtra", propertyType: "boolean" },
|
|
188
|
+
];
|
|
189
|
+
|
|
190
|
+
const results = validator.validate(spec, eventProperties, "evt-extra");
|
|
191
|
+
// Only spec properties get results — extra properties are not reported
|
|
192
|
+
expect(results).toHaveLength(1);
|
|
193
|
+
expect(results[0].propertyName).toBe("target");
|
|
194
|
+
expect(results.find((r: PropertyValidationResult) => r.propertyName === "extraProp")).toBeUndefined();
|
|
195
|
+
expect(results.find((r: PropertyValidationResult) => r.propertyName === "anotherExtra")).toBeUndefined();
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe("bandwidth optimization", () => {
|
|
200
|
+
test("returns passedEventIds only when strictly smaller than failedEventIds", () => {
|
|
201
|
+
const spec: EventSpec = {
|
|
202
|
+
eventName: "click",
|
|
203
|
+
properties: [{ propertyName: "target", propertyType: "string" }],
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// Single pass
|
|
207
|
+
const passResults = validator.validate(
|
|
208
|
+
spec,
|
|
209
|
+
[{ propertyName: "target", propertyType: "string" }],
|
|
210
|
+
"evt-pass"
|
|
211
|
+
);
|
|
212
|
+
const passResult = passResults.find((r: PropertyValidationResult) => r.propertyName === "target");
|
|
213
|
+
|
|
214
|
+
// When there's only passed and no failed, passedEventIds count (1) > failedEventIds count (0)
|
|
215
|
+
// So passedEventIds is NOT strictly smaller - it should be returned
|
|
216
|
+
// But the optimization says: return passedEventIds ONLY when strictly smaller than failedEventIds
|
|
217
|
+
// So when passedEventIds.length >= failedEventIds.length, we clear passedEventIds
|
|
218
|
+
expect(passResult?.passedEventIds).toEqual([]);
|
|
219
|
+
|
|
220
|
+
// Single fail
|
|
221
|
+
const failResults = validator.validate(
|
|
222
|
+
spec,
|
|
223
|
+
[{ propertyName: "target", propertyType: "int" }],
|
|
224
|
+
"evt-fail"
|
|
225
|
+
);
|
|
226
|
+
const failResult = failResults.find((r: PropertyValidationResult) => r.propertyName === "target");
|
|
227
|
+
// failedEventIds has 1, passedEventIds has 0 -> passedEventIds is strictly smaller, return it
|
|
228
|
+
expect(failResult?.failedEventIds).toContain("evt-fail");
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
});
|
|
@@ -15,7 +15,6 @@ describe("NetworkCallsHandler", () => {
|
|
|
15
15
|
let networkHandler: AvoNetworkCallsHandler;
|
|
16
16
|
let baseBody: BaseBody;
|
|
17
17
|
|
|
18
|
-
const customCallback = jest.fn();
|
|
19
18
|
const now = new Date();
|
|
20
19
|
|
|
21
20
|
beforeAll(() => {
|
|
@@ -43,8 +42,9 @@ describe("NetworkCallsHandler", () => {
|
|
|
43
42
|
libPlatform: "node",
|
|
44
43
|
messageId: mockedReturns.GUID,
|
|
45
44
|
trackingId: "",
|
|
45
|
+
sessionId: "",
|
|
46
|
+
anonymousId: "",
|
|
46
47
|
createdAt: new Date().toISOString(),
|
|
47
|
-
sessionId: mockedReturns.SESSION_ID,
|
|
48
48
|
samplingRate: 1.0,
|
|
49
49
|
};
|
|
50
50
|
});
|
|
@@ -53,23 +53,12 @@ describe("NetworkCallsHandler", () => {
|
|
|
53
53
|
jest.clearAllMocks();
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
-
test("bodyForSessionStartedCall returns base body + session started body used for session started", () => {
|
|
57
|
-
const body = networkHandler.bodyForSessionStartedCall(
|
|
58
|
-
mockedReturns.SESSION_ID
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
expect(body).toEqual({
|
|
62
|
-
...baseBody,
|
|
63
|
-
type: "sessionStarted",
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
56
|
test("bodyForEventSchemaCall returns base body + event schema used for event sending from non Codegen", () => {
|
|
68
57
|
const eventName = "event name";
|
|
69
58
|
const eventProperties = [{ propertyName: "prop0", propertyType: "string" }];
|
|
70
59
|
|
|
71
60
|
const body = networkHandler.bodyForEventSchemaCall(
|
|
72
|
-
|
|
61
|
+
"",
|
|
73
62
|
eventName,
|
|
74
63
|
eventProperties,
|
|
75
64
|
null,
|
|
@@ -78,6 +67,7 @@ describe("NetworkCallsHandler", () => {
|
|
|
78
67
|
|
|
79
68
|
expect(body).toEqual({
|
|
80
69
|
...baseBody,
|
|
70
|
+
anonymousId: "",
|
|
81
71
|
type: "event",
|
|
82
72
|
eventName,
|
|
83
73
|
eventProperties,
|
|
@@ -94,7 +84,7 @@ describe("NetworkCallsHandler", () => {
|
|
|
94
84
|
const eventProperties = [{ propertyName: "prop0", propertyType: "string" }];
|
|
95
85
|
|
|
96
86
|
const body = networkHandler.bodyForEventSchemaCall(
|
|
97
|
-
|
|
87
|
+
"",
|
|
98
88
|
eventName,
|
|
99
89
|
eventProperties,
|
|
100
90
|
eventId,
|
|
@@ -103,6 +93,7 @@ describe("NetworkCallsHandler", () => {
|
|
|
103
93
|
|
|
104
94
|
expect(body).toEqual({
|
|
105
95
|
...baseBody,
|
|
96
|
+
anonymousId: "",
|
|
106
97
|
type: "event",
|
|
107
98
|
eventName,
|
|
108
99
|
eventProperties,
|
|
@@ -111,4 +102,80 @@ describe("NetworkCallsHandler", () => {
|
|
|
111
102
|
eventHash,
|
|
112
103
|
});
|
|
113
104
|
});
|
|
105
|
+
|
|
106
|
+
test("bodyForEventSchemaCall uses streamId as anonymousId", () => {
|
|
107
|
+
const eventName = "event name";
|
|
108
|
+
const streamId = "user-123";
|
|
109
|
+
const eventProperties = [{ propertyName: "prop0", propertyType: "string" }];
|
|
110
|
+
|
|
111
|
+
const body = networkHandler.bodyForEventSchemaCall(
|
|
112
|
+
streamId,
|
|
113
|
+
eventName,
|
|
114
|
+
eventProperties,
|
|
115
|
+
null,
|
|
116
|
+
null
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
expect(body.anonymousId).toBe("user-123");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("bodyForEventSchemaCall with rawEventProperties passes them through (no encryption in dev without key)", () => {
|
|
123
|
+
const eventName = "event name";
|
|
124
|
+
const eventProperties = [{ propertyName: "prop0", propertyType: "string" }];
|
|
125
|
+
const rawEventProperties = { prop0: "hello" };
|
|
126
|
+
|
|
127
|
+
const body = networkHandler.bodyForEventSchemaCall(
|
|
128
|
+
"",
|
|
129
|
+
eventName,
|
|
130
|
+
eventProperties,
|
|
131
|
+
null,
|
|
132
|
+
null,
|
|
133
|
+
rawEventProperties
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
// Without a publicEncryptionKey, encryption is not triggered,
|
|
137
|
+
// so eventProperties should be the plain schema (not encrypted)
|
|
138
|
+
expect(body.eventProperties).toEqual(eventProperties);
|
|
139
|
+
expect(body.type).toBe("event");
|
|
140
|
+
expect(body.eventName).toBe(eventName);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("base body includes publicEncryptionKey when handler is constructed with one", () => {
|
|
144
|
+
const handlerWithKey = new AvoNetworkCallsHandler(
|
|
145
|
+
apiKey,
|
|
146
|
+
env,
|
|
147
|
+
"",
|
|
148
|
+
version,
|
|
149
|
+
inspectorVersion,
|
|
150
|
+
"test-public-key-abc"
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const eventName = "event name";
|
|
154
|
+
const eventProperties = [{ propertyName: "prop0", propertyType: "string" }];
|
|
155
|
+
|
|
156
|
+
const body = handlerWithKey.bodyForEventSchemaCall(
|
|
157
|
+
"",
|
|
158
|
+
eventName,
|
|
159
|
+
eventProperties,
|
|
160
|
+
null,
|
|
161
|
+
null
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
expect(body.publicEncryptionKey).toBe("test-public-key-abc");
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("base body does not include publicEncryptionKey when handler is constructed without one", () => {
|
|
168
|
+
const eventName = "event name";
|
|
169
|
+
const eventProperties = [{ propertyName: "prop0", propertyType: "string" }];
|
|
170
|
+
|
|
171
|
+
const body = networkHandler.bodyForEventSchemaCall(
|
|
172
|
+
"",
|
|
173
|
+
eventName,
|
|
174
|
+
eventProperties,
|
|
175
|
+
null,
|
|
176
|
+
null
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
expect(body.publicEncryptionKey).toBeUndefined();
|
|
180
|
+
});
|
|
114
181
|
});
|