mirror of
https://github.com/hanzoai/insights-js.git
synced 2026-10-30 18:34:19 +00:00
# Conflicts: # .github/workflows/check-insights-major-version.yml # .github/workflows/check-posthog-major-version.yml # .github/workflows/ci-security.yaml # .github/workflows/validate-versioning.yml # AGENTS.md # AGENTS.md~upstream_main # packages/ai/src/otel/capture.ts # packages/ai/src/otel/mappers/aiSdk.ts # packages/ai/src/otel/mappers/index.ts # packages/ai/src/otel/types.ts # packages/core/src/__tests__/posthog.init.spec.ts # packages/react-native/src/PostHogProvider.tsx # packages/react/src/context/PostHogContext.ts # packages/react/src/context/PostHogProvider.tsx # tooling/eslint-plugin-insights/no-unsafe-web-global.js
185 lines
6.7 KiB
TypeScript
185 lines
6.7 KiB
TypeScript
import { expect, test } from './utils/insights-playwright-test-base'
|
|
import { start } from './utils/setup'
|
|
import { Request } from '@playwright/test'
|
|
|
|
const startOptions = {
|
|
options: {},
|
|
url: '/playground/cypress/index.html',
|
|
}
|
|
|
|
test.describe('retry queue', () => {
|
|
test('retries failed capture requests and stops after success', async ({ page, context }) => {
|
|
test.setTimeout(90000)
|
|
const captureRequests: Request[] = []
|
|
let errorResponseCount = 0
|
|
const maxErrorResponses = 3
|
|
let successSeen = false
|
|
|
|
// Mock the capture endpoint to fail initially, then succeed
|
|
await context.route('**/e/**', async (route) => {
|
|
const request = route.request()
|
|
captureRequests.push(request)
|
|
|
|
if (errorResponseCount < maxErrorResponses) {
|
|
errorResponseCount++
|
|
await route.fulfill({
|
|
status: 500,
|
|
contentType: 'text/plain',
|
|
body: 'Internal Server Error',
|
|
})
|
|
} else {
|
|
successSeen = true
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({ status: 'ok' }),
|
|
})
|
|
}
|
|
})
|
|
|
|
// Initialize Insights without pageview to avoid extra requests
|
|
await start({ ...startOptions, options: { capture_pageview: false } }, page, context)
|
|
|
|
// Capture a custom event which will initially fail
|
|
await page.evaluate(() => {
|
|
window.insights.capture('test-retry-event', { test: 'data' })
|
|
})
|
|
|
|
// Wait until we see the successful response (4th request)
|
|
await expect(async () => {
|
|
expect(successSeen).toBe(true)
|
|
}).toPass({ timeout: 50000 })
|
|
|
|
// Check that we got multiple requests
|
|
expect(captureRequests.length).toBeGreaterThanOrEqual(3)
|
|
|
|
// Verify the first request had no retry_count
|
|
const firstRequest = captureRequests[0]
|
|
expect(firstRequest.url()).not.toContain('retry_count')
|
|
|
|
// Verify retry_count increments
|
|
const retryCountMatches = captureRequests
|
|
.map((req) => {
|
|
const match = req.url().match(/retry_count=(\d+)/)
|
|
return match ? parseInt(match[1]) : null
|
|
})
|
|
.filter((count) => count !== null)
|
|
|
|
// Should see incrementing retry counts
|
|
expect(retryCountMatches.length).toBeGreaterThanOrEqual(2)
|
|
expect(retryCountMatches).toContain(1)
|
|
expect(retryCountMatches).toContain(2)
|
|
// Verify counts are actually incrementing (not stuck at 1)
|
|
const uniqueCounts = Array.from(new Set(retryCountMatches))
|
|
expect(uniqueCounts.length).toBeGreaterThan(1)
|
|
|
|
// After success, record the count and verify no more requests arrive
|
|
const requestCountAfterSuccess = captureRequests.length
|
|
await expect(async () => {
|
|
expect(captureRequests.length).toBe(requestCountAfterSuccess)
|
|
}).toPass({ timeout: 5000 })
|
|
})
|
|
|
|
test('stops retrying after 10 attempts', async ({ page, context }) => {
|
|
test.setTimeout(60000)
|
|
const captureRequests: Request[] = []
|
|
|
|
// Mock the capture endpoint to always fail
|
|
await context.route('**/e/**', async (route) => {
|
|
captureRequests.push(route.request())
|
|
await route.fulfill({
|
|
status: 500,
|
|
contentType: 'text/plain',
|
|
body: 'Internal Server Error',
|
|
})
|
|
})
|
|
|
|
// Initialize Insights without pageview
|
|
await start({ ...startOptions, options: { capture_pageview: false } }, page, context)
|
|
|
|
// Capture a custom event which will fail
|
|
await page.evaluate(() => {
|
|
window.insights.capture('test-max-retries-event', { test: 'data' })
|
|
})
|
|
|
|
// Wait for several retries
|
|
// The backoff increases exponentially: 3s, 6s, 12s, 24s, etc.
|
|
// We'll wait long enough to see at least 3-4 retries
|
|
await page.waitForTimeout(25000)
|
|
|
|
// Extract all retry counts
|
|
const retryCountMatches = captureRequests
|
|
.map((req) => {
|
|
const match = req.url().match(/retry_count=(\d+)/)
|
|
return match ? parseInt(match[1]) : null
|
|
})
|
|
.filter((count) => count !== null)
|
|
.sort((a, b) => a! - b!)
|
|
|
|
// Should have some retries but not exceed 10
|
|
expect(retryCountMatches.length).toBeGreaterThan(0)
|
|
const maxRetryCount = Math.max(...(retryCountMatches as number[]))
|
|
expect(maxRetryCount).toBeLessThanOrEqual(10)
|
|
|
|
// Verify counts are incrementing
|
|
expect(retryCountMatches).toContain(1)
|
|
expect(retryCountMatches).toContain(2)
|
|
})
|
|
|
|
test('immediately retries when coming back online', async ({ page, context }) => {
|
|
const captureRequests: Request[] = []
|
|
|
|
// Mock the capture endpoint to fail initially
|
|
let shouldSucceed = false
|
|
await context.route('**/e/**', async (route) => {
|
|
captureRequests.push(route.request())
|
|
if (shouldSucceed) {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({ status: 'ok' }),
|
|
})
|
|
} else {
|
|
await route.fulfill({
|
|
status: 500,
|
|
contentType: 'text/plain',
|
|
body: 'Internal Server Error',
|
|
})
|
|
}
|
|
})
|
|
|
|
// Initialize Insights without pageview
|
|
await start({ ...startOptions, options: { capture_pageview: false } }, page, context)
|
|
|
|
// Capture an event that will fail
|
|
await page.evaluate(() => {
|
|
window.insights.capture('test-offline-event', { test: 'data' })
|
|
})
|
|
|
|
// Wait for at least the initial request to be made
|
|
await expect(async () => {
|
|
expect(captureRequests.length).toBeGreaterThanOrEqual(1)
|
|
}).toPass({ timeout: 10000 })
|
|
|
|
// Simulate going offline
|
|
await context.setOffline(true)
|
|
|
|
// Record the count before going online
|
|
const requestsWhileOffline = captureRequests.length
|
|
|
|
// Switch to success mode and come back online
|
|
shouldSucceed = true
|
|
await context.setOffline(false)
|
|
|
|
// Trigger the online event
|
|
await page.evaluate(() => {
|
|
window.dispatchEvent(new Event('online'))
|
|
})
|
|
|
|
// Wait until we see at least one more request after coming online
|
|
await expect(async () => {
|
|
expect(captureRequests.length).toBeGreaterThan(requestsWhileOffline)
|
|
}).toPass({ timeout: 10000 })
|
|
})
|
|
})
|