Skip to content

Commit 72ace2d

Browse files
push
1 parent 508c077 commit 72ace2d

File tree

5 files changed

+355
-0
lines changed

5 files changed

+355
-0
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"flow": {
3+
"platform": "server",
4+
"destinations": {
5+
"demo": {
6+
"code": "destinationDemo"
7+
}
8+
}
9+
},
10+
"build": {
11+
"packages": {
12+
"@walkeros/collector": {
13+
"imports": ["startFlow"]
14+
},
15+
"@walkeros/destination-demo": {
16+
"imports": ["destinationDemo"]
17+
}
18+
}
19+
}
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"flow": {
3+
"platform": "web",
4+
"destinations": {
5+
"demo": {
6+
"code": "destinationDemo"
7+
}
8+
}
9+
},
10+
"build": {
11+
"packages": {
12+
"@walkeros/collector": {
13+
"imports": ["startFlow"]
14+
},
15+
"@walkeros/destination-demo": {
16+
"imports": ["destinationDemo"]
17+
}
18+
}
19+
}
20+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"data": {
3+
"title": "Test Page",
4+
"path": "/test"
5+
}
6+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name": "page view",
3+
"data": {
4+
"title": "Test Page",
5+
"path": "/test"
6+
}
7+
}
Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
/**
2+
* Push Command Tests
3+
*
4+
* Tests the push command validation and execution logic
5+
*/
6+
7+
import path from 'path';
8+
import fs from 'fs-extra';
9+
import { loadJsonFromSource } from '../../config/index.js';
10+
11+
// Test fixtures
12+
const FIXTURES_DIR = path.join(__dirname, 'fixtures');
13+
const VALID_EVENT = path.join(FIXTURES_DIR, 'events/valid-event.json');
14+
const INVALID_EVENT = path.join(FIXTURES_DIR, 'events/invalid-event.json');
15+
16+
// Helper to cast event to Record for testing
17+
const asEvent = (e: unknown) => e as Record<string, unknown>;
18+
19+
describe('Event Loading and Validation', () => {
20+
it('should load valid event from file', async () => {
21+
const event = asEvent(
22+
await loadJsonFromSource(VALID_EVENT, { name: 'event' }),
23+
);
24+
25+
expect(event).toBeDefined();
26+
expect(event).toHaveProperty('name', 'page view');
27+
expect(event).toHaveProperty('data');
28+
expect(asEvent(event.data)).toHaveProperty('title', 'Test Page');
29+
});
30+
31+
it('should load event from JSON string', async () => {
32+
const jsonString = '{"name": "product view", "data": {"id": "P123"}}';
33+
const event = asEvent(
34+
await loadJsonFromSource(jsonString, { name: 'event' }),
35+
);
36+
37+
expect(event).toBeDefined();
38+
expect(event).toHaveProperty('name', 'product view');
39+
expect(asEvent(event.data)).toHaveProperty('id', 'P123');
40+
});
41+
42+
it('should validate event has name property', async () => {
43+
const event = asEvent(
44+
await loadJsonFromSource(VALID_EVENT, { name: 'event' }),
45+
);
46+
47+
expect('name' in event).toBe(true);
48+
expect(typeof event.name).toBe('string');
49+
});
50+
51+
it('should reject event without name property', async () => {
52+
const event = asEvent(
53+
await loadJsonFromSource(INVALID_EVENT, { name: 'event' }),
54+
);
55+
56+
expect(!('name' in event)).toBe(true);
57+
});
58+
});
59+
60+
describe('Event Name Format', () => {
61+
it('should accept entity-action format with space', async () => {
62+
const event = asEvent(
63+
await loadJsonFromSource('{"name": "page view", "data": {}}', {
64+
name: 'event',
65+
}),
66+
);
67+
68+
expect(event.name).toBe('page view');
69+
expect((event.name as string).includes(' ')).toBe(true);
70+
});
71+
72+
it('should detect missing space in event name', async () => {
73+
const event = asEvent(
74+
await loadJsonFromSource('{"name": "pageview", "data": {}}', {
75+
name: 'event',
76+
}),
77+
);
78+
79+
expect(event.name).toBe('pageview');
80+
expect((event.name as string).includes(' ')).toBe(false);
81+
});
82+
83+
it('should accept multiple word event names', async () => {
84+
const event = asEvent(
85+
await loadJsonFromSource('{"name": "product detail view", "data": {}}', {
86+
name: 'event',
87+
}),
88+
);
89+
90+
expect(event.name).toBe('product detail view');
91+
expect((event.name as string).includes(' ')).toBe(true);
92+
});
93+
});
94+
95+
describe('Event Structure', () => {
96+
it('should accept event with data property', async () => {
97+
const event = asEvent(
98+
await loadJsonFromSource(
99+
'{"name": "page view", "data": {"title": "Home", "path": "/"}}',
100+
{ name: 'event' },
101+
),
102+
);
103+
104+
expect(event.data).toBeDefined();
105+
expect(typeof event.data).toBe('object');
106+
expect(asEvent(event.data).title).toBe('Home');
107+
});
108+
109+
it('should accept event without data property', async () => {
110+
const event = asEvent(
111+
await loadJsonFromSource('{"name": "page view"}', {
112+
name: 'event',
113+
}),
114+
);
115+
116+
expect(event.name).toBe('page view');
117+
expect(event.data).toBeUndefined();
118+
});
119+
120+
it('should accept event with nested property', async () => {
121+
const event = asEvent(
122+
await loadJsonFromSource(
123+
'{"name": "page view", "nested": [{"type": "category", "data": {"name": "Tech"}}]}',
124+
{ name: 'event' },
125+
),
126+
);
127+
128+
expect(event.nested).toBeDefined();
129+
expect(Array.isArray(event.nested)).toBe(true);
130+
const nested = event.nested as Array<Record<string, unknown>>;
131+
expect(nested[0].type).toBe('category');
132+
});
133+
134+
it('should accept event with user property', async () => {
135+
const event = asEvent(
136+
await loadJsonFromSource(
137+
'{"name": "page view", "user": {"id": "user123", "device": "dev456"}}',
138+
{ name: 'event' },
139+
),
140+
);
141+
142+
expect(event.user).toBeDefined();
143+
const user = asEvent(event.user);
144+
expect(user.id).toBe('user123');
145+
expect(user.device).toBe('dev456');
146+
});
147+
148+
it('should accept event with globals property', async () => {
149+
const event = asEvent(
150+
await loadJsonFromSource(
151+
'{"name": "page view", "globals": {"language": "en", "currency": "USD"}}',
152+
{ name: 'event' },
153+
),
154+
);
155+
156+
expect(event.globals).toBeDefined();
157+
const globals = asEvent(event.globals);
158+
expect(globals.language).toBe('en');
159+
expect(globals.currency).toBe('USD');
160+
});
161+
162+
it('should accept event with context property', async () => {
163+
const event = asEvent(
164+
await loadJsonFromSource(
165+
'{"name": "page view", "context": {"stage": ["checkout", 1]}}',
166+
{ name: 'event' },
167+
),
168+
);
169+
170+
expect(event.context).toBeDefined();
171+
const context = asEvent(event.context);
172+
expect(context.stage).toEqual(['checkout', 1]);
173+
});
174+
175+
it('should accept event with consent property', async () => {
176+
const event = asEvent(
177+
await loadJsonFromSource(
178+
'{"name": "page view", "consent": {"functional": true, "marketing": false}}',
179+
{ name: 'event' },
180+
),
181+
);
182+
183+
expect(event.consent).toBeDefined();
184+
const consent = asEvent(event.consent);
185+
expect(consent.functional).toBe(true);
186+
expect(consent.marketing).toBe(false);
187+
});
188+
});
189+
190+
describe('Event Type Validation', () => {
191+
it('should load non-object event (validation happens at command level)', async () => {
192+
const result = await loadJsonFromSource('"not an object"', {
193+
name: 'event',
194+
});
195+
196+
// loadJsonFromSource loads JSON but doesn't validate structure
197+
expect(result).toBe('not an object');
198+
expect(typeof result).not.toBe('object');
199+
});
200+
201+
it('should load array as event (validation happens at command level)', async () => {
202+
const result = await loadJsonFromSource('[{"name": "page view"}]', {
203+
name: 'event',
204+
});
205+
206+
expect(Array.isArray(result)).toBe(true);
207+
});
208+
209+
it('should load null as event (validation happens at command level)', async () => {
210+
const result = await loadJsonFromSource('null', { name: 'event' });
211+
212+
expect(result).toBeNull();
213+
});
214+
215+
it('should load event with non-string name (validation happens at command level)', async () => {
216+
const event = await loadJsonFromSource('{"name": 123, "data": {}}', {
217+
name: 'event',
218+
});
219+
220+
const e = event as Record<string, unknown>;
221+
expect(e.name).toBe(123);
222+
expect(typeof e.name).not.toBe('string');
223+
});
224+
});
225+
226+
describe('JSON Source Loading', () => {
227+
it('should load from file path', async () => {
228+
const event = await loadJsonFromSource(VALID_EVENT, { name: 'event' });
229+
230+
expect(event).toBeDefined();
231+
const e = event as Record<string, unknown>;
232+
expect(e.name).toBe('page view');
233+
});
234+
235+
it('should load from JSON string', async () => {
236+
const event = await loadJsonFromSource('{"name": "page view"}', {
237+
name: 'event',
238+
});
239+
240+
expect(event).toBeDefined();
241+
const e = event as Record<string, unknown>;
242+
expect(e.name).toBe('page view');
243+
});
244+
245+
it('should handle JSON with whitespace', async () => {
246+
const event = await loadJsonFromSource(
247+
`{
248+
"name": "page view",
249+
"data": {
250+
"title": "Home"
251+
}
252+
}`,
253+
{ name: 'event' },
254+
);
255+
256+
const e = event as Record<string, unknown>;
257+
expect(e.name).toBe('page view');
258+
const data = e.data as Record<string, unknown>;
259+
expect(data.title).toBe('Home');
260+
});
261+
262+
it('should reject invalid JSON', async () => {
263+
await expect(
264+
loadJsonFromSource('{invalid json}', { name: 'event' }),
265+
).rejects.toThrow();
266+
});
267+
268+
it('should fallback to event name for non-existent file paths', async () => {
269+
// Non-existent file paths that don't look like JSON are treated as event names
270+
const result = await loadJsonFromSource('/tmp/nonexistent', {
271+
name: 'event',
272+
});
273+
274+
expect(result).toEqual({ name: '/tmp/nonexistent' });
275+
});
276+
277+
it('should reject JSON-like non-existent files', async () => {
278+
// Paths ending in .json should fail if they don't exist
279+
await expect(
280+
loadJsonFromSource('{invalid', { name: 'event' }),
281+
).rejects.toThrow();
282+
});
283+
});
284+
285+
describe('Fixture Files', () => {
286+
it('should have valid-event.json fixture', async () => {
287+
const exists = await fs.pathExists(VALID_EVENT);
288+
expect(exists).toBe(true);
289+
290+
const content = await fs.readJson(VALID_EVENT);
291+
expect(content).toHaveProperty('name');
292+
expect(content.name).toContain(' '); // Should follow entity-action format
293+
});
294+
295+
it('should have invalid-event.json fixture', async () => {
296+
const exists = await fs.pathExists(INVALID_EVENT);
297+
expect(exists).toBe(true);
298+
299+
const content = await fs.readJson(INVALID_EVENT);
300+
expect(content).not.toHaveProperty('name');
301+
});
302+
});

0 commit comments

Comments
 (0)