()
| 41 | } from './lib'; |
| 42 | |
| 43 | async function scenarioSingleSession() { |
| 44 | scenario('single session → reaper close → cleanup'); |
| 45 | const ip = `10.${(runId >> 16) & 255}.1.${runId & 255 || 1}`; |
| 46 | |
| 47 | const first = await screenView(ip, '/a'); |
| 48 | await screenView(ip, '/b'); |
| 49 | await screenView(ip, '/c'); |
| 50 | const { deviceId, sessionId } = first; |
| 51 | check('track returned a sessionId', !!sessionId, `got '${sessionId}'`); |
| 52 | |
| 53 | const blob = await pollUntil(async () => { |
| 54 | const b = await getBlob(deviceId); |
| 55 | return b?.id === sessionId ? b : null; |
| 56 | }); |
| 57 | check('redis: session blob exists with matching id', !!blob); |
| 58 | check( |
| 59 | 'redis: device in wallclock index', |
| 60 | (await redis.zscore(wallclockKey, deviceId)) !== null |
| 61 | ); |
| 62 | check( |
| 63 | 'redis: project in active set', |
| 64 | (await redis.sismember('session:projects', PROJECT_ID)) === 1 |
| 65 | ); |
| 66 | |
| 67 | const starts = await pollUntil(async () => { |
| 68 | const c = await countByName([sessionId], 'session_start'); |
| 69 | return c > 0 ? c : null; |
| 70 | }); |
| 71 | check('clickhouse: exactly one session_start', starts === 1, `got ${starts}`); |
| 72 | const views = await pollUntil(async () => { |
| 73 | const c = await countByName([sessionId], 'screen_view'); |
| 74 | return c >= 3 ? c : null; |
| 75 | }); |
| 76 | check('clickhouse: 3 screen_views ingested', !!views, `got ${views}`); |
| 77 | |
| 78 | console.log(` …waiting ${IDLE_WAIT_MS}ms for idle window, then reaping`); |
| 79 | await sleep(IDLE_WAIT_MS); |
| 80 | await triggerReaper(); |
| 81 | |
| 82 | const ends = await pollUntil(async () => { |
| 83 | const c = await countByName([sessionId], 'session_end'); |
| 84 | return c > 0 ? c : null; |
| 85 | }); |
| 86 | check('clickhouse: exactly one session_end', ends === 1, `got ${ends}`); |
| 87 | |
| 88 | const sessionRow = await pollUntil(async () => { |
| 89 | const rows = await chQuery<{ is_bounce: boolean }>( |
| 90 | `SELECT is_bounce FROM sessions FINAL WHERE project_id = '${PROJECT_ID}' AND id = '${sessionId}' LIMIT 1` |
| 91 | ); |
| 92 | return rows[0] ?? null; |
| 93 | }); |
| 94 | check('clickhouse: sessions row present (collapsed)', !!sessionRow); |
| 95 | check( |
| 96 | 'clickhouse: session not a bounce (3 screen_views)', |
| 97 | sessionRow?.is_bounce === false || Number(sessionRow?.is_bounce) === 0, |
| 98 | `is_bounce=${sessionRow?.is_bounce}` |
| 99 | ); |
| 100 |
no test coverage detected