diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..2951a88b --- /dev/null +++ b/.envrc @@ -0,0 +1,2 @@ +use flake +layout python diff --git a/.gitignore b/.gitignore index 48fb2d59..1ed296e9 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,5 @@ Thumbs.db # E2E test generated files **/e2e-tests/**/.tusk/logs/ **/e2e-tests/**/.tusk/traces/ + +.direnv diff --git a/benchmarks/.gitignore b/benchmarks/.gitignore deleted file mode 100644 index dd9b6bc2..00000000 --- a/benchmarks/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -results -profile/results -.benchmark-traces diff --git a/benchmarks/README.md b/benchmarks/README.md deleted file mode 100644 index 63c726e4..00000000 --- a/benchmarks/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Benchmarks - -These are some benchmarks. -Each benchmark is actually just a AVA test suite that uses tinybench for -measurements and formatting. -At the start of each test suite, we launch a server in `server/test-server.ts`. -Interestingly, it is this server that does all the work rather than the actual -benchmark files. - -## Usage - -You can run all tests and get a summary with -``` -npm run test:bench -``` -You can disable memory monitoring with -``` -BENCHMARK_ENABLE_MEMORY=false npm run test:bench -``` -Memory monitoring introduces a non-trivial impact to CPU, roughly around 10% or -so. - -The results are placed in the `results` folder, and there's a -`compare-benchmarks.ts` script that is run automatically and summarizes the data -in a markdown table; if you want to process the data further this can be a good -starting point. diff --git a/benchmarks/bench/common.ts b/benchmarks/bench/common.ts deleted file mode 100644 index 648f6ccd..00000000 --- a/benchmarks/bench/common.ts +++ /dev/null @@ -1,167 +0,0 @@ -import test from "ava"; -import { Bench, hrtimeNow } from "tinybench"; -import { TestServer } from "../server/test-server"; -import { createBenchmarkRunResult, persistBenchmarkResult } from "./result-utils"; -import { ResourceMonitor } from "./resource-monitor"; - -let server: TestServer; -let serverUrl: string; - -function main(testName: string = "SDK Active") { - test.before(async () => { - server = new TestServer(); - const info = await server.start(); - serverUrl = info.url; - }); - - test.after.always(async () => { - if (server) { - await server.stop(); - } - }); - - test.serial(testName, async (t) => { - t.timeout(600_000); - - const enableMemoryTracking = process.env.BENCHMARK_ENABLE_MEMORY !== "false"; - const resourceMonitor = new ResourceMonitor({ - intervalMs: 100, - enableMemoryTracking, - }); - - const bench = new Bench({ - time: 10000, - warmup: false, - now: hrtimeNow, - }); - - let currentTaskName: string | null = null; - - bench.add( - `High CPU: POST /api/compute-hash (${testName})`, - async () => { - const response = await fetch(`${serverUrl}/api/compute-hash`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ data: "sensitive-data-to-hash", iterations: 1000 }), - }); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - await response.json(); - }, - { - beforeAll: () => { - const taskName = `High CPU: POST /api/compute-hash (${testName})`; - console.log("Task starting:", taskName); - resourceMonitor.startTask(taskName); - currentTaskName = taskName; - }, - afterAll: () => { - console.log("Task ending:", currentTaskName); - resourceMonitor.endTask(); - currentTaskName = null; - }, - }, - ); - - bench.add( - `High IO, Low CPU: POST /api/io-bound (${testName})`, - async () => { - const response = await fetch(`${serverUrl}/api/io-bound`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ jobs: 5, delayMs: 5 }), - }); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - await response.json(); - }, - { - beforeAll: () => { - const taskName = `High IO, Low CPU: POST /api/io-bound (${testName})`; - console.log("Task starting:", taskName); - resourceMonitor.startTask(taskName); - currentTaskName = taskName; - }, - afterAll: () => { - console.log("Task ending:", currentTaskName); - resourceMonitor.endTask(); - currentTaskName = null; - }, - }, - ); - - const transformEndpoints = [ - { - path: "/api/auth/login", - method: "POST" as const, - body: { email: "user@example.com", password: "super-secret-password-123" }, - }, - { - path: "/api/users", - method: "POST" as const, - body: { - username: "testuser", - email: "test@example.com", - ssn: "123-45-6789", - creditCard: "4111-1111-1111-1111", - }, - }, - ]; - - let endpointIndex = 0; - bench.add( - `Transform endpoints (${testName})`, - async () => { - const endpoint = transformEndpoints[endpointIndex % transformEndpoints.length]; - endpointIndex++; - - const response = await fetch(`${serverUrl}${endpoint.path}`, { - method: endpoint.method, - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(endpoint.body), - }); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - await response.json(); - }, - { - beforeAll: () => { - const taskName = `Transform endpoints (${testName})`; - console.log("Task starting:", taskName); - resourceMonitor.startTask(taskName); - currentTaskName = taskName; - }, - afterAll: () => { - console.log("Task ending:", currentTaskName); - resourceMonitor.endTask(); - currentTaskName = null; - }, - }, - ); - - resourceMonitor.start(); - const runStartedAt = Date.now(); - await bench.run(); - const benchmarkDurationMs = Date.now() - runStartedAt; - - resourceMonitor.stop(); - - const label = process.env.BENCHMARK_RESULT_LABEL ?? "benchmark"; - const benchmarkResult = createBenchmarkRunResult( - bench, - resourceMonitor, - benchmarkDurationMs, - label, - ); - const outputPath = persistBenchmarkResult(benchmarkResult); - console.log(`Benchmark results saved to ${outputPath}`); - - t.pass(); - }); -} - -export default main; diff --git a/benchmarks/bench/resource-monitor.ts b/benchmarks/bench/resource-monitor.ts deleted file mode 100644 index 44d32cb7..00000000 --- a/benchmarks/bench/resource-monitor.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { TaskResourceStats } from "./result-utils"; - -interface ResourceMonitorOptions { - intervalMs?: number; - enableMemoryTracking?: boolean; -} - -interface TaskStats { - memory: { - rssSum: number; - rssMax: number; - }; - startCpuUsage: NodeJS.CpuUsage; - startTime: number; - count: number; -} - -interface CompletedTaskStats { - resourceStats: TaskResourceStats; - endTime: number; -} - -export class ResourceMonitor { - private taskStats: Map = new Map(); - private completedTaskStats: Map = new Map(); - private intervalId: NodeJS.Timeout | null = null; - private currentTaskName: string | null = null; - private currentTaskStats: TaskStats | null = null; - private isRunning: boolean = false; - private options: Required; - - constructor(options: ResourceMonitorOptions = {}) { - this.options = { - intervalMs: options.intervalMs ?? 100, - enableMemoryTracking: options.enableMemoryTracking ?? true, - }; - } - - start(): void { - this.isRunning = true; - - if (this.options.enableMemoryTracking) { - this.intervalId = setInterval(() => { - this.collectMemorySample(); - }, this.options.intervalMs); - } - } - - stop(): void { - this.isRunning = false; - if (this.intervalId) { - clearInterval(this.intervalId); - this.intervalId = null; - } - } - - startTask(taskName: string): void { - this.currentTaskName = taskName; - const startCpuUsage = process.cpuUsage(); - const startTime = Date.now(); - const taskStats = { - memory: { - rssSum: 0, - rssMax: 0, - }, - startCpuUsage, - startTime, - count: 0, - }; - this.taskStats.set(taskName, taskStats); - this.currentTaskStats = taskStats; - } - - endTask(): void { - if (this.currentTaskName && this.currentTaskStats) { - const endTime = Date.now(); - const totalElapsedMs = endTime - this.currentTaskStats.startTime; - const totalElapsedMicroseconds = totalElapsedMs * 1000; - - const totalCpuUsage = process.cpuUsage(this.currentTaskStats.startCpuUsage); - - const userPercent = - totalElapsedMicroseconds > 0 ? (totalCpuUsage.user / totalElapsedMicroseconds) * 100 : 0; - const systemPercent = - totalElapsedMicroseconds > 0 ? (totalCpuUsage.system / totalElapsedMicroseconds) * 100 : 0; - const totalPercent = userPercent + systemPercent; - - const resourceStats: TaskResourceStats = { - cpu: { - userPercent, - systemPercent, - totalPercent, - }, - memory: { - rss: - this.currentTaskStats.count > 0 - ? { - avg: this.currentTaskStats.memory.rssSum / this.currentTaskStats.count, - max: this.currentTaskStats.memory.rssMax, - } - : { - avg: 0, - max: 0, - }, - }, - }; - - this.completedTaskStats.set(this.currentTaskName, { - resourceStats, - endTime, - }); - } - - this.currentTaskName = null; - this.currentTaskStats = null; - } - - private collectMemorySample(): void { - if (!this.isRunning || !this.currentTaskStats) return; - - const memoryUsage = process.memoryUsage(); - this.currentTaskStats.memory.rssSum += memoryUsage.rss; - this.currentTaskStats.memory.rssMax = Math.max( - this.currentTaskStats.memory.rssMax, - memoryUsage.rss, - ); - this.currentTaskStats.count++; - } - - getTaskStats(taskName: string): TaskResourceStats | null { - const completedStats = this.completedTaskStats.get(taskName); - return completedStats ? completedStats.resourceStats : null; - } - - getAllTaskNames(): string[] { - return Array.from(this.completedTaskStats.keys()); - } -} diff --git a/benchmarks/bench/result-utils.ts b/benchmarks/bench/result-utils.ts deleted file mode 100644 index cbb608fb..00000000 --- a/benchmarks/bench/result-utils.ts +++ /dev/null @@ -1,277 +0,0 @@ -import * as fs from "fs"; -import * as path from "path"; -import * as os from "os"; -import { randomUUID } from "crypto"; -import type { Bench, Task } from "tinybench"; -import type { ResourceMonitor } from "./resource-monitor"; - -// Use process.cwd() as the base directory for the benchmarks -const CURRENT_DIR = path.join(process.cwd(), "benchmarks", "bench"); - -export interface CpuStatsSummary { - userPercent: number; - systemPercent: number; - totalPercent: number; -} - -export interface SimpleMemoryStats { - rss: { avg: number; max: number }; -} - -export interface TaskResourceStats { - cpu: CpuStatsSummary; - memory: SimpleMemoryStats; -} - -export interface HistogramBucket { - minNs: number; - maxNs: number; - count: number; -} - -export interface MetricSummary { - unit: "ns" | "ops/s"; - mean: number | null; - median: number | null; - min: number | null; - max: number | null; - standardDeviation: number | null; - standardError: number | null; - marginOfError: number | null; - relativeMarginOfError: number | null; - tail?: { - percentile: number; - value: number | null; - } | null; -} - -export interface TaskBenchmarkResult { - name: string; - samples: number; - latency: MetricSummary; - throughput: MetricSummary; - resource?: TaskResourceStats | null; - histogram?: HistogramBucket[] | null; -} - -export interface BenchmarkRunResult { - id: string; - label: string; - timestamp: string; - durationMs: number; - options: { - time: number; - warmupTime: number; - warmupIterations: number; - iterations: number; - }; - system: { - nodeVersion: string; - platform: NodeJS.Platform; - arch: string; - cpuCount: number; - totalMemory: number; - freeMemory: number; - loadAverage: number[]; - }; - tasks: TaskBenchmarkResult[]; -} - -export function resolveResultPath(label: string): string { - const resultDir = path.join(CURRENT_DIR, "..", "results"); - const sanitizedLabel = label.replace(/[^a-zA-Z0-9-_]+/g, "-") || "benchmark"; - const fileName = `${sanitizedLabel}.json`; - return path.join(resultDir, fileName); -} - -// Helper functions -function toNanoseconds(value: number | undefined | null): number | null { - if (value === undefined || value === null || Number.isNaN(value)) { - return null; - } - return value * 1_000_000_000; -} - -function safeNumber(value: number | undefined | null): number | null { - if (value === undefined || value === null || Number.isNaN(value)) { - return null; - } - return value; -} - -export function buildLatencyHistogram(samples: number[]): HistogramBucket[] | null { - if (samples.length === 0) { - return null; - } - - const sorted = [...samples].sort((a, b) => a - b); - const min = sorted[0]; - const max = sorted[sorted.length - 1]; - - if (!Number.isFinite(min) || !Number.isFinite(max)) { - return null; - } - - if (min === max) { - return [ - { - minNs: toNanoseconds(min) ?? 0, - maxNs: toNanoseconds(max) ?? 0, - count: samples.length, - }, - ]; - } - - const bucketCount = 20; - const range = max - min; - const bucketSize = range / bucketCount; - const counts = new Array(bucketCount).fill(0); - - for (const sample of samples) { - let bucketIndex = Math.floor((sample - min) / bucketSize); - if (!Number.isFinite(bucketIndex) || bucketIndex < 0) { - bucketIndex = 0; - } - if (bucketIndex >= bucketCount) { - bucketIndex = bucketCount - 1; - } - counts[bucketIndex]!++; - } - - return counts.map((count, index) => { - const bucketMin = min + index * bucketSize; - const isLast = index === bucketCount - 1; - const bucketMax = isLast ? max : min + (index + 1) * bucketSize; - return { - minNs: toNanoseconds(bucketMin) ?? 0, - maxNs: toNanoseconds(bucketMax) ?? 0, - count, - }; - }); -} - -export function buildMetricSummary( - unit: MetricSummary["unit"], - stats: { - mean: number; - p50?: number; - min: number; - max: number; - sd: number; - sem: number; - moe: number; - rme: number; - }, - transform?: (value: number | undefined) => number | null, - tail?: { percentile: number; value?: number }, -): MetricSummary { - const convert = transform ?? ((value?: number) => safeNumber(value ?? null)); - const summary: MetricSummary = { - unit, - mean: convert(stats.mean), - median: convert(stats.p50), - min: convert(stats.min), - max: convert(stats.max), - standardDeviation: convert(stats.sd), - standardError: convert(stats.sem), - marginOfError: convert(stats.moe), - relativeMarginOfError: safeNumber(stats.rme), - }; - - if (tail) { - summary.tail = { - percentile: tail.percentile, - value: convert(tail.value), - }; - } - - return summary; -} - -export function createTaskBenchmarkResult( - task: Task, - resourceMonitor: ResourceMonitor, -): TaskBenchmarkResult | null { - if (!task.result) { - return null; - } - - const { latency, throughput } = task.result; - - if (!latency || !throughput) { - console.warn(`Task ${task.name} missing latency or throughput results`); - return null; - } - - const resourceStats = resourceMonitor.getTaskStats(task.name) ?? null; - - return { - name: task.name, - samples: latency.samples?.length ?? 0, - latency: buildMetricSummary("ns", latency, toNanoseconds, { - percentile: 99, - value: latency.p99, - }), - throughput: buildMetricSummary("ops/s", throughput), - resource: resourceStats, - histogram: buildLatencyHistogram(latency.samples ?? []), - }; -} - -export function createBenchmarkRunResult( - bench: Bench, - resourceMonitor: ResourceMonitor, - durationMs: number, - label: string, -): BenchmarkRunResult { - const tasks = bench.tasks - .map((task: Task) => createTaskBenchmarkResult(task, resourceMonitor)) - .filter( - (taskResult: TaskBenchmarkResult | null): taskResult is TaskBenchmarkResult => - taskResult !== null, - ); - - return { - id: randomUUID(), - label, - timestamp: new Date().toISOString(), - durationMs, - options: { - time: bench.opts.time, - warmupTime: bench.opts.warmupTime, - warmupIterations: bench.opts.warmupIterations, - iterations: bench.opts.iterations, - }, - system: { - nodeVersion: process.version, - platform: process.platform, - arch: process.arch, - cpuCount: os.cpus().length, - totalMemory: os.totalmem(), - freeMemory: os.freemem(), - loadAverage: os.loadavg(), - }, - tasks, - }; -} - -export function persistBenchmarkResult(result: BenchmarkRunResult): string { - const outputPath = resolveResultPath(result.label); - const directory = path.dirname(outputPath); - fs.mkdirSync(directory, { recursive: true }); - fs.writeFileSync(outputPath, JSON.stringify(result, null, 2)); - return outputPath; -} - -export function loadBenchmarkResult(filePath: string): BenchmarkRunResult { - const raw = fs.readFileSync(filePath, "utf8"); - return JSON.parse(raw) as BenchmarkRunResult; -} - -export function createTaskLookup(run: BenchmarkRunResult): Map { - const lookup = new Map(); - for (const task of run.tasks) { - lookup.set(task.name, task); - } - return lookup; -} diff --git a/benchmarks/bench/sdk-active-with-transforms.bench.ts b/benchmarks/bench/sdk-active-with-transforms.bench.ts deleted file mode 100644 index 25e3f6e6..00000000 --- a/benchmarks/bench/sdk-active-with-transforms.bench.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Transforms Benchmark - SDK Active with Transform Rules - * - */ - -process.env.TUSK_DRIFT_MODE = "RECORD"; - -import { TuskDrift, TuskDriftCore } from "../../src/core/TuskDrift"; -import { FilesystemSpanAdapter } from "../../src/core/tracing/adapters/FilesystemSpanAdapter"; -import * as path from "path"; -import * as fs from "fs"; -import main from "./common.js"; - -const BENCHMARK_TRACE_DIR = path.join(__dirname, "..", ".benchmark-traces"); - -if (fs.existsSync(BENCHMARK_TRACE_DIR)) { - try { - fs.rmSync(BENCHMARK_TRACE_DIR, { recursive: true, force: true }); - } catch (error) { - console.warn(`Failed to clean benchmark trace directory: ${error}`); - } -} - -const adapter = new FilesystemSpanAdapter({ - baseDirectory: BENCHMARK_TRACE_DIR, -}); - -TuskDrift.initialize({ - apiKey: "benchmark-test-key", - env: "benchmark", - logLevel: "info", - transforms: { - fetch: [ - { - matcher: { - direction: "inbound" as const, - method: ["POST"], - pathPattern: "/api/auth/login", - jsonPath: "$.password", - }, - action: { type: "redact" as const }, - }, - { - matcher: { - direction: "inbound" as const, - method: ["POST"], - pathPattern: "/api/users", - jsonPath: "$.ssn", - }, - action: { type: "mask" as const }, - }, - { - matcher: { - direction: "inbound" as const, - method: ["POST"], - pathPattern: "/api/users", - jsonPath: "$.creditCard", - }, - action: { type: "redact" as const }, - }, - ], - }, -}); - -TuskDriftCore.getInstance().spanExporter?.clearAdapters(); -TuskDriftCore.getInstance().spanExporter?.addAdapter(adapter); - -TuskDrift.markAppAsReady(); - -process.env.BENCHMARK_RESULT_LABEL = "sdk-active-with-transforms"; - -main("SDK Active with Transforms"); diff --git a/benchmarks/bench/sdk-active.bench.ts b/benchmarks/bench/sdk-active.bench.ts deleted file mode 100644 index 7139ffbd..00000000 --- a/benchmarks/bench/sdk-active.bench.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * All Benchmarks - SDK Active - * - */ - -process.env.TUSK_DRIFT_MODE = "RECORD"; - -import { TuskDrift, TuskDriftCore } from "../../src/core/TuskDrift"; -import { FilesystemSpanAdapter } from "../../src/core/tracing/adapters/FilesystemSpanAdapter"; -import * as path from "path"; -import * as fs from "fs"; -import main from "./common.js"; - -const BENCHMARK_TRACE_DIR = path.join(__dirname, "..", ".benchmark-traces"); - -if (fs.existsSync(BENCHMARK_TRACE_DIR)) { - try { - fs.rmSync(BENCHMARK_TRACE_DIR, { recursive: true, force: true }); - } catch (error) { - console.warn(`Failed to clean benchmark trace directory: ${error}`); - } -} - -const adapter = new FilesystemSpanAdapter({ - baseDirectory: BENCHMARK_TRACE_DIR, -}); - -TuskDrift.initialize({ - apiKey: "benchmark-test-key", - env: "benchmark", - logLevel: "info", -}); - -TuskDriftCore.getInstance().spanExporter?.clearAdapters(); -TuskDriftCore.getInstance().spanExporter?.addAdapter(adapter); - -TuskDrift.markAppAsReady(); - -process.env.BENCHMARK_RESULT_LABEL = "sdk-active"; - -main("SDK Active"); diff --git a/benchmarks/bench/sdk-disabled.bench.ts b/benchmarks/bench/sdk-disabled.bench.ts deleted file mode 100644 index a6c48e98..00000000 --- a/benchmarks/bench/sdk-disabled.bench.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * All Benchmarks - SDK Disabled - */ - -import main from "./common"; - -process.env.BENCHMARK_RESULT_LABEL = "sdk-disabled"; - -main("SDK Disabled"); diff --git a/benchmarks/compare-benchmarks.ts b/benchmarks/compare-benchmarks.ts deleted file mode 100644 index 1cbeb974..00000000 --- a/benchmarks/compare-benchmarks.ts +++ /dev/null @@ -1,269 +0,0 @@ -#!/usr/bin/env node - -import { - BenchmarkRunResult, - MetricSummary, - TaskBenchmarkResult, - createTaskLookup, - loadBenchmarkResult, - resolveResultPath, -} from "./bench/result-utils"; - -interface ImpactResult { - label: string; - throughputDeltaPct: number | null; - tailLatencyDeltaPct: number | null; - baselineThroughput: number | null; - variantThroughput: number | null; - baselineTail: number | null; - variantTail: number | null; - cpuUserDeltaPct: number | null; - cpuSystemDeltaPct: number | null; - cpuTotalDeltaPct: number | null; - baselineCpuTotal: number | null; - variantCpuTotal: number | null; -} - -interface MemoryOverheadSummary { - avgRssDelta: number | null; - maxRssDelta: number | null; - avgHeapUsedDelta: number | null; - maxHeapUsedDelta: number | null; - samples: number; -} - -const CPU_HEAVY_TASK_BASE = "High CPU: POST /api/compute-hash"; -const IO_HEAVY_TASK_BASE = "High IO, Low CPU: POST /api/io-bound"; -const TRANSFORM_TASK_BASE = "Transform endpoints"; - -function findTaskByBaseName(tasks: Map, baseName: string): TaskBenchmarkResult | undefined { - for (const [taskName, taskResult] of tasks.entries()) { - if (taskName.startsWith(baseName + " (")) { - return taskResult; - } - } - return undefined; -} - -function percentageChange(baseline: number | null, variant: number | null): number | null { - if (baseline === null || variant === null) { - return null; - } - if (baseline === 0) { - return null; - } - return ((variant - baseline) / baseline) * 100; -} - -function getTailLatency(summary: MetricSummary): number | null { - if (summary.tail && summary.tail.value !== null) { - return summary.tail.value; - } - return summary.max ?? null; -} - -function formatPercentage(value: number | null): string { - if (value === null || Number.isNaN(value)) { - return "n/a"; - } - const sign = value >= 0 ? "+" : ""; - return `${sign}${value.toFixed(1)}%`; -} - -function formatLatency(ns: number | null): string { - if (ns === null || Number.isNaN(ns)) { - return "n/a"; - } - if (ns < 1_000) return `${ns.toFixed(0)} ns`; - if (ns < 1_000_000) return `${(ns / 1_000).toFixed(2)} μs`; - if (ns < 1_000_000_000) return `${(ns / 1_000_000).toFixed(2)} ms`; - return `${(ns / 1_000_000_000).toFixed(2)} s`; -} - -function formatThroughput(value: number | null): string { - if (value === null || Number.isNaN(value)) { - return "n/a"; - } - return value.toLocaleString(undefined, { maximumFractionDigits: 0 }); -} - -function formatBytes(bytes: number | null): string { - if (bytes === null || Number.isNaN(bytes)) { - return "n/a"; - } - const mb = bytes / (1024 * 1024); - return `${mb.toFixed(2)} MB`; -} - -function formatCpuPercent(value: number | null): string { - if (value === null || Number.isNaN(value)) { - return "n/a"; - } - return `${value.toFixed(1)}%`; -} - -function computeImpact( - label: string, - baseline: TaskBenchmarkResult | undefined, - variant: TaskBenchmarkResult | undefined, -): ImpactResult | null { - if (!baseline || !variant) { - return null; - } - - const baselineThroughput = baseline.throughput.mean ?? null; - const variantThroughput = variant.throughput.mean ?? null; - const baselineTail = getTailLatency(baseline.latency); - const variantTail = getTailLatency(variant.latency); - - const baselineCpuUser = baseline.resource?.cpu?.userPercent ?? null; - const variantCpuUser = variant.resource?.cpu?.userPercent ?? null; - const baselineCpuSystem = baseline.resource?.cpu?.systemPercent ?? null; - const variantCpuSystem = variant.resource?.cpu?.systemPercent ?? null; - const baselineCpuTotal = baseline.resource?.cpu?.totalPercent ?? null; - const variantCpuTotal = variant.resource?.cpu?.totalPercent ?? null; - - return { - label, - throughputDeltaPct: percentageChange(baselineThroughput, variantThroughput), - tailLatencyDeltaPct: percentageChange(baselineTail, variantTail), - baselineThroughput, - variantThroughput, - baselineTail, - variantTail, - cpuUserDeltaPct: percentageChange(baselineCpuUser, variantCpuUser), - cpuSystemDeltaPct: percentageChange(baselineCpuSystem, variantCpuSystem), - cpuTotalDeltaPct: percentageChange(baselineCpuTotal, variantCpuTotal), - baselineCpuTotal, - variantCpuTotal, - }; -} - -function computeMemoryOverhead( - baseline: BenchmarkRunResult, - variant: BenchmarkRunResult, -): MemoryOverheadSummary { - const baselineTasks = createTaskLookup(baseline); - const variantTasks = createTaskLookup(variant); - - let rssSum = 0; - let rssMaxDelta: number | null = null; - let count = 0; - - for (const [name, baselineTask] of Array.from(baselineTasks.entries())) { - const variantTaskName = Array.from(variantTasks.keys()).find(vName => - vName.replace(/ \([^)]+\)$/, '') === name.replace(/ \([^)]+\)$/, '') - ); - const variantTask = variantTaskName ? variantTasks.get(variantTaskName) : undefined; - - if (!baselineTask?.resource || !variantTask?.resource) { - continue; - } - - const baselineMem = baselineTask.resource.memory; - const variantMem = variantTask.resource.memory; - - const avgRssDelta = variantMem.rss.avg - baselineMem.rss.avg; - const maxRssDelta = variantMem.rss.max - baselineMem.rss.max; - rssSum += avgRssDelta; - rssMaxDelta = rssMaxDelta === null ? maxRssDelta : Math.max(rssMaxDelta, maxRssDelta); - - count++; - } - - return { - avgRssDelta: count ? rssSum / count : null, - maxRssDelta: rssMaxDelta, - avgHeapUsedDelta: null, - maxHeapUsedDelta: null, - samples: count, - }; -} - - -function main(): void { - const baselinePath = resolveResultPath("sdk-disabled"); - const activePath = resolveResultPath("sdk-active"); - const transformsPath = resolveResultPath("sdk-active-with-transforms"); - - const baseline = loadBenchmarkResult(baselinePath); - const active = loadBenchmarkResult(activePath); - - let transforms: BenchmarkRunResult | null = null; - try { - transforms = loadBenchmarkResult(transformsPath); - } catch (error) { - transforms = null; - } - - const baselineTasks = createTaskLookup(baseline); - const activeTasks = createTaskLookup(active); - const transformTasks = transforms ? createTaskLookup(transforms) : null; - - - console.log("\n## Benchmark Impact Summary\n"); - - const cpuImpact = computeImpact( - CPU_HEAVY_TASK_BASE, - findTaskByBaseName(baselineTasks, CPU_HEAVY_TASK_BASE), - findTaskByBaseName(activeTasks, CPU_HEAVY_TASK_BASE), - ); - - const ioImpact = computeImpact( - IO_HEAVY_TASK_BASE, - findTaskByBaseName(baselineTasks, IO_HEAVY_TASK_BASE), - findTaskByBaseName(activeTasks, IO_HEAVY_TASK_BASE), - ); - - const transformImpact = transformTasks ? computeImpact( - TRANSFORM_TASK_BASE, - findTaskByBaseName(activeTasks, TRANSFORM_TASK_BASE), - findTaskByBaseName(transformTasks, TRANSFORM_TASK_BASE), - ) : null; - - // Print performance impact table - console.log("| Workload | Throughput Δ | Tail Latency Δ | User CPU Δ |"); - console.log("|----------|-------------|---------------|-------------|"); - - if (cpuImpact) { - console.log(`| **CPU-bound** | ${formatPercentage(cpuImpact.throughputDeltaPct)} | ${formatPercentage(cpuImpact.tailLatencyDeltaPct)} | ${formatPercentage(cpuImpact.cpuUserDeltaPct)} |`); - } else { - console.log("| **CPU-bound** | N/A | N/A | N/A |"); - } - - if (ioImpact) { - console.log(`| **IO-bound** | ${formatPercentage(ioImpact.throughputDeltaPct)} | ${formatPercentage(ioImpact.tailLatencyDeltaPct)} | ${formatPercentage(ioImpact.cpuUserDeltaPct)} |`); - } else { - console.log("| **IO-bound** | N/A | N/A | N/A |"); - } - - if (transformImpact) { - console.log(`| **Transform endpoints** | ${formatPercentage(transformImpact.throughputDeltaPct)} | ${formatPercentage(transformImpact.tailLatencyDeltaPct)} | ${formatPercentage(transformImpact.cpuUserDeltaPct)} |`); - } else { - console.log("| **Transform endpoints** | N/A | N/A | N/A |"); - } - - // Print memory overhead table - const activeMemory = computeMemoryOverhead(baseline, active); - const transformMemory = transforms ? computeMemoryOverhead(baseline, transforms) : null; - - console.log("\n## Memory Overhead vs Baseline\n"); - console.log("| Configuration | Avg RSS Δ | Max RSS Δ |"); - console.log("|--------------|-----------|-----------|"); - - if (activeMemory && activeMemory.samples > 0) { - console.log(`| **SDK Active** | ${formatBytes(activeMemory.avgRssDelta)} | ${formatBytes(activeMemory.maxRssDelta)} |`); - } else { - console.log("| **SDK Active** | N/A | N/A |"); - } - - if (transformMemory && transformMemory.samples > 0) { - console.log(`| **SDK Active w/ Transforms** | ${formatBytes(transformMemory.avgRssDelta)} | ${formatBytes(transformMemory.maxRssDelta)} |`); - } else { - console.log("| **SDK Active w/ Transforms** | N/A | N/A |"); - } - - console.log("\nSummary complete.\n"); -} - -main(); diff --git a/benchmarks/profile/profile.sh b/benchmarks/profile/profile.sh deleted file mode 100755 index 8c82b569..00000000 --- a/benchmarks/profile/profile.sh +++ /dev/null @@ -1,6 +0,0 @@ -npm run tsc -- benchmarks/profile/simple.profile.ts --module commonjs --target es6 - -export TUSK_DRIFT_MODE=RECORD - -0x -o -D benchmarks/profile/results benchmarks/profile/simple.profile.js - diff --git a/benchmarks/profile/simple.profile.js b/benchmarks/profile/simple.profile.js deleted file mode 100644 index dfd13b61..00000000 --- a/benchmarks/profile/simple.profile.js +++ /dev/null @@ -1,52 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const dist_1 = require("../../dist"); -const tinybench_1 = require("tinybench"); -dist_1.TuskDrift.initialize({ - apiKey: "benchmark-test-key", - env: "benchmark", - logLevel: "info", -}); -const test_server_1 = require("../server/test-server"); -dist_1.TuskDrift.markAppAsReady(); -let server; -let serverUrl; -function startup() { - return __awaiter(this, void 0, void 0, function* () { - server = new test_server_1.TestServer(); - const info = yield server.start(); - serverUrl = info.url; - console.log(`\nTest server started at ${serverUrl}`); - }); -} -function teardown() { - return __awaiter(this, void 0, void 0, function* () { - yield server.stop(); - console.log("Test server stopped\n"); - }); -} -(() => __awaiter(void 0, void 0, void 0, function* () { - yield startup(); - const bench = new tinybench_1.Bench({ - time: 10000, - warmupTime: 1000, - warmupIterations: 100, - now: tinybench_1.hrtimeNow, - }); - bench.add("High Throughput: GET /api/simple", () => __awaiter(void 0, void 0, void 0, function* () { - const response = yield fetch(`${serverUrl}/api/simple`); - yield response.json(); - })); - yield bench.run(); - console.table(bench.table()); - yield teardown(); -}))().catch(console.error); diff --git a/benchmarks/profile/simple.profile.ts b/benchmarks/profile/simple.profile.ts deleted file mode 100644 index 654bc344..00000000 --- a/benchmarks/profile/simple.profile.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { TuskDrift } from "../../dist"; -import { Bench, hrtimeNow } from "tinybench"; - -TuskDrift.initialize({ - apiKey: "benchmark-test-key", - env: "benchmark", - logLevel: "info", -}); - -import { TestServer } from "../server/test-server"; - -TuskDrift.markAppAsReady(); - -let server: TestServer; -let serverUrl: string; - -async function startup() { - server = new TestServer(); - const info = await server.start(); - serverUrl = info.url; - console.log(`\nTest server started at ${serverUrl}`); -} - -async function teardown() { - await server.stop(); - console.log("Test server stopped\n"); -} - -(async () => { - await startup(); - - const bench = new Bench({ - time: 10000, - warmupTime: 1000, - warmupIterations: 100, - now: hrtimeNow, - }); - - bench.add("High Throughput: GET /api/simple", async () => { - const response = await fetch(`${serverUrl}/api/simple`); - await response.json(); - }); - - await bench.run(); - console.table(bench.table()); - - await teardown(); -})().catch(console.error); diff --git a/benchmarks/server/test-server.ts b/benchmarks/server/test-server.ts deleted file mode 100644 index d1d39a5a..00000000 --- a/benchmarks/server/test-server.ts +++ /dev/null @@ -1,273 +0,0 @@ -import type { Request, Response, Application } from "express"; -import * as crypto from "crypto"; -import type { Server } from "http"; - -// eslint-disable-next-line @typescript-eslint/no-var-requires -const express = require("express"); - -export interface TestServerConfig { - port?: number; - host?: string; -} - -export class TestServer { - private app: Application; - private server: Server | null = null; - private port: number; - private host: string; - - constructor(config: TestServerConfig = {}) { - this.port = config.port || 0; // 0 = random port - this.host = config.host || "127.0.0.1"; - this.app = express(); - this.setupRoutes(); - } - - private setupRoutes(): void { - // Parse JSON bodies - this.app.use(express.json({ limit: "50mb" })); - - // Health check endpoint - this.app.get("/health", (_req: Request, res: Response) => { - res.json({ status: "ok" }); - }); - - // Simple endpoint - minimal processing - this.app.get("/api/simple", (_req: Request, res: Response) => { - res.json({ message: "Hello World", timestamp: Date.now() }); - }); - - // Simple POST endpoint - minimal processing with small request body - this.app.post("/api/simple-post", (req: Request, res: Response) => { - res.json({ message: "Hello World", timestamp: Date.now() }); - }); - - // Echo endpoint - returns what you send - this.app.post("/api/echo", (req: Request, res: Response) => { - res.json(req.body); - }); - - // Small payload endpoint (100KB) - this.app.get("/api/small", (_req: Request, res: Response) => { - const data = { - id: "small-123", - data: "x".repeat(100 * 1024), // 100KB of data - timestamp: Date.now(), - }; - res.json(data); - }); - - // Small POST endpoint - accepts and returns same size payload as GET - this.app.post("/api/small-post", (req: Request, res: Response) => { - res.json({ - id: "small-post-123", - data: "x".repeat(100 * 1024), // 100KB of data - timestamp: Date.now(), - }); - }); - - // Medium payload endpoint (1MB) - this.app.get("/api/medium", (_req: Request, res: Response) => { - const data = { - id: "medium-456", - data: "x".repeat(1024 * 1024), // 1MB of data - timestamp: Date.now(), - }; - res.json(data); - }); - - // Medium POST endpoint - accepts and returns same size payload as GET - this.app.post("/api/medium-post", (req: Request, res: Response) => { - res.json({ - id: "medium-post-456", - data: "x".repeat(1024 * 1024), // 1MB of data - timestamp: Date.now(), - }); - }); - - // Large payload endpoint (2MB) - this.app.get("/api/large", (_req: Request, res: Response) => { - const data = { - id: "large-789", - data: "x".repeat(2 * 1024 * 1024), // 2MB of data - timestamp: Date.now(), - }; - res.json(data); - }); - - // Large POST endpoint - accepts and returns same size payload as GET - this.app.post("/api/large-post", (req: Request, res: Response) => { - res.json({ - id: "large-post-789", - data: "x".repeat(2 * 1024 * 1024), // 2MB of data - timestamp: Date.now(), - }); - }); - - // CPU-intensive endpoint - hashing - this.app.post("/api/compute-hash", (req: Request, res: Response) => { - const data = req.body.data || "default-data"; - const iterations = req.body.iterations || 1000; - - let hash = data; - for (let i = 0; i < iterations; i++) { - hash = crypto.createHash("sha256").update(hash).digest("hex"); - } - - res.json({ hash, iterations }); - }); - - // CPU-intensive endpoint - JSON parsing/stringifying - this.app.post("/api/compute-json", (req: Request, res: Response) => { - const iterations = req.body.iterations || 100; - const data = req.body.data || { test: "data", nested: { value: 123 } }; - - let result = data; - for (let i = 0; i < iterations; i++) { - const str = JSON.stringify(result); - result = JSON.parse(str); - result.iteration = i; - } - - res.json(result); - }); - - // Endpoint with sensitive data for transform testing - this.app.post("/api/auth/login", (req: Request, res: Response) => { - res.json({ - success: true, - token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", - user: { - id: 123, - email: req.body.email, - role: "user", - }, - }); - }); - - // Endpoint with nested sensitive data - this.app.post("/api/users", (req: Request, res: Response) => { - res.json({ - id: Date.now(), - username: req.body.username, - profile: { - email: req.body.email, - ssn: req.body.ssn || "123-45-6789", - creditCard: req.body.creditCard || "4111-1111-1111-1111", - }, - createdAt: new Date().toISOString(), - }); - }); - - // Endpoint that makes an outbound HTTP call - this.app.get("/api/proxy", async (_req: Request, res: Response) => { - try { - // Make a call to our own /api/simple endpoint - const response = await fetch(`http://${this.host}:${this.port}/api/simple`); - const data = await response.json(); - res.json({ proxied: data, timestamp: Date.now() }); - } catch (error) { - res.status(500).json({ error: (error as Error).message }); - } - }); - - // Streaming endpoint for testing body capture - this.app.post("/api/stream", (req: Request, res: Response) => { - let size = 0; - req.on("data", (chunk) => { - size += chunk.length; - }); - req.on("end", () => { - res.json({ receivedBytes: size }); - }); - }); - - // Error endpoint - this.app.get("/api/error", (_req: Request, res: Response) => { - res.status(500).json({ error: "Internal Server Error" }); - }); - - // Slow endpoint - simulates slow processing - this.app.get("/api/slow", async (_req: Request, res: Response) => { - await new Promise((resolve) => setTimeout(resolve, 100)); - res.json({ message: "Slow response", timestamp: Date.now() }); - }); - - // High IO, Low CPU endpoint - simulates IO-bound work with minimal CPU usage - // Multiple small async operations that await on timers - this.app.post("/api/io-bound", async (req: Request, res: Response) => { - const jobs = req.body.jobs || 5; - const delayMs = req.body.delayMs || 5; - - const results = []; - for (let i = 0; i < jobs; i++) { - // Simulate an IO operation (database query, file read, external API call, etc.) - await new Promise((resolve) => setTimeout(resolve, delayMs)); - results.push({ - jobId: i, - timestamp: Date.now(), - status: "completed", - }); - } - - res.json({ - totalJobs: jobs, - results, - completedAt: Date.now(), - }); - }); - } - - async start(): Promise<{ port: number; host: string; url: string }> { - return new Promise((resolve, reject) => { - try { - this.server = this.app.listen(this.port, this.host, () => { - const address = this.server!.address(); - if (!address || typeof address === "string") { - reject(new Error("Failed to get server address")); - return; - } - this.port = address.port; - const url = `http://${this.host}:${this.port}`; - console.log(`Test server started at ${url}`); - resolve({ port: this.port, host: this.host, url }); - }); - - this.server.on("error", reject); - } catch (error) { - reject(error); - } - }); - } - - async stop(): Promise { - return new Promise((resolve, reject) => { - if (!this.server) { - resolve(); - return; - } - - this.server.close((err) => { - if (err) { - reject(err); - } else { - console.log("Test server stopped"); - this.server = null; - resolve(); - } - }); - }); - } - - getPort(): number { - return this.port; - } - - getHost(): string { - return this.host; - } - - getUrl(): string { - return `http://${this.host}:${this.port}`; - } -} diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..87cf130c --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1769461804, + "narHash": "sha256-msG8SU5WsBUfVVa/9RPLaymvi5bI8edTavbIq3vRlhI=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "bfc1b8a4574108ceef22f02bafcf6611380c100d", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..bf1fa1e9 --- /dev/null +++ b/flake.nix @@ -0,0 +1,47 @@ +{ + description = "A very basic flake"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + }; + + outputs = + { + self, + nixpkgs, + }: + let + x86-linux = "x86_64-linux"; + arm-linux = "aarch64-linux"; + arm-macos = "aarch64-darwin"; + + defaultSystems = [ + x86-linux + arm-linux + arm-macos + ]; + eachSystem = + fun: systems: + nixpkgs.lib.genAttrs systems ( + system: + let + pkgs = import nixpkgs { + inherit system; + }; + in + fun pkgs + ); + in + { + devShell = eachSystem ( + pkgs: + with pkgs; + mkShell { + buildInputs = [ + (python3.withPackages (p: with p; [ ])) + ]; + } + ) defaultSystems; + }; + +} diff --git a/run-all-benchmarks.sh b/run-all-benchmarks.sh new file mode 100755 index 00000000..c5bf4b46 --- /dev/null +++ b/run-all-benchmarks.sh @@ -0,0 +1,225 @@ +#!/bin/bash + +# Script to run benchmarks for all Node SDK E2E instrumentation tests serially. +# Each instrumentation runs with BENCHMARKS=1, producing a comparison of +# SDK disabled vs enabled (RECORD mode) with Go-style ns/op stats. +# +# Usage: +# ./run-all-benchmarks.sh # Run all, 5s per endpoint +# ./run-all-benchmarks.sh -d 20 # Run all, 20s per endpoint +# ./run-all-benchmarks.sh -w 5 # 5s warmup per endpoint +# ./run-all-benchmarks.sh -f http,pg # Run only http and pg +# ./run-all-benchmarks.sh -h # Show help + +set -e + +# Default values +BENCHMARK_DURATION=${BENCHMARK_DURATION:-5} +BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} +FILTER="" + +# Colors for output +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +usage() { + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Run SDK benchmarks for all (or selected) instrumentations serially." + echo "" + echo "Options:" + echo " -d, --duration N Seconds per endpoint for timed loop (default: 5)" + echo " -w, --warmup N Seconds of warmup per endpoint before timing (default: 3)" + echo " -f, --filter LIST Comma-separated list of instrumentations to benchmark" + echo " e.g. http,pg,ioredis" + echo " -h, --help Show this help message" + echo "" + echo "Environment variables:" + echo " BENCHMARK_DURATION Same as --duration" + echo " BENCHMARK_WARMUP Same as --warmup" + echo " TUSK_CLI_VERSION CLI version to use in Docker builds" + echo "" + echo "Examples:" + echo " $0 # Benchmark all instrumentations" + echo " $0 -d 20 # 20s per endpoint" + echo " $0 -f http,pg # Only benchmark http and pg" +} + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + -d|--duration) + if [[ -z "$2" ]] || [[ "$2" == -* ]]; then + echo "Error: --duration requires a number argument" + exit 1 + fi + BENCHMARK_DURATION="$2" + shift 2 + ;; + -w|--warmup) + if [[ -z "$2" ]] || [[ "$2" == -* ]]; then + echo "Error: --warmup requires a number argument" + exit 1 + fi + BENCHMARK_WARMUP="$2" + shift 2 + ;; + -f|--filter) + if [[ -z "$2" ]] || [[ "$2" == -* ]]; then + echo "Error: --filter requires a comma-separated list" + exit 1 + fi + FILTER="$2" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Error: Unknown option $1" + usage + exit 1 + ;; + esac +done + +# Validate numbers +if ! [[ "$BENCHMARK_DURATION" =~ ^[0-9]+$ ]]; then + echo "Error: --duration must be a positive integer" + exit 1 +fi + +if ! [[ "$BENCHMARK_WARMUP" =~ ^[0-9]+$ ]]; then + echo "Error: --warmup must be a positive integer" + exit 1 +fi + +# Get the directory where this script is located (SDK root) +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Discover CJS variant run.sh scripts (one per library). +# Benchmarks only run the CJS variant to avoid CJS+ESM running in parallel +# and skewing results - the module system doesn't affect benchmark numbers. +ALL_SCRIPTS=($(find "$SCRIPT_DIR/src/instrumentation/libraries" -path "*/e2e-tests/cjs-*/run.sh" -type f | sort)) + +if [ ${#ALL_SCRIPTS[@]} -eq 0 ]; then + echo -e "${RED}No e2e test scripts found!${NC}" + exit 1 +fi + +# Apply filter if provided +RUN_SCRIPTS=() +RUN_NAMES=() +for script in "${ALL_SCRIPTS[@]}"; do + # Extract library name from path: .../libraries/{name}/e2e-tests/cjs-{name}/run.sh + NAME=$(echo "$script" | sed -E 's|.*/libraries/([^/]+)/e2e-tests/cjs-[^/]+/run.sh|\1|') + if [ -n "$FILTER" ]; then + # Check if NAME is in the comma-separated filter list + if echo ",$FILTER," | grep -q ",$NAME,"; then + RUN_SCRIPTS+=("$script") + RUN_NAMES+=("$NAME") + fi + else + RUN_SCRIPTS+=("$script") + RUN_NAMES+=("$NAME") + fi +done + +NUM_TESTS=${#RUN_SCRIPTS[@]} + +if [ $NUM_TESTS -eq 0 ]; then + echo -e "${RED}No matching instrumentations found for filter: $FILTER${NC}" + echo "Available: $(printf '%s\n' "${ALL_SCRIPTS[@]}" | sed -E 's|.*/libraries/([^/]+)/e2e-tests/cjs-[^/]+/run.sh|\1|' | tr '\n' ' ')" + exit 1 +fi + +echo "" +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Node SDK Benchmarks${NC}" +echo -e "${BLUE}========================================${NC}" +echo "Instrumentations: ${RUN_NAMES[*]}" +echo "Duration per endpoint: ${BENCHMARK_DURATION}s" +echo "Warmup per endpoint: ${BENCHMARK_WARMUP}s" +echo "Total instrumentations: $NUM_TESTS" +echo -e "${BLUE}========================================${NC}" +echo "" + +# Export benchmark env vars so they pass through to run.sh scripts +export BENCHMARKS=1 +export BENCHMARK_DURATION +export BENCHMARK_WARMUP + +# Track results +OVERALL_EXIT_CODE=0 +declare -a EXIT_CODES + +for i in "${!RUN_SCRIPTS[@]}"; do + SCRIPT="${RUN_SCRIPTS[$i]}" + NAME="${RUN_NAMES[$i]}" + TEST_DIR=$(dirname "$SCRIPT") + + echo "" + echo -e "${BLUE}============================================================${NC}" + echo -e "${BLUE}[$((i + 1))/$NUM_TESTS] Benchmarking: $NAME${NC}" + echo -e "${BLUE}============================================================${NC}" + echo "" + + # Check if this library has been migrated to entrypoint pattern + if [ ! -f "$TEST_DIR/entrypoint.sh" ]; then + echo -e "${YELLOW}⚠ Skipping $NAME - not yet migrated to benchmark-compatible pattern${NC}" + echo -e "${YELLOW} (Only libraries with entrypoint.sh support benchmarks)${NC}" + EXIT_CODES+=("0") + continue + fi + + chmod +x "$SCRIPT" + + set +e + (cd "$TEST_DIR" && ./run.sh) + EXIT_CODE=$? + set -e + + # Cleanup any leftover containers from this library + docker ps -aq --filter "name=${NAME}" 2>/dev/null | xargs -r docker rm -f 2>/dev/null || true + + EXIT_CODES+=("$EXIT_CODE") + + if [ $EXIT_CODE -ne 0 ]; then + echo -e "${RED}Benchmark for $NAME failed with exit code $EXIT_CODE${NC}" + OVERALL_EXIT_CODE=1 + fi + + echo "" +done + +# Final summary +echo "" +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Benchmark Summary${NC}" +echo -e "${BLUE}========================================${NC}" + +for i in "${!RUN_NAMES[@]}"; do + NAME="${RUN_NAMES[$i]}" + EXIT_CODE="${EXIT_CODES[$i]}" + + if [ "$EXIT_CODE" -eq 0 ]; then + echo -e "${GREEN} ✓${NC} $NAME" + else + echo -e "${RED} ✗${NC} $NAME (exit code $EXIT_CODE)" + fi +done + +echo -e "${BLUE}========================================${NC}" + +if [ $OVERALL_EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}All $NUM_TESTS benchmark(s) completed successfully.${NC}" +else + echo -e "${RED}Some benchmarks failed.${NC}" +fi + +echo "" +exit $OVERALL_EXIT_CODE diff --git a/src/instrumentation/libraries/e2e-common/base-entrypoint.sh b/src/instrumentation/libraries/e2e-common/base-entrypoint.sh new file mode 100644 index 00000000..5e9328fd --- /dev/null +++ b/src/instrumentation/libraries/e2e-common/base-entrypoint.sh @@ -0,0 +1,278 @@ +#!/bin/bash +set -e -m -o pipefail + +# Shared E2E Test Entrypoint for Node SDK +# Mirrors Python SDK's base_runner.py pattern. +# +# Usage: Set config vars then source this file from each variant's entrypoint.sh: +# +# #!/bin/bash +# SERVER_WAIT_TIME=5 +# source /app/base-entrypoint.sh +# +# For libraries with custom setup (e.g. prisma): +# +# #!/bin/bash +# SERVER_WAIT_TIME=10 +# setup_library() { +# npx prisma generate +# npx prisma db push --force-reset --skip-generate +# } +# source /app/base-entrypoint.sh +# +# Configuration (set before sourcing): +# SERVER_WAIT_TIME - Seconds to wait after starting server (default: 5) +# setup_library() - Optional hook for library-specific setup + +SERVER_WAIT_TIME=${SERVER_WAIT_TIME:-5} + +# Colors +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +log() { echo -e "${2:-$NC}$1${NC}"; } + +# Stop the server and all its child processes (npm spawns node underneath) +stop_server() { + if [ -n "$SERVER_PID" ]; then + kill -- -$SERVER_PID 2>/dev/null || kill $SERVER_PID 2>/dev/null || true + wait $SERVER_PID 2>/dev/null || true + SERVER_PID="" + fi +} +cleanup() { + log "Stopping server..." "$YELLOW" + stop_server +} +trap cleanup EXIT +SERVER_PID="" + +# ============================================================ +# Phase 1: Setup +# ============================================================ +log "================================================" "$BLUE" +log "Phase 1: Setup" "$BLUE" +log "================================================" "$BLUE" + +log "Installing dependencies..." +npm install --silent + +# Call library-specific setup hook if defined +if type setup_library &>/dev/null; then + log "Running library-specific setup..." + setup_library +fi + +log "Building TypeScript..." +npm run build --silent + +# Clean traces/logs +rm -rf .tusk/traces/* .tusk/logs/* 2>/dev/null || true +mkdir -p .tusk/traces .tusk/logs + +log "Setup complete" "$GREEN" + +# ============================================================ +# Phase 2: Record Traces (or run benchmarks) +# ============================================================ +if [ -n "$BENCHMARKS" ]; then + DURATION=${BENCHMARK_DURATION:-5} + WARMUP=${BENCHMARK_WARMUP:-3} + BASELINE_OUTPUT=$(mktemp) + SDK_OUTPUT=$(mktemp) + + log "================================================" "$BLUE" + log "BASELINE (SDK DISABLED)" "$BLUE" + log "Duration per endpoint: ${DURATION}s, Warmup: ${WARMUP}s" "$BLUE" + log "================================================" "$BLUE" + + TUSK_DRIFT_MODE=DISABLED npm run dev & + SERVER_PID=$! + sleep "$SERVER_WAIT_TIME" + + BENCHMARKS="$BENCHMARKS" BENCHMARK_DURATION="$DURATION" BENCHMARK_WARMUP="$WARMUP" \ + node /app/src/test_requests.mjs | tee "$BASELINE_OUTPUT" + + stop_server + sleep 2 + + log "" + log "================================================" "$BLUE" + log "WITH SDK (TUSK_DRIFT_MODE=RECORD)" "$BLUE" + log "================================================" "$BLUE" + + rm -rf .tusk/traces/* .tusk/logs/* 2>/dev/null || true + TUSK_DRIFT_MODE=RECORD npm run dev & + SERVER_PID=$! + sleep "$SERVER_WAIT_TIME" + + BENCHMARKS="$BENCHMARKS" BENCHMARK_DURATION="$DURATION" BENCHMARK_WARMUP="$WARMUP" \ + node /app/src/test_requests.mjs | tee "$SDK_OUTPUT" + + stop_server + + # Print comparison table + log "" + log "==============================================================================" "$BLUE" + log "COMPARISON (negative = slower with SDK)" "$BLUE" + log "==============================================================================" "$BLUE" + printf "%-40s %12s %12s %10s\n" "Benchmark" "Baseline" "With SDK" "Diff" + log "------------------------------------------------------------------------------" + + # Parse baseline results into parallel arrays + BENCH_NAMES=() + BENCH_BASELINE=() + while IFS= read -r line; do + name=$(echo "$line" | grep -oE '^Benchmark_\S+' || true) + ops=$(echo "$line" | grep -oE '[0-9.]+\s+ops/s' | awk '{print $1}' || true) + if [ -n "$name" ] && [ -n "$ops" ]; then + BENCH_NAMES+=("$name") + BENCH_BASELINE+=("$ops") + fi + done < "$BASELINE_OUTPUT" + + # Parse SDK results into associative-style lookup (using parallel array) + SDK_NAMES=() + SDK_OPS=() + while IFS= read -r line; do + name=$(echo "$line" | grep -oE '^Benchmark_\S+' || true) + ops=$(echo "$line" | grep -oE '[0-9.]+\s+ops/s' | awk '{print $1}' || true) + if [ -n "$name" ] && [ -n "$ops" ]; then + SDK_NAMES+=("$name") + SDK_OPS+=("$ops") + fi + done < "$SDK_OUTPUT" + + # Display comparison for each benchmark + for i in "${!BENCH_NAMES[@]}"; do + name="${BENCH_NAMES[$i]}" + base_ops="${BENCH_BASELINE[$i]}" + + # Find matching SDK result + sdk_ops="" + for j in "${!SDK_NAMES[@]}"; do + if [ "${SDK_NAMES[$j]}" = "$name" ]; then + sdk_ops="${SDK_OPS[$j]}" + break + fi + done + + if [ -z "$sdk_ops" ]; then + printf "%-40s %10s/s %12s %10s\n" "$name" "$base_ops" "N/A" "" + continue + fi + + # Calculate percentage difference using awk + diff_str=$(awk "BEGIN { + diff = (($sdk_ops - $base_ops) / $base_ops) * 100; + printf \"%+.1f%%\", diff + }") + + diff_val=$(awk "BEGIN { + print (($sdk_ops - $base_ops) / $base_ops) * 100 + }") + + # Color: red if >5% slower, yellow if slower, green if faster + color="$GREEN" + if awk "BEGIN { exit !($diff_val < -5) }"; then + color="$RED" + elif awk "BEGIN { exit !($diff_val < 0) }"; then + color="$YELLOW" + fi + + echo -e "${color}$(printf "%-40s %10s/s %10s/s %10s" "$name" "$base_ops" "$sdk_ops" "$diff_str")${NC}" + done + + log "==============================================================================" "$BLUE" + + rm -f "$BASELINE_OUTPUT" "$SDK_OUTPUT" + log "" + log "Benchmark complete" "$GREEN" + exit 0 +fi + +# Normal E2E test mode +log "================================================" "$BLUE" +log "Phase 2: Recording Traces" "$BLUE" +log "================================================" "$BLUE" + +log "Starting server in RECORD mode..." +TUSK_DRIFT_MODE=RECORD npm run dev & +SERVER_PID=$! +sleep "$SERVER_WAIT_TIME" + +log "Executing test requests..." +node /app/src/test_requests.mjs + +log "Waiting for traces to flush..." +sleep 3 + +log "Stopping server..." +stop_server +sleep 2 + +TRACE_COUNT=$(ls -1 .tusk/traces/*.jsonl 2>/dev/null | wc -l) +log "Recorded $TRACE_COUNT trace files" "$GREEN" + +if [ "$TRACE_COUNT" -eq 0 ]; then + log "ERROR: No traces recorded!" "$RED" + exit 1 +fi + +# ============================================================ +# Phase 3: Run Tusk Tests +# ============================================================ +log "================================================" "$BLUE" +log "Phase 3: Running Tusk Tests" "$BLUE" +log "================================================" "$BLUE" + +set +e +TEST_OUTPUT=$(TUSK_ANALYTICS_DISABLED=1 tusk run --print --output-format json --enable-service-logs 2>&1) +TUSK_EXIT=$? +set -e + +echo "$TEST_OUTPUT" + +# Check tusk exit code +if [ $TUSK_EXIT -ne 0 ]; then + log "Tusk tests failed with exit code $TUSK_EXIT" "$RED" + exit 1 +fi + +# Parse results - count passed/failed +ALL_PASSED=$(echo "$TEST_OUTPUT" | grep -c -E '"passed":[[:space:]]*true' || true) +ANY_FAILED=$(echo "$TEST_OUTPUT" | grep -c '"passed":\s*false' || true) + +log "================================================" +if [ "$ANY_FAILED" -gt 0 ]; then + log "Some tests failed!" "$RED" + exit 1 +elif [ "$ALL_PASSED" -gt 0 ]; then + log "All $ALL_PASSED tests passed!" "$GREEN" +else + log "No test results found" "$YELLOW" + exit 1 +fi + +# ============================================================ +# Phase 4: Check for warnings +# ============================================================ +log "================================================" "$BLUE" +log "Phase 4: Checking for Instrumentation Warnings" "$BLUE" +log "================================================" "$BLUE" + +if grep -r "TCP called from inbound request context" .tusk/logs/ 2>/dev/null; then + log "ERROR: Found TCP instrumentation warning!" "$RED" + exit 1 +else + log "No instrumentation warnings found" "$GREEN" +fi + +log "================================================" "$BLUE" +log "E2E Test Complete" "$GREEN" +log "================================================" "$BLUE" + +exit 0 diff --git a/src/instrumentation/libraries/e2e-common/test-utils.mjs b/src/instrumentation/libraries/e2e-common/test-utils.mjs new file mode 100644 index 00000000..abad39c8 --- /dev/null +++ b/src/instrumentation/libraries/e2e-common/test-utils.mjs @@ -0,0 +1,117 @@ +/** + * Shared test utilities for Node SDK E2E tests. + * Mirrors Python SDK's e2e_common/test_utils.py pattern. + * + * Usage in test_requests.mjs: + * import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + * await makeRequest('GET', '/health'); + * await makeRequest('POST', '/users/create', { body: { name: 'Test' } }); + * printRequestSummary(); + */ + +const PORT = process.env.PORT || '3000'; +const BASE_URL = `http://localhost:${PORT}`; +const BENCHMARK_MODE = !!process.env.BENCHMARKS; +const BENCHMARK_DURATION = parseInt(process.env.BENCHMARK_DURATION || '5', 10); +const BENCHMARK_WARMUP = parseInt(process.env.BENCHMARK_WARMUP || '3', 10); + +let totalRequestsSent = 0; + +/** + * Build fetch options for a request. + * Always sets Connection: close to match curl behavior (one TCP connection + * per request). This prevents HTTP keep-alive from confusing the SDK's + * TCP instrumentation, which can cause unpatched dependency alerts and + * trace mismatches. + */ +function buildFetchOptions(method, options) { + const fetchOptions = { + method, + headers: { 'Connection': 'close' }, + }; + if (options?.body) { + fetchOptions.headers['Content-Type'] = 'application/json'; + fetchOptions.body = JSON.stringify(options.body); + } + if (options?.headers) { + Object.assign(fetchOptions.headers, options.headers); + } + return fetchOptions; +} + +/** + * Run a benchmark loop for a single endpoint. + * Outputs Go-style stats: name count ns/op ops/s + */ +async function _benchmarkRequest(method, endpoint, options) { + const benchName = `Benchmark_${method}${endpoint.replace(/\//g, '_')}`; + const url = `${BASE_URL}${endpoint}`; + const fetchOptions = buildFetchOptions(method, options); + + // Warmup phase + const warmupEnd = Date.now() + BENCHMARK_WARMUP * 1000; + while (Date.now() < warmupEnd) { + try { + const resp = await fetch(url, fetchOptions); + await resp.text(); + } catch { + // ignore warmup errors + } + } + + // Timed phase + let count = 0; + const startNs = process.hrtime.bigint(); + const durationNs = BigInt(BENCHMARK_DURATION) * 1_000_000_000n; + const endNs = startNs + durationNs; + + while (process.hrtime.bigint() < endNs) { + try { + const resp = await fetch(url, fetchOptions); + await resp.text(); + count++; + } catch { + count++; + } + } + + const elapsedNs = Number(process.hrtime.bigint() - startNs); + totalRequestsSent += count; + + if (count > 0) { + const nsPerOp = Math.round(elapsedNs / count); + const opsPerSec = ((count * 1_000_000_000) / elapsedNs).toFixed(2); + const nameCol = benchName.padEnd(45); + const countCol = String(count).padStart(5); + const nsCol = String(nsPerOp).padStart(12); + const opsCol = String(opsPerSec).padStart(10); + console.log(`${nameCol} ${countCol} ${nsCol} ns/op ${opsCol} ops/s`); + } +} + +/** + * Make a request to the test server. + * In normal mode: single request. + * In benchmark mode: timed loop with warmup. + */ +export async function makeRequest(method, endpoint, options) { + if (BENCHMARK_MODE) { + await _benchmarkRequest(method, endpoint, options); + return; + } + + const url = `${BASE_URL}${endpoint}`; + const fetchOptions = buildFetchOptions(method, options); + + const resp = await fetch(url, fetchOptions); + await resp.text(); + totalRequestsSent++; +} + +/** + * Print summary of total requests sent. + * Parseable by base-entrypoint.sh. + */ +export function printRequestSummary() { + console.log(`TOTAL_REQUESTS_SENT:${totalRequestsSent}`); +} diff --git a/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/Dockerfile b/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/Dockerfile index 8b5bfdbb..596f6aff 100644 --- a/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/Dockerfile +++ b/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/Dockerfile @@ -20,5 +20,17 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ # Expose the server port EXPOSE 3000 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +# Copy entrypoint script +COPY src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/entrypoint.sh ./ +RUN chmod +x entrypoint.sh + +# Create .tusk directories +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs + +# Copy shared test infrastructure +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh + +# Run entrypoint +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/docker-compose.yml b/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/docker-compose.yml index b385e945..fe55c42f 100644 --- a/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/docker-compose.yml +++ b/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: app: build: @@ -8,19 +6,17 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" environment: - PORT=3000 - TUSK_ANALYTICS_DISABLED=1 + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-5} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} volumes: - # Mount SDK source for hot reload (this is what package.json expects) + # Mount SDK source for the SDK dependency - ../../../../../..:/sdk:ro - # Mount .tusk folder to persist traces - - ./.tusk:/app/.tusk - # Mount app source for development + # Mount app source - ./src:/app/src + # Mount .tusk config (but traces/logs are created inside container) + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro working_dir: /app - # Keep container running without starting the app - # The CLI will control starting/stopping the app - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/entrypoint.sh b/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/entrypoint.sh new file mode 100755 index 00000000..b2e6f53a --- /dev/null +++ b/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +SERVER_WAIT_TIME=5 +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/run.sh b/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/run.sh index c7601e7d..55cb5d5a 100755 --- a/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/run.sh +++ b/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/run.sh @@ -1,97 +1,52 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -# Generate unique docker compose project name PROJECT_NAME="fetch-cjs-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting Fetch E2E test run on port ${APP_PORT}..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker container -echo "Step 1: Starting docker container..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for container to be ready -echo "Waiting for container to be ready..." -sleep 3 - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "npm run build && npm run dev" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 5 - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all Fetch endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null - -echo " - GET /test/fetch-get" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/fetch-get > /dev/null - -echo " - POST /test/fetch-post" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"title":"test","body":"test body"}' http://localhost:3000/test/fetch-post > /dev/null - -echo " - GET /test/fetch-headers" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/fetch-headers > /dev/null - -echo " - GET /test/fetch-json" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/fetch-json > /dev/null - -echo " - GET /test/fetch-url-object" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/fetch-url-object > /dev/null - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 +# Colors +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: FETCH (CJS)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" +trap cleanup EXIT -# Step 7.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -# Step 8: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "Fetch E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/src/index.ts b/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/src/index.ts index aca5c728..ab0ce2e0 100644 --- a/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/src/index.ts +++ b/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/src/index.ts @@ -1,5 +1,5 @@ -import { TuskDrift } from './tdInit'; -import express, { Request, Response } from 'express'; +import { TuskDrift } from "./tdInit"; +import express, { Request, Response } from "express"; const app = express(); const PORT = process.env.PORT || 3000; @@ -12,9 +12,6 @@ app.get("/test/fetch-get", async (req: Request, res: Response) => { const response = await fetch("https://jsonplaceholder.typicode.com/posts/1"); const data = await response.json(); - console.log("data", data); - console.log("response", response); - res.json({ success: true, data: data, @@ -148,13 +145,13 @@ app.listen(PORT, () => { TuskDrift.markAppAsReady(); console.log(`Fetch integration test server running on port ${PORT}`); console.log(`Test mode: ${process.env.TUSK_DRIFT_MODE || "record"}`); - console.log('Available endpoints:'); - console.log(' GET /health - Health check'); - console.log(' GET /test/fetch-get - Test fetch GET'); - console.log(' POST /test/fetch-post - Test fetch POST'); - console.log(' GET /test/fetch-headers - Test fetch with custom headers'); - console.log(' GET /test/fetch-json - Test fetch with JSON response'); - console.log(' GET /test/fetch-url-object - Test fetch with URL object'); + console.log("Available endpoints:"); + console.log(" GET /health - Health check"); + console.log(" GET /test/fetch-get - Test fetch GET"); + console.log(" POST /test/fetch-post - Test fetch POST"); + console.log(" GET /test/fetch-headers - Test fetch with custom headers"); + console.log(" GET /test/fetch-json - Test fetch with JSON response"); + console.log(" GET /test/fetch-url-object - Test fetch with URL object"); }); // Graceful shutdown diff --git a/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/src/tdInit.ts b/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/src/tdInit.ts index 09b04a42..b7f7e9a6 100644 --- a/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/src/tdInit.ts +++ b/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/src/tdInit.ts @@ -1,9 +1,8 @@ -import { TuskDrift } from '@use-tusk/drift-node-sdk'; +import { TuskDrift } from "@use-tusk/drift-node-sdk"; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/src/test_requests.mjs b/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/src/test_requests.mjs new file mode 100644 index 00000000..8dfd1625 --- /dev/null +++ b/src/instrumentation/libraries/fetch/e2e-tests/cjs-fetch/src/test_requests.mjs @@ -0,0 +1,10 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('GET', '/test/fetch-get'); +await makeRequest('POST', '/test/fetch-post', { body: { title: 'test', body: 'test body' } }); +await makeRequest('GET', '/test/fetch-headers'); +await makeRequest('GET', '/test/fetch-json'); +await makeRequest('GET', '/test/fetch-url-object'); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/Dockerfile b/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/Dockerfile index 0e741a8b..67d9ce4b 100644 --- a/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/Dockerfile +++ b/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/Dockerfile @@ -20,5 +20,13 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ # Expose the server port EXPOSE 3000 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +COPY src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/entrypoint.sh ./ +RUN chmod +x entrypoint.sh +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs + +# Copy shared test infrastructure +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh + +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/docker-compose.yml b/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/docker-compose.yml index 063d9c8c..0511deda 100644 --- a/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/docker-compose.yml +++ b/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: app: build: @@ -8,19 +6,17 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" environment: - PORT=3000 - TUSK_ANALYTICS_DISABLED=1 + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-5} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} volumes: # Mount SDK source for hot reload (this is what package.json expects) - ../../../../../..:/sdk:ro - # Mount .tusk folder to persist traces - - ./.tusk:/app/.tusk + # Mount .tusk config + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro # Mount app source for development - ./src:/app/src working_dir: /app - # Keep container running without starting the app - # The CLI will control starting/stopping the app - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/entrypoint.sh b/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/entrypoint.sh new file mode 100755 index 00000000..b2e6f53a --- /dev/null +++ b/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +SERVER_WAIT_TIME=5 +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/run.sh b/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/run.sh index 5f256259..4cb7ab08 100755 --- a/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/run.sh +++ b/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/run.sh @@ -1,97 +1,51 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -# Generate unique docker compose project name PROJECT_NAME="fetch-esm-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting Fetch ESM E2E test run on port ${APP_PORT}..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker container -echo "Step 1: Starting docker container..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for container to be ready -echo "Waiting for container to be ready..." -sleep 3 - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "npm run build && npm run dev" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 5 - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all Fetch endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' -echo " - GET /test/fetch-get" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/fetch-get > /dev/null - -echo " - POST /test/fetch-post" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"title":"test","body":"test body"}' http://localhost:3000/test/fetch-post > /dev/null - -echo " - GET /test/fetch-headers" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/fetch-headers > /dev/null - -echo " - GET /test/fetch-json" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/fetch-json > /dev/null - -echo " - GET /test/fetch-url-object" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/fetch-url-object > /dev/null - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: FETCH (ESM)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" +trap cleanup EXIT -# Step 7.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -# Step 8: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "Fetch ESM E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/src/index.ts b/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/src/index.ts index be882422..dc91be66 100644 --- a/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/src/index.ts +++ b/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/src/index.ts @@ -1,5 +1,5 @@ -import { TuskDrift } from './tdInit.js'; -import express, { Request, Response } from 'express'; +import { TuskDrift } from "./tdInit.js"; +import express, { Request, Response } from "express"; const app = express(); const PORT = process.env.PORT || 3000; @@ -12,9 +12,6 @@ app.get("/test/fetch-get", async (req: Request, res: Response) => { const response = await fetch("https://jsonplaceholder.typicode.com/posts/1"); const data = await response.json(); - console.log("data", data); - console.log("response", response); - res.json({ success: true, data: data, @@ -148,13 +145,13 @@ app.listen(PORT, () => { TuskDrift.markAppAsReady(); console.log(`Fetch integration test server running on port ${PORT}`); console.log(`Test mode: ${process.env.TUSK_DRIFT_MODE || "record"}`); - console.log('Available endpoints:'); - console.log(' GET /health - Health check'); - console.log(' GET /test/fetch-get - Test fetch GET'); - console.log(' POST /test/fetch-post - Test fetch POST'); - console.log(' GET /test/fetch-headers - Test fetch with custom headers'); - console.log(' GET /test/fetch-json - Test fetch with JSON response'); - console.log(' GET /test/fetch-url-object - Test fetch with URL object'); + console.log("Available endpoints:"); + console.log(" GET /health - Health check"); + console.log(" GET /test/fetch-get - Test fetch GET"); + console.log(" POST /test/fetch-post - Test fetch POST"); + console.log(" GET /test/fetch-headers - Test fetch with custom headers"); + console.log(" GET /test/fetch-json - Test fetch with JSON response"); + console.log(" GET /test/fetch-url-object - Test fetch with URL object"); }); // Graceful shutdown diff --git a/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/src/tdInit.ts b/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/src/tdInit.ts index cf11e0f1..d8e4a51f 100644 --- a/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/src/tdInit.ts +++ b/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/src/tdInit.ts @@ -10,7 +10,6 @@ import { TuskDrift } from '@use-tusk/drift-node-sdk'; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/src/test_requests.mjs b/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/src/test_requests.mjs new file mode 100644 index 00000000..8dfd1625 --- /dev/null +++ b/src/instrumentation/libraries/fetch/e2e-tests/esm-fetch/src/test_requests.mjs @@ -0,0 +1,10 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('GET', '/test/fetch-get'); +await makeRequest('POST', '/test/fetch-post', { body: { title: 'test', body: 'test body' } }); +await makeRequest('GET', '/test/fetch-headers'); +await makeRequest('GET', '/test/fetch-json'); +await makeRequest('GET', '/test/fetch-url-object'); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/Dockerfile b/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/Dockerfile index 61dd5601..4d40c397 100644 --- a/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/Dockerfile +++ b/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/Dockerfile @@ -20,5 +20,10 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ # Expose the server port EXPOSE 3000 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +COPY src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/entrypoint.sh ./ +RUN chmod +x entrypoint.sh +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/docker-compose.yml b/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/docker-compose.yml index 2b0ed114..78b7fee8 100644 --- a/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/docker-compose.yml +++ b/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: app: build: @@ -8,24 +6,22 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" environment: - PORT=3000 - FIREBASE_PROJECT_ID=${FIREBASE_PROJECT_ID} - FIREBASE_SERVICE_ACCOUNT=${FIREBASE_SERVICE_ACCOUNT} - TUSK_ANALYTICS_DISABLED=1 + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-5} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} volumes: # Mount SDK source for hot reload (this is what package.json expects) - ../../../../../..:/sdk:ro - # Mount .tusk folder to persist traces - - ./.tusk:/app/.tusk + # Mount .tusk config + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro # Mount app source for development - ./src:/app/src working_dir: /app - # Keep container running without starting the app - # The CLI will control starting/stopping the app - command: tail -f /dev/null # # # Instead of using a real firebase project, we could use a local emulator for cjs-firestore/esm-firestore e2e tests. diff --git a/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/entrypoint.sh b/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/entrypoint.sh new file mode 100755 index 00000000..5bdb66e6 --- /dev/null +++ b/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/entrypoint.sh @@ -0,0 +1,13 @@ +#!/bin/bash +SERVER_WAIT_TIME=10 +setup_library() { + if [ -z "$FIREBASE_PROJECT_ID" ]; then + echo -e "\033[0;31mERROR: FIREBASE_PROJECT_ID environment variable is not set\033[0m" + exit 1 + fi + if [ -z "$FIREBASE_SERVICE_ACCOUNT" ]; then + echo -e "\033[0;31mERROR: FIREBASE_SERVICE_ACCOUNT environment variable is not set\033[0m" + exit 1 + fi +} +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/run.sh b/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/run.sh index 6e03cd4e..45af8741 100755 --- a/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/run.sh +++ b/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/run.sh @@ -1,126 +1,62 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -# Generate unique docker compose project name PROJECT_NAME="firestore-cjs-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' -echo "Starting Firestore E2E test run on port ${APP_PORT}..." +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: FIRESTORE (CJS)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" # Check for required environment variables if [ -z "$FIREBASE_PROJECT_ID" ]; then - echo "Error: FIREBASE_PROJECT_ID environment variable is not set" + echo -e "${RED}Error: FIREBASE_PROJECT_ID environment variable is not set${NC}" exit 1 fi if [ -z "$FIREBASE_SERVICE_ACCOUNT" ]; then - echo "Error: FIREBASE_SERVICE_ACCOUNT environment variable is not set" + echo -e "${RED}Error: FIREBASE_SERVICE_ACCOUNT environment variable is not set${NC}" exit 1 fi -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker container (app only) -echo "Step 1: Starting docker container..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for container to be ready -echo "Waiting for container to be ready..." -sleep 5 - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "npm run build && npm run dev" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 10 - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all Firestore endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null - -echo " - GET /document/get" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/document/get > /dev/null - -echo " - POST /document/create" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"name":"Created User","email":"created@example.com"}' http://localhost:3000/document/create > /dev/null - -echo " - POST /document/set" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"name":"Set User","email":"set@example.com"}' http://localhost:3000/document/set > /dev/null - -echo " - PUT /document/update" -docker compose -p $PROJECT_NAME exec -T app curl -s -X PUT -H "Content-Type: application/json" -d '{"name":"Updated User"}' http://localhost:3000/document/update > /dev/null - -echo " - DELETE /document/delete" -docker compose -p $PROJECT_NAME exec -T app curl -s -X DELETE http://localhost:3000/document/delete > /dev/null +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -echo " - POST /collection/add" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"name":"Product A","price":99.99}' http://localhost:3000/collection/add > /dev/null +trap cleanup EXIT -echo " - POST /collection/doc-autoid" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"name":"Auto Product","price":49.99}' http://localhost:3000/collection/doc-autoid > /dev/null +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -echo " - GET /query/get" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/query/get > /dev/null - -echo " - POST /transaction/increment" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/transaction/increment > /dev/null - -echo " - POST /transaction/transfer" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/transaction/transfer > /dev/null - -echo " - POST /batch/write" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/batch/write > /dev/null - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 - -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) - -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" - -# Step 7.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" - -# Step 8: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "Firestore E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/src/tdInit.ts b/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/src/tdInit.ts index 09b04a42..9b1acad7 100644 --- a/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/src/tdInit.ts +++ b/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/src/tdInit.ts @@ -3,7 +3,6 @@ import { TuskDrift } from '@use-tusk/drift-node-sdk'; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/src/test_requests.mjs b/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/src/test_requests.mjs new file mode 100644 index 00000000..704c570b --- /dev/null +++ b/src/instrumentation/libraries/firestore/e2e-tests/cjs-firestore/src/test_requests.mjs @@ -0,0 +1,16 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('GET', '/document/get'); +await makeRequest('POST', '/document/create', { body: { name: 'Test User', email: 'test@example.com' } }); +await makeRequest('POST', '/document/set', { body: { name: 'Set User', email: 'set@example.com' } }); +await makeRequest('PUT', '/document/update', { body: { name: 'Updated User' } }); +await makeRequest('DELETE', '/document/delete'); +await makeRequest('POST', '/collection/add', { body: { name: 'New Product', price: 49.99 } }); +await makeRequest('POST', '/collection/doc-autoid', { body: { name: 'Auto Product', price: 29.99 } }); +await makeRequest('GET', '/query/get'); +await makeRequest('POST', '/transaction/increment'); +await makeRequest('POST', '/transaction/transfer'); +await makeRequest('POST', '/batch/write'); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/Dockerfile b/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/Dockerfile index 915de7dc..39284759 100644 --- a/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/Dockerfile +++ b/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/Dockerfile @@ -23,5 +23,10 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ # Expose the server port EXPOSE 3000 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +COPY src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/entrypoint.sh ./ +RUN chmod +x entrypoint.sh +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/docker-compose.yml b/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/docker-compose.yml index 2069a138..d77bb3a7 100644 --- a/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/docker-compose.yml +++ b/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: app: build: @@ -8,21 +6,19 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" environment: - PORT=3000 - FIREBASE_PROJECT_ID=${FIREBASE_PROJECT_ID} - FIREBASE_SERVICE_ACCOUNT=${FIREBASE_SERVICE_ACCOUNT} - TUSK_ANALYTICS_DISABLED=1 + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-5} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} volumes: # Mount SDK source for hot reload (this is what package.json expects) - ../../../../../..:/sdk:ro - # Mount .tusk folder to persist traces - - ./.tusk:/app/.tusk + # Mount .tusk config + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro # Mount app source for development - ./src:/app/src working_dir: /app - # Keep container running without starting the app - # The CLI will control starting/stopping the app - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/entrypoint.sh b/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/entrypoint.sh new file mode 100755 index 00000000..5bdb66e6 --- /dev/null +++ b/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/entrypoint.sh @@ -0,0 +1,13 @@ +#!/bin/bash +SERVER_WAIT_TIME=10 +setup_library() { + if [ -z "$FIREBASE_PROJECT_ID" ]; then + echo -e "\033[0;31mERROR: FIREBASE_PROJECT_ID environment variable is not set\033[0m" + exit 1 + fi + if [ -z "$FIREBASE_SERVICE_ACCOUNT" ]; then + echo -e "\033[0;31mERROR: FIREBASE_SERVICE_ACCOUNT environment variable is not set\033[0m" + exit 1 + fi +} +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/run.sh b/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/run.sh index ad72c222..8c55980a 100755 --- a/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/run.sh +++ b/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/run.sh @@ -1,129 +1,62 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -# Generate unique docker compose project name PROJECT_NAME="firestore-esm-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' -echo "Starting Firestore ESM E2E test run on port ${APP_PORT}..." +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: FIRESTORE (ESM)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" # Check for required environment variables if [ -z "$FIREBASE_PROJECT_ID" ]; then - echo "Error: FIREBASE_PROJECT_ID environment variable is not set" + echo -e "${RED}Error: FIREBASE_PROJECT_ID environment variable is not set${NC}" exit 1 fi if [ -z "$FIREBASE_SERVICE_ACCOUNT" ]; then - echo "Error: FIREBASE_SERVICE_ACCOUNT environment variable is not set" + echo -e "${RED}Error: FIREBASE_SERVICE_ACCOUNT environment variable is not set${NC}" exit 1 fi -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker container (app only) -echo "Step 1: Starting docker container..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for container to be ready -echo "Waiting for container to be ready..." -sleep 5 - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "npm run build && npm run dev" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 10 - -echo "Checking server logs..." -docker compose -p $PROJECT_NAME logs app - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all Firestore endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null - -echo " - GET /document/get" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/document/get > /dev/null - -echo " - POST /document/create" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"name":"Created User","email":"created@example.com"}' http://localhost:3000/document/create > /dev/null - -echo " - POST /document/set" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"name":"Set User","email":"set@example.com"}' http://localhost:3000/document/set > /dev/null - -echo " - PUT /document/update" -docker compose -p $PROJECT_NAME exec -T app curl -s -X PUT -H "Content-Type: application/json" -d '{"name":"Updated User"}' http://localhost:3000/document/update > /dev/null +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -echo " - DELETE /document/delete" -docker compose -p $PROJECT_NAME exec -T app curl -s -X DELETE http://localhost:3000/document/delete > /dev/null +trap cleanup EXIT -echo " - POST /collection/add" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"name":"Product A","price":99.99}' http://localhost:3000/collection/add > /dev/null +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -echo " - POST /collection/doc-autoid" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"name":"Auto Product","price":49.99}' http://localhost:3000/collection/doc-autoid > /dev/null - -echo " - GET /query/get" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/query/get > /dev/null - -echo " - POST /transaction/increment" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/transaction/increment > /dev/null - -echo " - POST /transaction/transfer" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/transaction/transfer > /dev/null - -echo " - POST /batch/write" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/batch/write > /dev/null - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 - -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) - -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" - -# Step 7.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" - -# Step 8: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "Firestore ESM E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/src/tdInit.ts b/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/src/tdInit.ts index cf11e0f1..d8e4a51f 100644 --- a/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/src/tdInit.ts +++ b/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/src/tdInit.ts @@ -10,7 +10,6 @@ import { TuskDrift } from '@use-tusk/drift-node-sdk'; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/src/test_requests.mjs b/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/src/test_requests.mjs new file mode 100644 index 00000000..704c570b --- /dev/null +++ b/src/instrumentation/libraries/firestore/e2e-tests/esm-firestore/src/test_requests.mjs @@ -0,0 +1,16 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('GET', '/document/get'); +await makeRequest('POST', '/document/create', { body: { name: 'Test User', email: 'test@example.com' } }); +await makeRequest('POST', '/document/set', { body: { name: 'Set User', email: 'set@example.com' } }); +await makeRequest('PUT', '/document/update', { body: { name: 'Updated User' } }); +await makeRequest('DELETE', '/document/delete'); +await makeRequest('POST', '/collection/add', { body: { name: 'New Product', price: 49.99 } }); +await makeRequest('POST', '/collection/doc-autoid', { body: { name: 'Auto Product', price: 29.99 } }); +await makeRequest('GET', '/query/get'); +await makeRequest('POST', '/transaction/increment'); +await makeRequest('POST', '/transaction/transfer'); +await makeRequest('POST', '/batch/write'); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/Dockerfile b/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/Dockerfile index 1f82232b..ebc5fd63 100644 --- a/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/Dockerfile +++ b/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/Dockerfile @@ -21,5 +21,10 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ EXPOSE 3000 EXPOSE 50051 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +COPY src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/entrypoint.sh ./ +RUN chmod +x entrypoint.sh +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/docker-compose.yml b/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/docker-compose.yml index c1d686c6..9a712fa8 100644 --- a/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/docker-compose.yml +++ b/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: app: build: @@ -8,10 +6,10 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" - - "${GRPC_PORT:-50051}:50051" environment: + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-5} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} - PORT=3000 - GRPC_SERVER_ADDRESS=localhost:50051 - TUSK_ANALYTICS_DISABLED=1 @@ -19,10 +17,7 @@ services: # Mount SDK source for hot reload (this is what package.json expects) - ../../../../../..:/sdk:ro # Mount .tusk folder to persist traces - - ./.tusk:/app/.tusk + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro # Mount app source for development - ./src:/app/src working_dir: /app - # Keep container running without starting the app - # The CLI will control starting/stopping the app - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/entrypoint.sh b/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/entrypoint.sh new file mode 100755 index 00000000..188f3789 --- /dev/null +++ b/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +SERVER_WAIT_TIME=10 +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/run.sh b/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/run.sh index e11e0e48..c90b8a2a 100755 --- a/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/run.sh +++ b/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/run.sh @@ -1,155 +1,51 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -GRPC_PORT=$((APP_PORT + 40000)) # Offset gRPC port to avoid conflicts -export GRPC_PORT -# Generate unique docker compose project name PROJECT_NAME="grpc-cjs-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting gRPC E2E test run on port ${APP_PORT} (gRPC: ${GRPC_PORT})..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker container -echo "Step 1: Starting docker container..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for container to be ready -echo "Waiting for container to be ready..." -sleep 5 - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "npm run build && npm run dev" - -# Wait for server to start (gRPC server needs a bit more time) -echo "Waiting for server to start..." -sleep 10 - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all gRPC endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null - -# Greeter service endpoints -echo " - GET /greet/hello" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/greet/hello > /dev/null - -echo " - GET /greet/hello-with-metadata" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/greet/hello-with-metadata > /dev/null - -echo " - POST /greet/custom" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"name":"CustomUser","greeting_type":"casual"}' http://localhost:3000/greet/custom > /dev/null - -echo " - GET /greet/hello-again" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/greet/hello-again > /dev/null - -echo " - GET /greet/many-times" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/greet/many-times > /dev/null - -# Calculator service endpoints -echo " - GET /calc/add" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/calc/add > /dev/null - -echo " - GET /calc/subtract" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/calc/subtract > /dev/null - -echo " - GET /calc/multiply" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/calc/multiply > /dev/null - -echo " - POST /calc/divide" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"num1":20,"num2":4}' http://localhost:3000/calc/divide > /dev/null +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' -echo " - GET /calc/divide-by-zero (expected error)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/calc/divide-by-zero > /dev/null - -# User service endpoints -echo " - GET /users/1" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/users/1 > /dev/null - -echo " - POST /users" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"name":"Test User","email":"testuser@example.com","age":28,"roles":["user","tester"]}' http://localhost:3000/users > /dev/null - -echo " - PUT /users/1" -docker compose -p $PROJECT_NAME exec -T app curl -s -X PUT -H "Content-Type: application/json" -d '{"name":"Alice Updated","email":"alice.updated@example.com","age":31}' http://localhost:3000/users/1 > /dev/null - -echo " - GET /users (list with pagination)" -docker compose -p $PROJECT_NAME exec -T app curl -s "http://localhost:3000/users?limit=5&offset=0" > /dev/null - -echo " - DELETE /users/2" -docker compose -p $PROJECT_NAME exec -T app curl -s -X DELETE http://localhost:3000/users/2 > /dev/null - -# Test endpoints -echo " - GET /test/user-not-found (expected error)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/user-not-found > /dev/null - -echo " - GET /test/sequential-calls" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/sequential-calls > /dev/null - -echo " - POST /test/complex-data" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/complex-data > /dev/null - -# File service endpoints (testing binary data handling) -echo " - POST /files/upload (testing binary data)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/files/upload > /dev/null - -echo " - GET /files/download/file_1 (testing binary data)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/files/download/file_1 > /dev/null - -echo " - GET /test/unary-callback-only" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/unary-callback-only > /dev/null - -echo " - GET /test/unary-options-only" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/unary-options-only > /dev/null - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: GRPC (CJS)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" +trap cleanup EXIT -# Step 6.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -# Step 8: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "gRPC E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/src/grpc/server.ts b/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/src/grpc/server.ts index 5514f4c9..44527313 100644 --- a/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/src/grpc/server.ts +++ b/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/src/grpc/server.ts @@ -81,7 +81,6 @@ userIdCounter = 3; // Greeter service implementation function sayHello(call: any, callback: any) { - console.log("[gRPC Server] SayHello called with:", call.request); const name = call.request.name || "World"; const greetingType = call.request.greeting_type || "formal"; @@ -102,7 +101,6 @@ function sayHello(call: any, callback: any) { } function sayHelloAgain(call: any, callback: any) { - console.log("[gRPC Server] SayHelloAgain called with:", call.request); const name = call.request.name || "World"; callback(null, { message: `Hello again, ${name}!`, @@ -112,7 +110,6 @@ function sayHelloAgain(call: any, callback: any) { } function greetManyTimes(call: any, callback: any) { - console.log("[gRPC Server] GreetManyTimes called with:", call.request); const name = call.request.name || "World"; callback(null, { message: `Greetings, ${name}! Nice to see you multiple times!`, @@ -123,7 +120,6 @@ function greetManyTimes(call: any, callback: any) { // Calculator service implementation function add(call: any, callback: any) { - console.log("[gRPC Server] Add called with:", call.request); const { num1, num2 } = call.request; const result = num1 + num2; callback(null, { @@ -135,7 +131,6 @@ function add(call: any, callback: any) { } function subtract(call: any, callback: any) { - console.log("[gRPC Server] Subtract called with:", call.request); const { num1, num2 } = call.request; const result = num1 - num2; callback(null, { @@ -147,7 +142,6 @@ function subtract(call: any, callback: any) { } function multiply(call: any, callback: any) { - console.log("[gRPC Server] Multiply called with:", call.request); const { num1, num2 } = call.request; const result = num1 * num2; callback(null, { @@ -159,7 +153,6 @@ function multiply(call: any, callback: any) { } function divide(call: any, callback: any) { - console.log("[gRPC Server] Divide called with:", call.request); const { num1, num2 } = call.request; if (num2 === 0) { @@ -182,7 +175,6 @@ function divide(call: any, callback: any) { // User service implementation function getUser(call: any, callback: any) { - console.log("[gRPC Server] GetUser called with:", call.request); const userId = call.request.id; const user = users.get(userId); @@ -199,7 +191,6 @@ function getUser(call: any, callback: any) { } function createUser(call: any, callback: any) { - console.log("[gRPC Server] CreateUser called with:", call.request); const newUser = { id: userIdCounter++, name: call.request.name, @@ -218,7 +209,6 @@ function createUser(call: any, callback: any) { } function updateUser(call: any, callback: any) { - console.log("[gRPC Server] UpdateUser called with:", call.request); const userId = call.request.id; const user = users.get(userId); @@ -248,7 +238,6 @@ function updateUser(call: any, callback: any) { } function deleteUser(call: any, callback: any) { - console.log("[gRPC Server] DeleteUser called with:", call.request); const userId = call.request.id; const user = users.get(userId); @@ -269,7 +258,6 @@ function deleteUser(call: any, callback: any) { } function listUsers(call: any, callback: any) { - console.log("[gRPC Server] ListUsers called with:", call.request); const limit = call.request.limit || 10; const offset = call.request.offset || 0; @@ -288,15 +276,7 @@ const files = new Map { // Simple hello with basic request app.get("/greet/hello", async (req: Request, res: Response) => { try { - console.log("[Express] /greet/hello called"); const response: any = await grpcCallPromise(greeterClient, "SayHello", { name: "World", greeting_type: "formal", @@ -57,7 +56,6 @@ app.get("/greet/hello", async (req: Request, res: Response) => { // Hello with metadata app.get("/greet/hello-with-metadata", async (req: Request, res: Response) => { try { - console.log("[Express] /greet/hello-with-metadata called"); const metadata = new grpc.Metadata(); metadata.add("user-id", "test-user-123"); metadata.add("request-id", "req-456"); @@ -95,7 +93,6 @@ app.get("/greet/hello-with-metadata", async (req: Request, res: Response) => { // Custom greeting app.post("/greet/custom", async (req: Request, res: Response) => { try { - console.log("[Express] /greet/custom called with body:", req.body); const { name, greeting_type } = req.body; const response: any = await grpcCallPromise(greeterClient, "SayHello", { @@ -120,7 +117,6 @@ app.post("/greet/custom", async (req: Request, res: Response) => { // Say hello again app.get("/greet/hello-again", async (req: Request, res: Response) => { try { - console.log("[Express] /greet/hello-again called"); const response: any = await grpcCallPromise(greeterClient, "SayHelloAgain", { name: "Bob", greeting_type: "casual", @@ -143,7 +139,6 @@ app.get("/greet/hello-again", async (req: Request, res: Response) => { // Greet many times app.get("/greet/many-times", async (req: Request, res: Response) => { try { - console.log("[Express] /greet/many-times called"); const response: any = await grpcCallPromise(greeterClient, "GreetManyTimes", { name: "Charlie", greeting_type: "formal", @@ -168,7 +163,6 @@ app.get("/greet/many-times", async (req: Request, res: Response) => { // Add operation app.get("/calc/add", async (req: Request, res: Response) => { try { - console.log("[Express] /calc/add called"); const response: any = await grpcCallPromise(calculatorClient, "Add", { num1: 10.5, num2: 5.3, @@ -192,7 +186,6 @@ app.get("/calc/add", async (req: Request, res: Response) => { // Subtract operation app.get("/calc/subtract", async (req: Request, res: Response) => { try { - console.log("[Express] /calc/subtract called"); const response: any = await grpcCallPromise(calculatorClient, "Subtract", { num1: 20.0, num2: 7.5, @@ -216,7 +209,6 @@ app.get("/calc/subtract", async (req: Request, res: Response) => { // Multiply operation app.get("/calc/multiply", async (req: Request, res: Response) => { try { - console.log("[Express] /calc/multiply called"); const response: any = await grpcCallPromise(calculatorClient, "Multiply", { num1: 3.5, num2: 4.0, @@ -240,7 +232,6 @@ app.get("/calc/multiply", async (req: Request, res: Response) => { // Divide operation (with error handling) app.post("/calc/divide", async (req: Request, res: Response) => { try { - console.log("[Express] /calc/divide called with body:", req.body); const { num1, num2 } = req.body; const response: any = await grpcCallPromise(calculatorClient, "Divide", { @@ -268,7 +259,6 @@ app.post("/calc/divide", async (req: Request, res: Response) => { // Test division by zero error app.get("/calc/divide-by-zero", async (req: Request, res: Response) => { try { - console.log("[Express] /calc/divide-by-zero called"); const response: any = await grpcCallPromise(calculatorClient, "Divide", { num1: 10, num2: 0, @@ -297,7 +287,6 @@ app.get("/calc/divide-by-zero", async (req: Request, res: Response) => { app.get("/users/:id", async (req: Request, res: Response) => { try { const userId = parseInt(req.params.id); - console.log("[Express] /users/:id called with id:", userId); const response: any = await grpcCallPromise(userClient, "GetUser", { id: userId }); @@ -319,7 +308,6 @@ app.get("/users/:id", async (req: Request, res: Response) => { // Create user app.post("/users", async (req: Request, res: Response) => { try { - console.log("[Express] POST /users called with body:", req.body); const { name, email, age, roles } = req.body; const response: any = await grpcCallPromise(userClient, "CreateUser", { @@ -347,7 +335,6 @@ app.post("/users", async (req: Request, res: Response) => { app.put("/users/:id", async (req: Request, res: Response) => { try { const userId = parseInt(req.params.id); - console.log("[Express] PUT /users/:id called with id:", userId, "body:", req.body); const { name, email, age, roles } = req.body; const response: any = await grpcCallPromise(userClient, "UpdateUser", { @@ -377,7 +364,6 @@ app.put("/users/:id", async (req: Request, res: Response) => { app.delete("/users/:id", async (req: Request, res: Response) => { try { const userId = parseInt(req.params.id); - console.log("[Express] DELETE /users/:id called with id:", userId); const response: any = await grpcCallPromise(userClient, "DeleteUser", { id: userId }); @@ -401,7 +387,6 @@ app.get("/users", async (req: Request, res: Response) => { try { const limit = parseInt(req.query.limit as string) || 10; const offset = parseInt(req.query.offset as string) || 0; - console.log("[Express] GET /users called with limit:", limit, "offset:", offset); const response: any = await grpcCallPromise(userClient, "ListUsers", { limit, @@ -427,7 +412,6 @@ app.get("/users", async (req: Request, res: Response) => { // Test user not found error app.get("/test/user-not-found", async (req: Request, res: Response) => { try { - console.log("[Express] /test/user-not-found called"); const response: any = await grpcCallPromise(userClient, "GetUser", { id: 99999 }); res.json({ @@ -449,8 +433,6 @@ app.get("/test/user-not-found", async (req: Request, res: Response) => { // Test multiple sequential calls app.get("/test/sequential-calls", async (req: Request, res: Response) => { try { - console.log("[Express] /test/sequential-calls called"); - // Make multiple gRPC calls sequentially const greeting: any = await grpcCallPromise(greeterClient, "SayHello", { name: "Sequential Test", @@ -486,8 +468,6 @@ app.get("/test/sequential-calls", async (req: Request, res: Response) => { // Test with complex nested data app.post("/test/complex-data", async (req: Request, res: Response) => { try { - console.log("[Express] /test/complex-data called"); - const response: any = await grpcCallPromise(userClient, "CreateUser", { name: "Complex User", email: "complex@example.com", @@ -512,8 +492,6 @@ app.post("/test/complex-data", async (req: Request, res: Response) => { // Upload file with binary content app.post("/files/upload", async (req: Request, res: Response) => { try { - console.log("[Express] POST /files/upload called"); - // Create binary data to upload const binaryContent = Buffer.from([ 0x89, @@ -578,7 +556,6 @@ app.post("/files/upload", async (req: Request, res: Response) => { app.get("/files/download/:fileId", async (req: Request, res: Response) => { try { const fileId = req.params.fileId; - console.log("[Express] GET /files/download/:fileId called with:", fileId); const response: any = await grpcCallPromise(fileClient, "DownloadFile", { file_id: fileId, diff --git a/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/src/tdInit.ts b/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/src/tdInit.ts index 09b04a42..9b1acad7 100644 --- a/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/src/tdInit.ts +++ b/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/src/tdInit.ts @@ -3,7 +3,6 @@ import { TuskDrift } from '@use-tusk/drift-node-sdk'; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/src/test_requests.mjs b/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/src/test_requests.mjs new file mode 100644 index 00000000..c77ed440 --- /dev/null +++ b/src/instrumentation/libraries/grpc/e2e-tests/cjs-grpc/src/test_requests.mjs @@ -0,0 +1,27 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('GET', '/greet/hello'); +await makeRequest('GET', '/greet/hello-with-metadata'); +await makeRequest('POST', '/greet/custom', { body: { name: 'CustomUser', greeting_type: 'casual' } }); +await makeRequest('GET', '/greet/hello-again'); +await makeRequest('GET', '/greet/many-times'); +await makeRequest('GET', '/calc/add'); +await makeRequest('GET', '/calc/subtract'); +await makeRequest('GET', '/calc/multiply'); +await makeRequest('POST', '/calc/divide', { body: { num1: 20, num2: 4 } }); +await makeRequest('GET', '/calc/divide-by-zero'); +await makeRequest('GET', '/users/1'); +await makeRequest('POST', '/users', { body: { name: 'Test User', email: 'testuser@example.com', age: 28, roles: ['user', 'tester'] } }); +await makeRequest('PUT', '/users/1', { body: { name: 'Alice Updated', email: 'alice.updated@example.com', age: 31 } }); +await makeRequest('GET', '/users?limit=5&offset=0'); +await makeRequest('DELETE', '/users/2'); +await makeRequest('GET', '/test/user-not-found'); +await makeRequest('GET', '/test/sequential-calls'); +await makeRequest('POST', '/test/complex-data'); +await makeRequest('POST', '/files/upload'); +await makeRequest('GET', '/files/download/file_1'); +await makeRequest('GET', '/test/unary-callback-only'); +await makeRequest('GET', '/test/unary-options-only'); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/Dockerfile b/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/Dockerfile index 34210a20..5d1a9e37 100644 --- a/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/Dockerfile +++ b/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/Dockerfile @@ -21,5 +21,10 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ EXPOSE 3000 EXPOSE 50051 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +COPY src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/entrypoint.sh ./ +RUN chmod +x entrypoint.sh +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/docker-compose.yml b/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/docker-compose.yml index c23ed79d..44c9a873 100644 --- a/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/docker-compose.yml +++ b/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: app: build: @@ -8,10 +6,10 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" - - "${GRPC_PORT:-50051}:50051" environment: + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-5} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} - PORT=3000 - GRPC_SERVER_ADDRESS=localhost:50051 - TUSK_ANALYTICS_DISABLED=1 @@ -19,10 +17,7 @@ services: # Mount SDK source for hot reload (this is what package.json expects) - ../../../../../..:/sdk:ro # Mount .tusk folder to persist traces - - ./.tusk:/app/.tusk + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro # Mount app source for development - ./src:/app/src working_dir: /app - # Keep container running without starting the app - # The CLI will control starting/stopping the app - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/entrypoint.sh b/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/entrypoint.sh new file mode 100755 index 00000000..188f3789 --- /dev/null +++ b/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +SERVER_WAIT_TIME=10 +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/run.sh b/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/run.sh index 48971356..5bcbeca7 100755 --- a/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/run.sh +++ b/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/run.sh @@ -1,155 +1,51 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -GRPC_PORT=$((APP_PORT + 40000)) # Offset gRPC port to avoid conflicts -export GRPC_PORT -# Generate unique docker compose project name PROJECT_NAME="grpc-esm-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting gRPC ESM E2E test run on port ${APP_PORT} (gRPC: ${GRPC_PORT})..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker container -echo "Step 1: Starting docker container..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for container to be ready -echo "Waiting for container to be ready..." -sleep 5 - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "npm run build && npm run dev" - -# Wait for server to start (gRPC server needs a bit more time) -echo "Waiting for server to start..." -sleep 10 - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all gRPC endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null - -# Greeter service endpoints -echo " - GET /greet/hello" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/greet/hello > /dev/null - -echo " - GET /greet/hello-with-metadata" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/greet/hello-with-metadata > /dev/null - -echo " - POST /greet/custom" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"name":"CustomUser","greeting_type":"casual"}' http://localhost:3000/greet/custom > /dev/null - -echo " - GET /greet/hello-again" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/greet/hello-again > /dev/null - -echo " - GET /greet/many-times" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/greet/many-times > /dev/null - -# Calculator service endpoints -echo " - GET /calc/add" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/calc/add > /dev/null - -echo " - GET /calc/subtract" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/calc/subtract > /dev/null - -echo " - GET /calc/multiply" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/calc/multiply > /dev/null - -echo " - POST /calc/divide" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"num1":20,"num2":4}' http://localhost:3000/calc/divide > /dev/null +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' -echo " - GET /calc/divide-by-zero (expected error)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/calc/divide-by-zero > /dev/null - -# User service endpoints -echo " - GET /users/1" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/users/1 > /dev/null - -echo " - POST /users" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"name":"Test User","email":"testuser@example.com","age":28,"roles":["user","tester"]}' http://localhost:3000/users > /dev/null - -echo " - PUT /users/1" -docker compose -p $PROJECT_NAME exec -T app curl -s -X PUT -H "Content-Type: application/json" -d '{"name":"Alice Updated","email":"alice.updated@example.com","age":31}' http://localhost:3000/users/1 > /dev/null - -echo " - GET /users (list with pagination)" -docker compose -p $PROJECT_NAME exec -T app curl -s "http://localhost:3000/users?limit=5&offset=0" > /dev/null - -echo " - DELETE /users/2" -docker compose -p $PROJECT_NAME exec -T app curl -s -X DELETE http://localhost:3000/users/2 > /dev/null - -# Test endpoints -echo " - GET /test/user-not-found (expected error)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/user-not-found > /dev/null - -echo " - GET /test/sequential-calls" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/sequential-calls > /dev/null - -echo " - POST /test/complex-data" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/complex-data > /dev/null - -# File service endpoints (testing binary data handling) -echo " - POST /files/upload (testing binary data)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/files/upload > /dev/null - -echo " - GET /files/download/file_1 (testing binary data)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/files/download/file_1 > /dev/null - -echo " - GET /test/unary-callback-only" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/unary-callback-only > /dev/null - -echo " - GET /test/unary-options-only" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/unary-options-only > /dev/null - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: GRPC (ESM)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" +trap cleanup EXIT -# Step 6.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -# Step 8: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "gRPC ESM E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/src/grpc/server.ts b/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/src/grpc/server.ts index 858be3cb..451fe54b 100644 --- a/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/src/grpc/server.ts +++ b/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/src/grpc/server.ts @@ -86,7 +86,6 @@ userIdCounter = 3; // Greeter service implementation function sayHello(call: any, callback: any) { - console.log("[gRPC Server] SayHello called with:", call.request); const name = call.request.name || "World"; const greetingType = call.request.greeting_type || "formal"; @@ -107,7 +106,6 @@ function sayHello(call: any, callback: any) { } function sayHelloAgain(call: any, callback: any) { - console.log("[gRPC Server] SayHelloAgain called with:", call.request); const name = call.request.name || "World"; callback(null, { message: `Hello again, ${name}!`, @@ -117,7 +115,6 @@ function sayHelloAgain(call: any, callback: any) { } function greetManyTimes(call: any, callback: any) { - console.log("[gRPC Server] GreetManyTimes called with:", call.request); const name = call.request.name || "World"; callback(null, { message: `Greetings, ${name}! Nice to see you multiple times!`, @@ -128,7 +125,6 @@ function greetManyTimes(call: any, callback: any) { // Calculator service implementation function add(call: any, callback: any) { - console.log("[gRPC Server] Add called with:", call.request); const { num1, num2 } = call.request; const result = num1 + num2; callback(null, { @@ -140,7 +136,6 @@ function add(call: any, callback: any) { } function subtract(call: any, callback: any) { - console.log("[gRPC Server] Subtract called with:", call.request); const { num1, num2 } = call.request; const result = num1 - num2; callback(null, { @@ -152,7 +147,6 @@ function subtract(call: any, callback: any) { } function multiply(call: any, callback: any) { - console.log("[gRPC Server] Multiply called with:", call.request); const { num1, num2 } = call.request; const result = num1 * num2; callback(null, { @@ -164,7 +158,6 @@ function multiply(call: any, callback: any) { } function divide(call: any, callback: any) { - console.log("[gRPC Server] Divide called with:", call.request); const { num1, num2 } = call.request; if (num2 === 0) { @@ -187,7 +180,6 @@ function divide(call: any, callback: any) { // User service implementation function getUser(call: any, callback: any) { - console.log("[gRPC Server] GetUser called with:", call.request); const userId = call.request.id; const user = users.get(userId); @@ -204,7 +196,6 @@ function getUser(call: any, callback: any) { } function createUser(call: any, callback: any) { - console.log("[gRPC Server] CreateUser called with:", call.request); const newUser = { id: userIdCounter++, name: call.request.name, @@ -223,7 +214,6 @@ function createUser(call: any, callback: any) { } function updateUser(call: any, callback: any) { - console.log("[gRPC Server] UpdateUser called with:", call.request); const userId = call.request.id; const user = users.get(userId); @@ -253,7 +243,6 @@ function updateUser(call: any, callback: any) { } function deleteUser(call: any, callback: any) { - console.log("[gRPC Server] DeleteUser called with:", call.request); const userId = call.request.id; const user = users.get(userId); @@ -274,7 +263,6 @@ function deleteUser(call: any, callback: any) { } function listUsers(call: any, callback: any) { - console.log("[gRPC Server] ListUsers called with:", call.request); const limit = call.request.limit || 10; const offset = call.request.offset || 0; @@ -293,15 +281,8 @@ const files = new Map { // Simple hello with basic request app.get("/greet/hello", async (req: Request, res: Response) => { try { - console.log("[Express] /greet/hello called"); const response: any = await clients.grpcCallPromise( clients.clientsObj.greeterClient, "SayHello", @@ -54,7 +53,6 @@ app.get("/greet/hello", async (req: Request, res: Response) => { // Hello with metadata app.get("/greet/hello-with-metadata", async (req: Request, res: Response) => { try { - console.log("[Express] /greet/hello-with-metadata called"); const metadata = new grpc.Metadata(); metadata.add("user-id", "test-user-123"); metadata.add("request-id", "req-456"); @@ -92,7 +90,6 @@ app.get("/greet/hello-with-metadata", async (req: Request, res: Response) => { // Custom greeting app.post("/greet/custom", async (req: Request, res: Response) => { try { - console.log("[Express] /greet/custom called with body:", req.body); const { name, greeting_type } = req.body; const response: any = await clients.grpcCallPromise( @@ -121,7 +118,6 @@ app.post("/greet/custom", async (req: Request, res: Response) => { // Say hello again app.get("/greet/hello-again", async (req: Request, res: Response) => { try { - console.log("[Express] /greet/hello-again called"); const response: any = await clients.grpcCallPromise( clients.clientsObj.greeterClient, "SayHelloAgain", @@ -148,7 +144,6 @@ app.get("/greet/hello-again", async (req: Request, res: Response) => { // Greet many times app.get("/greet/many-times", async (req: Request, res: Response) => { try { - console.log("[Express] /greet/many-times called"); const response: any = await clients.grpcCallPromise( clients.clientsObj.greeterClient, "GreetManyTimes", @@ -175,7 +170,6 @@ app.get("/greet/many-times", async (req: Request, res: Response) => { // Add operation app.get("/calc/add", async (req: Request, res: Response) => { try { - console.log("[Express] /calc/add called"); const response: any = await clients.grpcCallPromise( clients.clientsObj.calculatorClient, "Add", @@ -203,7 +197,6 @@ app.get("/calc/add", async (req: Request, res: Response) => { // Subtract operation app.get("/calc/subtract", async (req: Request, res: Response) => { try { - console.log("[Express] /calc/subtract called"); const response: any = await clients.grpcCallPromise( clients.clientsObj.calculatorClient, "Subtract", @@ -231,7 +224,6 @@ app.get("/calc/subtract", async (req: Request, res: Response) => { // Multiply operation app.get("/calc/multiply", async (req: Request, res: Response) => { try { - console.log("[Express] /calc/multiply called"); const response: any = await clients.grpcCallPromise( clients.clientsObj.calculatorClient, "Multiply", @@ -259,7 +251,6 @@ app.get("/calc/multiply", async (req: Request, res: Response) => { // Divide operation (with error handling) app.post("/calc/divide", async (req: Request, res: Response) => { try { - console.log("[Express] /calc/divide called with body:", req.body); const { num1, num2 } = req.body; const response: any = await clients.grpcCallPromise( @@ -291,7 +282,6 @@ app.post("/calc/divide", async (req: Request, res: Response) => { // Test division by zero error app.get("/calc/divide-by-zero", async (req: Request, res: Response) => { try { - console.log("[Express] /calc/divide-by-zero called"); const response: any = await clients.grpcCallPromise( clients.clientsObj.calculatorClient, "Divide", @@ -324,7 +314,6 @@ app.get("/calc/divide-by-zero", async (req: Request, res: Response) => { app.get("/users/:id", async (req: Request, res: Response) => { try { const userId = parseInt(req.params.id); - console.log("[Express] /users/:id called with id:", userId); const response: any = await clients.grpcCallPromise(clients.clientsObj.userClient, "GetUser", { id: userId, @@ -348,7 +337,6 @@ app.get("/users/:id", async (req: Request, res: Response) => { // Create user app.post("/users", async (req: Request, res: Response) => { try { - console.log("[Express] POST /users called with body:", req.body); const { name, email, age, roles } = req.body; const response: any = await clients.grpcCallPromise( @@ -380,7 +368,6 @@ app.post("/users", async (req: Request, res: Response) => { app.put("/users/:id", async (req: Request, res: Response) => { try { const userId = parseInt(req.params.id); - console.log("[Express] PUT /users/:id called with id:", userId, "body:", req.body); const { name, email, age, roles } = req.body; const response: any = await clients.grpcCallPromise( @@ -414,7 +401,6 @@ app.put("/users/:id", async (req: Request, res: Response) => { app.delete("/users/:id", async (req: Request, res: Response) => { try { const userId = parseInt(req.params.id); - console.log("[Express] DELETE /users/:id called with id:", userId); const response: any = await clients.grpcCallPromise( clients.clientsObj.userClient, @@ -444,7 +430,6 @@ app.get("/users", async (req: Request, res: Response) => { try { const limit = parseInt(req.query.limit as string) || 10; const offset = parseInt(req.query.offset as string) || 0; - console.log("[Express] GET /users called with limit:", limit, "offset:", offset); const response: any = await clients.grpcCallPromise( clients.clientsObj.userClient, @@ -474,7 +459,6 @@ app.get("/users", async (req: Request, res: Response) => { // Test user not found error app.get("/test/user-not-found", async (req: Request, res: Response) => { try { - console.log("[Express] /test/user-not-found called"); const response: any = await clients.grpcCallPromise(clients.clientsObj.userClient, "GetUser", { id: 99999, }); @@ -498,8 +482,6 @@ app.get("/test/user-not-found", async (req: Request, res: Response) => { // Test multiple sequential calls app.get("/test/sequential-calls", async (req: Request, res: Response) => { try { - console.log("[Express] /test/sequential-calls called"); - // Make multiple gRPC calls sequentially const greeting: any = await clients.grpcCallPromise( clients.clientsObj.greeterClient, @@ -545,8 +527,6 @@ app.get("/test/sequential-calls", async (req: Request, res: Response) => { // Test with complex nested data app.post("/test/complex-data", async (req: Request, res: Response) => { try { - console.log("[Express] /test/complex-data called"); - const response: any = await clients.grpcCallPromise( clients.clientsObj.userClient, "CreateUser", @@ -575,8 +555,6 @@ app.post("/test/complex-data", async (req: Request, res: Response) => { // Upload file with binary content app.post("/files/upload", async (req: Request, res: Response) => { try { - console.log("[Express] POST /files/upload called"); - // Create binary data to upload const binaryContent = Buffer.from([ 0x89, @@ -645,8 +623,6 @@ app.post("/files/upload", async (req: Request, res: Response) => { app.get("/files/download/:fileId", async (req: Request, res: Response) => { try { const fileId = req.params.fileId; - console.log("[Express] GET /files/download/:fileId called with:", fileId); - const response: any = await clients.grpcCallPromise( clients.clientsObj.fileClient, "DownloadFile", diff --git a/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/src/tdInit.ts b/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/src/tdInit.ts index cf11e0f1..d8e4a51f 100644 --- a/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/src/tdInit.ts +++ b/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/src/tdInit.ts @@ -10,7 +10,6 @@ import { TuskDrift } from '@use-tusk/drift-node-sdk'; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/src/test_requests.mjs b/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/src/test_requests.mjs new file mode 100644 index 00000000..c77ed440 --- /dev/null +++ b/src/instrumentation/libraries/grpc/e2e-tests/esm-grpc/src/test_requests.mjs @@ -0,0 +1,27 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('GET', '/greet/hello'); +await makeRequest('GET', '/greet/hello-with-metadata'); +await makeRequest('POST', '/greet/custom', { body: { name: 'CustomUser', greeting_type: 'casual' } }); +await makeRequest('GET', '/greet/hello-again'); +await makeRequest('GET', '/greet/many-times'); +await makeRequest('GET', '/calc/add'); +await makeRequest('GET', '/calc/subtract'); +await makeRequest('GET', '/calc/multiply'); +await makeRequest('POST', '/calc/divide', { body: { num1: 20, num2: 4 } }); +await makeRequest('GET', '/calc/divide-by-zero'); +await makeRequest('GET', '/users/1'); +await makeRequest('POST', '/users', { body: { name: 'Test User', email: 'testuser@example.com', age: 28, roles: ['user', 'tester'] } }); +await makeRequest('PUT', '/users/1', { body: { name: 'Alice Updated', email: 'alice.updated@example.com', age: 31 } }); +await makeRequest('GET', '/users?limit=5&offset=0'); +await makeRequest('DELETE', '/users/2'); +await makeRequest('GET', '/test/user-not-found'); +await makeRequest('GET', '/test/sequential-calls'); +await makeRequest('POST', '/test/complex-data'); +await makeRequest('POST', '/files/upload'); +await makeRequest('GET', '/files/download/file_1'); +await makeRequest('GET', '/test/unary-callback-only'); +await makeRequest('GET', '/test/unary-options-only'); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/http/e2e-tests/cjs-http/Dockerfile b/src/instrumentation/libraries/http/e2e-tests/cjs-http/Dockerfile index e54ca161..614b1463 100644 --- a/src/instrumentation/libraries/http/e2e-tests/cjs-http/Dockerfile +++ b/src/instrumentation/libraries/http/e2e-tests/cjs-http/Dockerfile @@ -6,6 +6,13 @@ WORKDIR /app COPY src/instrumentation/libraries/http/e2e-tests/cjs-http/package*.json ./ COPY src/instrumentation/libraries/http/e2e-tests/cjs-http/tsconfig.json ./ +# Copy entrypoint script +COPY src/instrumentation/libraries/http/e2e-tests/cjs-http/entrypoint.sh ./ +RUN chmod +x entrypoint.sh + +# Create .tusk directories +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs + # Add cache-busting argument to force fresh CLI download ARG CACHEBUST=1 ARG TUSK_CLI_VERSION=latest @@ -20,5 +27,10 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ # Expose the server port EXPOSE 3000 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +# Copy shared test infrastructure +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh + +# Run entrypoint (like Python SDK) +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/http/e2e-tests/cjs-http/docker-compose.yml b/src/instrumentation/libraries/http/e2e-tests/cjs-http/docker-compose.yml index d1c23fd3..e202e9e3 100644 --- a/src/instrumentation/libraries/http/e2e-tests/cjs-http/docker-compose.yml +++ b/src/instrumentation/libraries/http/e2e-tests/cjs-http/docker-compose.yml @@ -8,19 +8,17 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" environment: - PORT=3000 - TUSK_ANALYTICS_DISABLED=1 + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-5} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} volumes: - # Mount SDK source for hot reload (this is what package.json expects) + # Mount SDK source for the SDK dependency - ../../../../../..:/sdk:ro - # Mount .tusk folder to persist traces - - ./.tusk:/app/.tusk - # Mount app source for development + # Mount app source - ./src:/app/src + # Mount .tusk config (but traces/logs are created inside container) + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro working_dir: /app - # Keep container running without starting the app - # The CLI will control starting/stopping the app - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/http/e2e-tests/cjs-http/entrypoint.sh b/src/instrumentation/libraries/http/e2e-tests/cjs-http/entrypoint.sh new file mode 100755 index 00000000..b2e6f53a --- /dev/null +++ b/src/instrumentation/libraries/http/e2e-tests/cjs-http/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +SERVER_WAIT_TIME=5 +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/http/e2e-tests/cjs-http/run.sh b/src/instrumentation/libraries/http/e2e-tests/cjs-http/run.sh index f0e859d0..5e081718 100755 --- a/src/instrumentation/libraries/http/e2e-tests/cjs-http/run.sh +++ b/src/instrumentation/libraries/http/e2e-tests/cjs-http/run.sh @@ -1,6 +1,4 @@ #!/bin/bash - -# Exit on error set -e # Accept optional port parameter (default: 3000) @@ -10,94 +8,51 @@ export APP_PORT # Generate unique docker compose project name PROJECT_NAME="http-cjs-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting HTTP E2E test run on port ${APP_PORT}..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker container -echo "Step 1: Starting docker container..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for container to be ready -echo "Waiting for container to be ready..." -sleep 3 - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "npm run build && npm run dev" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 5 - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null - -echo " - GET /test-http-get" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test-http-get > /dev/null - -echo " - POST /test-http-request" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test-http-request > /dev/null - -echo " - GET /test-https-get" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test-https-get > /dev/null - -echo " - GET /test-axios-get" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test-axios-get > /dev/null - -echo " - POST /test-axios-post" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test-axios-post > /dev/null - -echo " - GET /test-url-object-get" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test-url-object-get > /dev/null - -echo " - POST /test-url-object-request" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test-url-object-request > /dev/null - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 +# Colors +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: HTTP (CJS)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) +# Cleanup function +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" +# Register cleanup on exit +trap cleanup EXIT -# Step 6.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" +# Build containers +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -# Step 8: Clean up +# Run the test container (like Python SDK's `docker compose run --rm app`) +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}✓ Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}✗ Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/http/e2e-tests/cjs-http/src/index.ts b/src/instrumentation/libraries/http/e2e-tests/cjs-http/src/index.ts index b2834dbe..be1a587c 100644 --- a/src/instrumentation/libraries/http/e2e-tests/cjs-http/src/index.ts +++ b/src/instrumentation/libraries/http/e2e-tests/cjs-http/src/index.ts @@ -8,8 +8,6 @@ const server = http.createServer(async (req, res) => { const url = req.url || "/"; const method = req.method || "GET"; - console.log(`Received request: ${method} ${url}`); - try { // Test raw http.get if (url === "/test-http-get" && method === "GET") { diff --git a/src/instrumentation/libraries/http/e2e-tests/cjs-http/src/tdInit.ts b/src/instrumentation/libraries/http/e2e-tests/cjs-http/src/tdInit.ts index 09b04a42..9b1acad7 100644 --- a/src/instrumentation/libraries/http/e2e-tests/cjs-http/src/tdInit.ts +++ b/src/instrumentation/libraries/http/e2e-tests/cjs-http/src/tdInit.ts @@ -3,7 +3,6 @@ import { TuskDrift } from '@use-tusk/drift-node-sdk'; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/http/e2e-tests/cjs-http/src/test_requests.mjs b/src/instrumentation/libraries/http/e2e-tests/cjs-http/src/test_requests.mjs new file mode 100644 index 00000000..9ee70a9a --- /dev/null +++ b/src/instrumentation/libraries/http/e2e-tests/cjs-http/src/test_requests.mjs @@ -0,0 +1,12 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('GET', '/test-http-get'); +await makeRequest('POST', '/test-http-request'); +await makeRequest('GET', '/test-https-get'); +await makeRequest('GET', '/test-axios-get'); +await makeRequest('POST', '/test-axios-post'); +await makeRequest('GET', '/test-url-object-get'); +await makeRequest('POST', '/test-url-object-request'); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/http/e2e-tests/esm-http/Dockerfile b/src/instrumentation/libraries/http/e2e-tests/esm-http/Dockerfile index abb10a19..1c39e3e4 100644 --- a/src/instrumentation/libraries/http/e2e-tests/esm-http/Dockerfile +++ b/src/instrumentation/libraries/http/e2e-tests/esm-http/Dockerfile @@ -6,6 +6,13 @@ WORKDIR /app COPY src/instrumentation/libraries/http/e2e-tests/esm-http/package*.json ./ COPY src/instrumentation/libraries/http/e2e-tests/esm-http/tsconfig.json ./ +# Copy entrypoint script +COPY src/instrumentation/libraries/http/e2e-tests/esm-http/entrypoint.sh ./ +RUN chmod +x entrypoint.sh + +# Create .tusk directories +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs + # Add cache-busting argument to force fresh CLI download ARG CACHEBUST=1 ARG TUSK_CLI_VERSION=latest @@ -20,5 +27,10 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ # Expose the server port EXPOSE 3000 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +# Copy shared test infrastructure +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh + +# Run entrypoint (like Python SDK) +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/http/e2e-tests/esm-http/docker-compose.yml b/src/instrumentation/libraries/http/e2e-tests/esm-http/docker-compose.yml index c089be92..feaca731 100644 --- a/src/instrumentation/libraries/http/e2e-tests/esm-http/docker-compose.yml +++ b/src/instrumentation/libraries/http/e2e-tests/esm-http/docker-compose.yml @@ -8,19 +8,17 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" environment: - PORT=3000 - TUSK_ANALYTICS_DISABLED=1 + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-5} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} volumes: - # Mount SDK source for hot reload (this is what package.json expects) + # Mount SDK source for the SDK dependency - ../../../../../..:/sdk:ro - # Mount .tusk folder to persist traces - - ./.tusk:/app/.tusk - # Mount app source for development + # Mount app source - ./src:/app/src + # Mount .tusk config (but traces/logs are created inside container) + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro working_dir: /app - # Keep container running without starting the app - # The CLI will control starting/stopping the app - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/http/e2e-tests/esm-http/entrypoint.sh b/src/instrumentation/libraries/http/e2e-tests/esm-http/entrypoint.sh new file mode 100755 index 00000000..b2e6f53a --- /dev/null +++ b/src/instrumentation/libraries/http/e2e-tests/esm-http/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +SERVER_WAIT_TIME=5 +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/http/e2e-tests/esm-http/run.sh b/src/instrumentation/libraries/http/e2e-tests/esm-http/run.sh index 444ad857..f7d0102e 100755 --- a/src/instrumentation/libraries/http/e2e-tests/esm-http/run.sh +++ b/src/instrumentation/libraries/http/e2e-tests/esm-http/run.sh @@ -1,6 +1,4 @@ #!/bin/bash - -# Exit on error set -e # Accept optional port parameter (default: 3000) @@ -10,94 +8,51 @@ export APP_PORT # Generate unique docker compose project name PROJECT_NAME="http-esm-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting HTTP ESM E2E test run on port ${APP_PORT}..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker container -echo "Step 1: Starting docker container..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for container to be ready -echo "Waiting for container to be ready..." -sleep 3 - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "npm run build && npm run dev" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 5 - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null - -echo " - GET /test-http-get" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test-http-get > /dev/null - -echo " - POST /test-http-request" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test-http-request > /dev/null - -echo " - GET /test-https-get" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test-https-get > /dev/null - -echo " - GET /test-axios-get" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test-axios-get > /dev/null - -echo " - POST /test-axios-post" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test-axios-post > /dev/null - -echo " - GET /test-url-object-get" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test-url-object-get > /dev/null - -echo " - POST /test-url-object-request" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test-url-object-request > /dev/null - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 +# Colors +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: HTTP (ESM)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) +# Cleanup function +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" +# Register cleanup on exit +trap cleanup EXIT -# Step 6.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" +# Build containers +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -# Step 8: Clean up +# Run the test container (like Python SDK's `docker compose run --rm app`) +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "HTTP ESM E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}✓ Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}✗ Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/http/e2e-tests/esm-http/src/index.ts b/src/instrumentation/libraries/http/e2e-tests/esm-http/src/index.ts index 8f09ac77..6d6a7075 100644 --- a/src/instrumentation/libraries/http/e2e-tests/esm-http/src/index.ts +++ b/src/instrumentation/libraries/http/e2e-tests/esm-http/src/index.ts @@ -8,8 +8,6 @@ const server = http.createServer(async (req, res) => { const url = req.url || "/"; const method = req.method || "GET"; - console.log(`Received request: ${method} ${url}`); - try { // Test raw http.get if (url === "/test-http-get" && method === "GET") { diff --git a/src/instrumentation/libraries/http/e2e-tests/esm-http/src/tdInit.ts b/src/instrumentation/libraries/http/e2e-tests/esm-http/src/tdInit.ts index cf11e0f1..d8e4a51f 100644 --- a/src/instrumentation/libraries/http/e2e-tests/esm-http/src/tdInit.ts +++ b/src/instrumentation/libraries/http/e2e-tests/esm-http/src/tdInit.ts @@ -10,7 +10,6 @@ import { TuskDrift } from '@use-tusk/drift-node-sdk'; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/http/e2e-tests/esm-http/src/test_requests.mjs b/src/instrumentation/libraries/http/e2e-tests/esm-http/src/test_requests.mjs new file mode 100644 index 00000000..9ee70a9a --- /dev/null +++ b/src/instrumentation/libraries/http/e2e-tests/esm-http/src/test_requests.mjs @@ -0,0 +1,12 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('GET', '/test-http-get'); +await makeRequest('POST', '/test-http-request'); +await makeRequest('GET', '/test-https-get'); +await makeRequest('GET', '/test-axios-get'); +await makeRequest('POST', '/test-axios-post'); +await makeRequest('GET', '/test-url-object-get'); +await makeRequest('POST', '/test-url-object-request'); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/http/e2e-tests/run-all.sh b/src/instrumentation/libraries/http/e2e-tests/run-all.sh index a24c68f1..3d49b42d 100755 --- a/src/instrumentation/libraries/http/e2e-tests/run-all.sh +++ b/src/instrumentation/libraries/http/e2e-tests/run-all.sh @@ -11,4 +11,5 @@ source "$SCRIPT_DIR/../../e2e-common/e2e-helpers.sh" # Run all E2E tests for http # Accepts optional base port parameter (default: 3000) -run_all_e2e_tests "$SCRIPT_DIR" "http" "${1:-3000}" +# Use sequential mode to avoid port conflicts and for clearer output +run_all_e2e_tests "$SCRIPT_DIR" "http" "${1:-3000}" "sequential" diff --git a/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/Dockerfile b/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/Dockerfile index 819aa3b8..c96647eb 100644 --- a/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/Dockerfile +++ b/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/Dockerfile @@ -20,5 +20,10 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ # Expose the server port EXPOSE 3000 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +COPY src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/entrypoint.sh ./ +RUN chmod +x entrypoint.sh +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/docker-compose.yml b/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/docker-compose.yml index f848dc14..d6569b9c 100644 --- a/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/docker-compose.yml +++ b/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: redis: image: redis:7-alpine @@ -16,24 +14,22 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" environment: - PORT=3000 - REDIS_HOST=redis - REDIS_PORT=6379 - TUSK_ANALYTICS_DISABLED=1 + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-5} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} volumes: # Mount SDK source for hot reload (this is what package.json expects) - ../../../../../..:/sdk:ro - # Mount .tusk folder to persist traces - - ./.tusk:/app/.tusk + # Mount .tusk config file to persist configuration + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro # Mount app source for development - ./src:/app/src working_dir: /app depends_on: redis: condition: service_healthy - # Keep container running without starting the app - # The CLI will control starting/stopping the app - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/entrypoint.sh b/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/entrypoint.sh new file mode 100755 index 00000000..ed1dce09 --- /dev/null +++ b/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +SERVER_WAIT_TIME=8 +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/run.sh b/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/run.sh index 46955fdc..89d5d240 100755 --- a/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/run.sh +++ b/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/run.sh @@ -1,214 +1,51 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -# Generate unique docker compose project name PROJECT_NAME="ioredis-cjs-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting ioredis E2E test run on port ${APP_PORT}..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker containers (redis + app) -echo "Step 1: Starting docker containers..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for containers to be ready -echo "Waiting for containers to be ready..." -sleep 5 - -# Wait for Redis to be healthy -echo "Waiting for Redis to be healthy..." -until docker compose -p $PROJECT_NAME exec -T redis redis-cli ping > /dev/null 2>&1; do - echo " Redis is not ready yet..." - sleep 2 -done -echo "Redis is ready!" - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' -# Check app directory structure -echo "Checking app directory structure..." -echo "Contents of /app:" -docker compose -p $PROJECT_NAME exec -T app ls -la /app +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: IOREDIS (CJS)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" echo "" -echo "Contents of /app/src:" -docker compose -p $PROJECT_NAME exec -T app ls -la /app/src || echo " (src directory not found)" -echo "" -echo "Package.json scripts:" -docker compose -p $PROJECT_NAME exec -T app cat /app/package.json | grep -A 10 '"scripts"' || echo " (could not read package.json)" -echo "" - -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "cd /app && npm run build >> /tmp/server.log 2>&1 && npm run dev >> /tmp/server.log 2>&1" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 8 - -# Show initial server output -echo "Initial server output:" -docker compose -p $PROJECT_NAME exec -T app cat /tmp/server.log 2>/dev/null || echo " (no output yet)" - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all ioredis endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null - -echo " - GET /test/get" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/get > /dev/null - -echo " - POST /test/set" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:newkey", "value": "newvalue"}' http://localhost:3000/test/set > /dev/null - -echo " - POST /test/del" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:newkey"}' http://localhost:3000/test/del > /dev/null - -echo " - POST /test/exists" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:key1"}' http://localhost:3000/test/exists > /dev/null - -echo " - POST /test/expire" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:key1", "seconds": 100}' http://localhost:3000/test/expire > /dev/null - -echo " - POST /test/ttl" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:key1"}' http://localhost:3000/test/ttl > /dev/null - -echo " - GET /test/incr" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/incr > /dev/null - -echo " - GET /test/decr" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/decr > /dev/null - -echo " - GET /test/mget" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/mget > /dev/null - -echo " - POST /test/mset" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/mset > /dev/null - -echo " - GET /test/hget" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/hget > /dev/null - -echo " - POST /test/hset" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:user:2", "field": "name", "value": "Jane Doe"}' http://localhost:3000/test/hset > /dev/null - -echo " - GET /test/hgetall" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/hgetall > /dev/null - -echo " - POST /test/hdel" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:user:1", "field": "age"}' http://localhost:3000/test/hdel > /dev/null -echo " - POST /test/lpush" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:list", "value": "item0"}' http://localhost:3000/test/lpush > /dev/null +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -echo " - POST /test/rpush" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:list", "value": "item4"}' http://localhost:3000/test/rpush > /dev/null +trap cleanup EXIT -echo " - POST /test/lpop" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:list"}' http://localhost:3000/test/lpop > /dev/null +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -echo " - POST /test/rpop" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:list"}' http://localhost:3000/test/rpop > /dev/null - -echo " - GET /test/lrange" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/lrange > /dev/null - -echo " - POST /test/llen" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:list"}' http://localhost:3000/test/llen > /dev/null - -echo " - POST /test/sadd" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:set", "member": "member4"}' http://localhost:3000/test/sadd > /dev/null - -echo " - POST /test/srem" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:set", "member": "member4"}' http://localhost:3000/test/srem > /dev/null - -echo " - GET /test/smembers" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/smembers > /dev/null - -echo " - POST /test/sismember" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:set", "member": "member1"}' http://localhost:3000/test/sismember > /dev/null - -echo " - POST /test/zadd" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:zset", "score": 4, "member": "score4"}' http://localhost:3000/test/zadd > /dev/null - -echo " - GET /test/zrange" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zrange > /dev/null - -echo " - POST /test/zrem" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:zset", "member": "score4"}' http://localhost:3000/test/zrem > /dev/null - -echo " - POST /test/zscore" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:zset", "member": "score1"}' http://localhost:3000/test/zscore > /dev/null - -echo " - POST /test/keys" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"pattern": "test:*"}' http://localhost:3000/test/keys > /dev/null - -echo " - POST /test/flushdb" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/flushdb > /dev/null - -echo " - GET /test/ping" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/ping > /dev/null - -echo " - GET /test/pipeline" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pipeline > /dev/null - -echo " - GET /test/multi" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/multi > /dev/null - -echo " - GET /test/new-client" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/new-client > /dev/null - -echo " - GET /test/getbuffer" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/getbuffer > /dev/null - -echo " - GET /test/mgetbuffer" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/mgetbuffer > /dev/null - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 - -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) - -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" - -# Step 7.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" - -# Step 8: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "ioredis E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/src/tdInit.ts b/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/src/tdInit.ts index 09b04a42..9b1acad7 100644 --- a/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/src/tdInit.ts +++ b/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/src/tdInit.ts @@ -3,7 +3,6 @@ import { TuskDrift } from '@use-tusk/drift-node-sdk'; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/src/test_requests.mjs b/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/src/test_requests.mjs new file mode 100644 index 00000000..5a141eb2 --- /dev/null +++ b/src/instrumentation/libraries/ioredis/e2e-tests/cjs-ioredis/src/test_requests.mjs @@ -0,0 +1,41 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('GET', '/test/get'); +await makeRequest('POST', '/test/set', { body: { key: 'test:newkey', value: 'newvalue' } }); +await makeRequest('POST', '/test/del', { body: { key: 'test:newkey' } }); +await makeRequest('POST', '/test/exists', { body: { key: 'test:key1' } }); +await makeRequest('POST', '/test/expire', { body: { key: 'test:key1', seconds: 100 } }); +await makeRequest('POST', '/test/ttl', { body: { key: 'test:key1' } }); +await makeRequest('GET', '/test/incr'); +await makeRequest('GET', '/test/decr'); +await makeRequest('GET', '/test/mget'); +await makeRequest('POST', '/test/mset'); +await makeRequest('GET', '/test/hget'); +await makeRequest('POST', '/test/hset', { body: { key: 'test:user:2', field: 'name', value: 'Jane Doe' } }); +await makeRequest('GET', '/test/hgetall'); +await makeRequest('POST', '/test/hdel', { body: { key: 'test:user:1', field: 'age' } }); +await makeRequest('POST', '/test/lpush', { body: { key: 'test:list', value: 'item0' } }); +await makeRequest('POST', '/test/rpush', { body: { key: 'test:list', value: 'item4' } }); +await makeRequest('POST', '/test/lpop', { body: { key: 'test:list' } }); +await makeRequest('POST', '/test/rpop', { body: { key: 'test:list' } }); +await makeRequest('GET', '/test/lrange'); +await makeRequest('POST', '/test/llen', { body: { key: 'test:list' } }); +await makeRequest('POST', '/test/sadd', { body: { key: 'test:set', member: 'member4' } }); +await makeRequest('POST', '/test/srem', { body: { key: 'test:set', member: 'member4' } }); +await makeRequest('GET', '/test/smembers'); +await makeRequest('POST', '/test/sismember', { body: { key: 'test:set', member: 'member1' } }); +await makeRequest('POST', '/test/zadd', { body: { key: 'test:zset', score: 4, member: 'score4' } }); +await makeRequest('GET', '/test/zrange'); +await makeRequest('POST', '/test/zrem', { body: { key: 'test:zset', member: 'score4' } }); +await makeRequest('POST', '/test/zscore', { body: { key: 'test:zset', member: 'score1' } }); +await makeRequest('POST', '/test/keys', { body: { pattern: 'test:*' } }); +await makeRequest('POST', '/test/flushdb'); +await makeRequest('GET', '/test/ping'); +await makeRequest('GET', '/test/pipeline'); +await makeRequest('GET', '/test/multi'); +await makeRequest('GET', '/test/new-client'); +await makeRequest('GET', '/test/getbuffer'); +await makeRequest('GET', '/test/mgetbuffer'); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/Dockerfile b/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/Dockerfile index c99102e7..0d196b02 100644 --- a/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/Dockerfile +++ b/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/Dockerfile @@ -20,5 +20,10 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ # Expose the server port EXPOSE 3000 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +COPY src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/entrypoint.sh ./ +RUN chmod +x entrypoint.sh +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/docker-compose.yml b/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/docker-compose.yml index 8e396e64..93262e2b 100644 --- a/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/docker-compose.yml +++ b/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: redis: image: redis:7-alpine @@ -16,24 +14,22 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" environment: - PORT=3000 - REDIS_HOST=redis - REDIS_PORT=6379 - TUSK_ANALYTICS_DISABLED=1 + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-5} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} volumes: # Mount SDK source for hot reload (this is what package.json expects) - ../../../../../..:/sdk:ro - # Mount .tusk folder to persist traces - - ./.tusk:/app/.tusk + # Mount .tusk config file to persist configuration + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro # Mount app source for development - ./src:/app/src working_dir: /app depends_on: redis: condition: service_healthy - # Keep container running without starting the app - # The CLI will control starting/stopping the app - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/entrypoint.sh b/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/entrypoint.sh new file mode 100755 index 00000000..ed1dce09 --- /dev/null +++ b/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +SERVER_WAIT_TIME=8 +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/run.sh b/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/run.sh index ce44427a..d10d67e7 100755 --- a/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/run.sh +++ b/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/run.sh @@ -1,198 +1,51 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -# Generate unique docker compose project name PROJECT_NAME="ioredis-esm-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting ioredis ESM E2E test run on port ${APP_PORT}..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker containers (redis + app) -echo "Step 1: Starting docker containers..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for containers to be ready -echo "Waiting for containers to be ready..." -sleep 5 - -# Wait for Redis to be healthy -echo "Waiting for Redis to be healthy..." -until docker compose -p $PROJECT_NAME exec -T redis redis-cli ping > /dev/null 2>&1; do - echo " Redis is not ready yet..." - sleep 2 -done -echo "Redis is ready!" - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "npm run build && npm run dev" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 8 - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all ioredis endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null - -echo " - GET /test/get" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/get > /dev/null - -echo " - POST /test/set" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:newkey", "value": "newvalue"}' http://localhost:3000/test/set > /dev/null - -echo " - POST /test/del" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:newkey"}' http://localhost:3000/test/del > /dev/null - -echo " - POST /test/exists" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:key1"}' http://localhost:3000/test/exists > /dev/null - -echo " - POST /test/expire" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:key1", "seconds": 100}' http://localhost:3000/test/expire > /dev/null - -echo " - POST /test/ttl" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:key1"}' http://localhost:3000/test/ttl > /dev/null - -echo " - GET /test/incr" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/incr > /dev/null - -echo " - GET /test/decr" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/decr > /dev/null - -echo " - GET /test/mget" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/mget > /dev/null - -echo " - POST /test/mset" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/mset > /dev/null - -echo " - GET /test/hget" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/hget > /dev/null - -echo " - POST /test/hset" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:user:2", "field": "name", "value": "Jane Doe"}' http://localhost:3000/test/hset > /dev/null - -echo " - GET /test/hgetall" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/hgetall > /dev/null - -echo " - POST /test/hdel" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:user:1", "field": "age"}' http://localhost:3000/test/hdel > /dev/null - -echo " - POST /test/lpush" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:list", "value": "item0"}' http://localhost:3000/test/lpush > /dev/null +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' -echo " - POST /test/rpush" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:list", "value": "item4"}' http://localhost:3000/test/rpush > /dev/null - -echo " - POST /test/lpop" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:list"}' http://localhost:3000/test/lpop > /dev/null - -echo " - POST /test/rpop" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:list"}' http://localhost:3000/test/rpop > /dev/null - -echo " - GET /test/lrange" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/lrange > /dev/null - -echo " - POST /test/llen" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:list"}' http://localhost:3000/test/llen > /dev/null - -echo " - POST /test/sadd" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:set", "member": "member4"}' http://localhost:3000/test/sadd > /dev/null - -echo " - POST /test/srem" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:set", "member": "member4"}' http://localhost:3000/test/srem > /dev/null - -echo " - GET /test/smembers" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/smembers > /dev/null - -echo " - POST /test/sismember" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:set", "member": "member1"}' http://localhost:3000/test/sismember > /dev/null - -echo " - POST /test/zadd" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:zset", "score": 4, "member": "score4"}' http://localhost:3000/test/zadd > /dev/null - -echo " - GET /test/zrange" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zrange > /dev/null - -echo " - POST /test/zrem" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:zset", "member": "score4"}' http://localhost:3000/test/zrem > /dev/null - -echo " - POST /test/zscore" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key": "test:zset", "member": "score1"}' http://localhost:3000/test/zscore > /dev/null - -echo " - POST /test/keys" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"pattern": "test:*"}' http://localhost:3000/test/keys > /dev/null - -echo " - POST /test/flushdb" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/flushdb > /dev/null - -echo " - GET /test/ping" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/ping > /dev/null - -echo " - GET /test/pipeline" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pipeline > /dev/null - -echo " - GET /test/multi" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/multi > /dev/null - -echo " - GET /test/new-client" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/new-client > /dev/null - -echo " - GET /test/getbuffer" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/getbuffer > /dev/null - -echo " - GET /test/mgetbuffer" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/mgetbuffer > /dev/null - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: IOREDIS (ESM)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" +trap cleanup EXIT -# Step 7.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -# Step 8: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "ioredis E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/src/tdInit.ts b/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/src/tdInit.ts index cf11e0f1..d8e4a51f 100644 --- a/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/src/tdInit.ts +++ b/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/src/tdInit.ts @@ -10,7 +10,6 @@ import { TuskDrift } from '@use-tusk/drift-node-sdk'; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/src/test_requests.mjs b/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/src/test_requests.mjs new file mode 100644 index 00000000..5a141eb2 --- /dev/null +++ b/src/instrumentation/libraries/ioredis/e2e-tests/esm-ioredis/src/test_requests.mjs @@ -0,0 +1,41 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('GET', '/test/get'); +await makeRequest('POST', '/test/set', { body: { key: 'test:newkey', value: 'newvalue' } }); +await makeRequest('POST', '/test/del', { body: { key: 'test:newkey' } }); +await makeRequest('POST', '/test/exists', { body: { key: 'test:key1' } }); +await makeRequest('POST', '/test/expire', { body: { key: 'test:key1', seconds: 100 } }); +await makeRequest('POST', '/test/ttl', { body: { key: 'test:key1' } }); +await makeRequest('GET', '/test/incr'); +await makeRequest('GET', '/test/decr'); +await makeRequest('GET', '/test/mget'); +await makeRequest('POST', '/test/mset'); +await makeRequest('GET', '/test/hget'); +await makeRequest('POST', '/test/hset', { body: { key: 'test:user:2', field: 'name', value: 'Jane Doe' } }); +await makeRequest('GET', '/test/hgetall'); +await makeRequest('POST', '/test/hdel', { body: { key: 'test:user:1', field: 'age' } }); +await makeRequest('POST', '/test/lpush', { body: { key: 'test:list', value: 'item0' } }); +await makeRequest('POST', '/test/rpush', { body: { key: 'test:list', value: 'item4' } }); +await makeRequest('POST', '/test/lpop', { body: { key: 'test:list' } }); +await makeRequest('POST', '/test/rpop', { body: { key: 'test:list' } }); +await makeRequest('GET', '/test/lrange'); +await makeRequest('POST', '/test/llen', { body: { key: 'test:list' } }); +await makeRequest('POST', '/test/sadd', { body: { key: 'test:set', member: 'member4' } }); +await makeRequest('POST', '/test/srem', { body: { key: 'test:set', member: 'member4' } }); +await makeRequest('GET', '/test/smembers'); +await makeRequest('POST', '/test/sismember', { body: { key: 'test:set', member: 'member1' } }); +await makeRequest('POST', '/test/zadd', { body: { key: 'test:zset', score: 4, member: 'score4' } }); +await makeRequest('GET', '/test/zrange'); +await makeRequest('POST', '/test/zrem', { body: { key: 'test:zset', member: 'score4' } }); +await makeRequest('POST', '/test/zscore', { body: { key: 'test:zset', member: 'score1' } }); +await makeRequest('POST', '/test/keys', { body: { pattern: 'test:*' } }); +await makeRequest('POST', '/test/flushdb'); +await makeRequest('GET', '/test/ping'); +await makeRequest('GET', '/test/pipeline'); +await makeRequest('GET', '/test/multi'); +await makeRequest('GET', '/test/new-client'); +await makeRequest('GET', '/test/getbuffer'); +await makeRequest('GET', '/test/mgetbuffer'); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/Dockerfile b/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/Dockerfile index ab767339..3c32e223 100644 --- a/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/Dockerfile +++ b/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/Dockerfile @@ -20,5 +20,10 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ # Expose the server port EXPOSE 3000 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +COPY src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/entrypoint.sh ./ +RUN chmod +x entrypoint.sh +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/docker-compose.yml b/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/docker-compose.yml index 648f3c49..6db8d33d 100644 --- a/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/docker-compose.yml +++ b/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: mysql: image: mysql:8.0 @@ -22,8 +20,6 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" environment: - PORT=3000 - MYSQL_HOST=mysql @@ -32,17 +28,17 @@ services: - MYSQL_USER=testuser - MYSQL_PASSWORD=testpass - TUSK_ANALYTICS_DISABLED=1 + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-60} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} volumes: # Mount SDK source for hot reload (this is what package.json expects) - ../../../../../..:/sdk:ro # Mount .tusk folder to persist traces - - ./.tusk:/app/.tusk + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro # Mount app source for development - ./src:/app/src working_dir: /app depends_on: mysql: condition: service_healthy - # Keep container running without starting the app - # The CLI will control starting/stopping the app - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/entrypoint.sh b/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/entrypoint.sh new file mode 100755 index 00000000..188f3789 --- /dev/null +++ b/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +SERVER_WAIT_TIME=10 +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/run.sh b/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/run.sh index 00087e53..f9b02f38 100755 --- a/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/run.sh +++ b/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/run.sh @@ -1,192 +1,51 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -# Generate unique docker compose project name PROJECT_NAME="mysql-cjs-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting MySQL E2E test run on port ${APP_PORT}..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker containers (mysql + app) -echo "Step 1: Starting docker containers..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for containers to be ready -echo "Waiting for containers to be ready..." -sleep 5 - -# Wait for MySQL to be healthy -echo "Waiting for MySQL to be healthy..." -until docker compose -p $PROJECT_NAME exec -T mysql mysqladmin ping -h localhost -u testuser -ptestpass > /dev/null 2>&1; do - echo " MySQL is not ready yet..." - sleep 2 -done -echo "MySQL is ready!" - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "npm run build && npm run dev" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 10 - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all MySQL endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null - -echo " - GET /connection/query-callback" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/connection/query-callback > /dev/null - -echo " - GET /connection/query-params" -docker compose -p $PROJECT_NAME exec -T app curl -s "http://localhost:3000/connection/query-params?key=test_key_1" > /dev/null - -echo " - GET /connection/query-options" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/connection/query-options > /dev/null - -echo " - GET /connection/query-stream" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/connection/query-stream > /dev/null - -echo " - GET /connection/multi-statement" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/connection/multi-statement > /dev/null - -echo " - GET /pool/query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/pool/query > /dev/null - -echo " - GET /pool/get-connection" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/pool/get-connection > /dev/null - -echo " - POST /transaction/commit" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/transaction/commit > /dev/null - -echo " - POST /transaction/rollback" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/transaction/rollback > /dev/null - -echo " - POST /test/transaction-with-options" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/transaction-with-options > /dev/null - -echo " - POST /crud/insert" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key":"crud_test_insert","value":"test_value"}' http://localhost:3000/crud/insert > /dev/null - -echo " - PUT /crud/update" -docker compose -p $PROJECT_NAME exec -T app curl -s -X PUT -H "Content-Type: application/json" -d '{"key":"test_key_1","value":"updated_value"}' http://localhost:3000/crud/update > /dev/null - -echo " - DELETE /crud/delete" -docker compose -p $PROJECT_NAME exec -T app curl -s -X DELETE -H "Content-Type: application/json" -d '{"key":"crud_test_insert"}' http://localhost:3000/crud/delete > /dev/null - -echo " - GET /advanced/join" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/advanced/join > /dev/null +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' -echo " - GET /advanced/aggregate" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/advanced/aggregate > /dev/null - -echo " - GET /advanced/subquery" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/advanced/subquery > /dev/null - -echo " - GET /advanced/prepared" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/advanced/prepared > /dev/null - -echo " - GET /lifecycle/ping" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/lifecycle/ping > /dev/null - -echo " - GET /lifecycle/end-and-reconnect" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/lifecycle/end-and-reconnect > /dev/null - -echo " - POST /lifecycle/change-user" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/lifecycle/change-user > /dev/null - -echo " - GET /lifecycle/pause-resume" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/lifecycle/pause-resume > /dev/null - -echo " - GET /pool/end-and-recreate" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/pool/end-and-recreate > /dev/null - -echo " - GET /test/pool-events" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-events > /dev/null - -echo " - GET /test/pool-namespace-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-namespace-query > /dev/null - -echo " - GET /events/connect" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/events/connect > /dev/null - -echo " - GET /stream/query-stream-method" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/stream/query-stream-method > /dev/null - -echo " - GET /test/connection-destroy" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/connection-destroy > /dev/null - -echo " - GET /test/query-object-reuse" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/query-object-reuse > /dev/null - -echo " - GET /test/pool-namespace-query-stream" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-namespace-query-stream > /dev/null - -echo " - POST /test/pool-connection-transaction-options" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/pool-connection-transaction-options > /dev/null - -echo " - GET /test/pool-getconnection-query-with-internal-callback" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-getconnection-query-with-internal-callback > /dev/null - -echo " - GET /test/pool-namespace-query-with-internal-callback" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-namespace-query-with-internal-callback > /dev/null - -echo " - GET /knex/basic-select" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/knex/basic-select > /dev/null - -echo " - GET /knex/raw-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/knex/raw-query > /dev/null - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: MYSQL (CJS)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" +trap cleanup EXIT -# Step 7.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -# Step 8: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -# cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "MySQL E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/src/index.ts b/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/src/index.ts index dcb2bf18..a7e627bd 100644 --- a/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/src/index.ts +++ b/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/src/index.ts @@ -71,7 +71,6 @@ app.get("/health", (req: Request, res: Response) => { // Test basic query with callback app.get("/connection/query-callback", async (req: Request, res: Response) => { try { - console.log("Testing connection.query with callback..."); const connection = getConnection(); connection.query("SELECT * FROM cache LIMIT 3", (error, results, fields) => { @@ -80,7 +79,6 @@ app.get("/connection/query-callback", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("Query results:", results); res.json({ message: "Query executed with callback", count: results.length, @@ -96,7 +94,6 @@ app.get("/connection/query-callback", async (req: Request, res: Response) => { // Test query with parameters and callback app.get("/connection/query-params", async (req: Request, res: Response) => { try { - console.log("Testing connection.query with parameters..."); const connection = getConnection(); const key = (req.query.key as string) || "test_key_1"; @@ -106,7 +103,6 @@ app.get("/connection/query-params", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("Query results:", results); res.json({ message: "Query executed with parameters", data: results, @@ -121,7 +117,6 @@ app.get("/connection/query-params", async (req: Request, res: Response) => { // Test query with options object app.get("/connection/query-options", async (req: Request, res: Response) => { try { - console.log("Testing connection.query with options object..."); const connection = getConnection(); const options = { @@ -135,7 +130,6 @@ app.get("/connection/query-options", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("Query results:", results); res.json({ message: "Query executed with options object", data: results, @@ -150,7 +144,6 @@ app.get("/connection/query-options", async (req: Request, res: Response) => { // Test query using event emitter mode app.get("/connection/query-stream", async (req: Request, res: Response) => { try { - console.log("Testing connection.query with event emitter..."); const connection = getConnection(); const results: any[] = []; @@ -161,18 +154,10 @@ app.get("/connection/query-stream", async (req: Request, res: Response) => { console.error("Query error:", err); res.status(500).json({ error: err.message }); }) - .on("fields", (fields) => { - console.log( - "Received fields:", - fields.map((f: any) => f.name), - ); - }) .on("result", (row) => { - console.log("Received row:", row); results.push(row); }) .on("end", () => { - console.log("Query completed"); res.json({ message: "Query executed with event emitter", count: results.length, @@ -188,7 +173,6 @@ app.get("/connection/query-stream", async (req: Request, res: Response) => { // Test multi-statement queries app.get("/connection/multi-statement", async (req: Request, res: Response) => { try { - console.log("Testing multi-statement query..."); const connection = getConnection(); const multiQuery = ` @@ -203,7 +187,6 @@ app.get("/connection/multi-statement", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("Multi-statement results:", results); res.json({ message: "Multi-statement query executed", results: results, @@ -220,7 +203,6 @@ app.get("/connection/multi-statement", async (req: Request, res: Response) => { // Test pool query app.get("/pool/query", async (req: Request, res: Response) => { try { - console.log("Testing pool.query..."); const pool = getPool(); pool.query("SELECT * FROM cache ORDER BY created_at DESC LIMIT 2", (error, results, fields) => { @@ -229,7 +211,6 @@ app.get("/pool/query", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("Pool query results:", results); res.json({ message: "Pool query executed", count: results.length, @@ -245,7 +226,6 @@ app.get("/pool/query", async (req: Request, res: Response) => { // Test pool getConnection app.get("/pool/get-connection", async (req: Request, res: Response) => { try { - console.log("Testing pool.getConnection..."); const pool = getPool(); pool.getConnection((error, connection) => { @@ -264,7 +244,6 @@ app.get("/pool/get-connection", async (req: Request, res: Response) => { return res.status(500).json({ error: queryError.message }); } - console.log("Pooled connection query results:", results); res.json({ message: "Query executed on pooled connection", data: results, @@ -279,8 +258,6 @@ app.get("/pool/get-connection", async (req: Request, res: Response) => { app.get("/test/pool-events", async (req: Request, res: Response) => { try { - console.log("Testing pool events..."); - const events: string[] = []; // Create a new pool to track events @@ -294,22 +271,18 @@ app.get("/test/pool-events", async (req: Request, res: Response) => { }); eventPool.on("connection", (connection: any) => { - console.log("Pool event: connection"); events.push("connection"); }); eventPool.on("acquire", (connection: any) => { - console.log("Pool event: acquire"); events.push("acquire"); }); eventPool.on("release", (connection: any) => { - console.log("Pool event: release"); events.push("release"); }); eventPool.on("enqueue", () => { - console.log("Pool event: enqueue"); events.push("enqueue"); }); @@ -343,8 +316,6 @@ app.get("/test/pool-events", async (req: Request, res: Response) => { app.get("/test/pool-namespace-query", async (req: Request, res: Response) => { try { - console.log("Testing PoolNamespace.query()..."); - // Create a pool cluster const poolCluster = mysql.createPoolCluster(); @@ -381,7 +352,6 @@ app.get("/test/pool-namespace-query", async (req: Request, res: Response) => { // Test beginTransaction(callback) signature app.post("/transaction/commit", async (req: Request, res: Response) => { try { - console.log("Testing transaction with commit..."); const connection = getConnection(); connection.beginTransaction((beginError) => { @@ -413,7 +383,6 @@ app.post("/transaction/commit", async (req: Request, res: Response) => { }); } - console.log("Transaction committed successfully"); res.json({ message: "Transaction committed", }); @@ -430,8 +399,6 @@ app.post("/transaction/commit", async (req: Request, res: Response) => { // Test beginTransaction(options, callback) signature app.post("/test/transaction-with-options", async (req: Request, res: Response) => { try { - console.log("Testing transaction with options object..."); - // Create a temporary connection for this test const tempConnection = mysql.createConnection({ host: process.env.MYSQL_HOST || "mysql", @@ -493,7 +460,6 @@ app.post("/test/transaction-with-options", async (req: Request, res: Response) = // Test transaction with rollback app.post("/transaction/rollback", async (req: Request, res: Response) => { try { - console.log("Testing transaction with rollback..."); const connection = getConnection(); connection.beginTransaction((beginError) => { @@ -524,7 +490,6 @@ app.post("/transaction/rollback", async (req: Request, res: Response) => { return res.status(500).json({ error: rollbackError.message }); } - console.log("Transaction rolled back successfully"); res.json({ message: "Transaction rolled back intentionally", }); @@ -543,7 +508,6 @@ app.post("/transaction/rollback", async (req: Request, res: Response) => { // Test INSERT app.post("/crud/insert", async (req: Request, res: Response) => { try { - console.log("Testing INSERT query..."); const connection = getConnection(); const timestamp = Date.now(); @@ -560,7 +524,6 @@ app.post("/crud/insert", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("INSERT results:", results); res.json({ message: "INSERT executed", insertId: results.insertId, @@ -578,7 +541,6 @@ app.post("/crud/insert", async (req: Request, res: Response) => { // Test UPDATE app.put("/crud/update", async (req: Request, res: Response) => { try { - console.log("Testing UPDATE query..."); const connection = getConnection(); const { key, value } = req.body; @@ -594,7 +556,6 @@ app.put("/crud/update", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("UPDATE results:", results); res.json({ message: "UPDATE executed", affectedRows: results.affectedRows, @@ -612,7 +573,6 @@ app.put("/crud/update", async (req: Request, res: Response) => { // Test DELETE app.delete("/crud/delete", async (req: Request, res: Response) => { try { - console.log("Testing DELETE query..."); const connection = getConnection(); const { key } = req.body; @@ -626,7 +586,6 @@ app.delete("/crud/delete", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("DELETE results:", results); res.json({ message: "DELETE executed", affectedRows: results.affectedRows, @@ -644,7 +603,6 @@ app.delete("/crud/delete", async (req: Request, res: Response) => { // Test JOIN query app.get("/advanced/join", async (req: Request, res: Response) => { try { - console.log("Testing JOIN query..."); const connection = getConnection(); const query = ` @@ -661,7 +619,6 @@ app.get("/advanced/join", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("JOIN results:", results); res.json({ message: "JOIN query executed", count: results.length, @@ -677,7 +634,6 @@ app.get("/advanced/join", async (req: Request, res: Response) => { // Test aggregate functions app.get("/advanced/aggregate", async (req: Request, res: Response) => { try { - console.log("Testing aggregate query..."); const connection = getConnection(); const query = ` @@ -695,7 +651,6 @@ app.get("/advanced/aggregate", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("Aggregate results:", results); res.json({ message: "Aggregate query executed", data: results[0], @@ -710,7 +665,6 @@ app.get("/advanced/aggregate", async (req: Request, res: Response) => { // Test subquery app.get("/advanced/subquery", async (req: Request, res: Response) => { try { - console.log("Testing subquery..."); const connection = getConnection(); const query = ` @@ -730,7 +684,6 @@ app.get("/advanced/subquery", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("Subquery results:", results); res.json({ message: "Subquery executed", count: results.length, @@ -746,7 +699,6 @@ app.get("/advanced/subquery", async (req: Request, res: Response) => { // Test prepared statement-like behavior app.get("/advanced/prepared", async (req: Request, res: Response) => { try { - console.log("Testing prepared statement-like query..."); const connection = getConnection(); const params = ["test_key_%", "alice@example.com"]; @@ -764,7 +716,6 @@ app.get("/advanced/prepared", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("Prepared query results:", results); res.json({ message: "Prepared-like query executed", count: results.length, @@ -782,7 +733,6 @@ app.get("/advanced/prepared", async (req: Request, res: Response) => { // Test connection.ping() app.get("/lifecycle/ping", async (req: Request, res: Response) => { try { - console.log("Testing connection.ping()..."); const connection = getConnection(); connection.ping((error) => { @@ -791,7 +741,6 @@ app.get("/lifecycle/ping", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("Ping successful"); res.json({ message: "Ping successful", status: "ok", @@ -806,8 +755,6 @@ app.get("/lifecycle/ping", async (req: Request, res: Response) => { // Test connection.end() and reconnect app.get("/lifecycle/end-and-reconnect", async (req: Request, res: Response) => { try { - console.log("Testing connection.end()..."); - // Create a temporary connection for this test const tempConnection = mysql.createConnection({ host: process.env.MYSQL_HOST || "mysql", @@ -821,7 +768,6 @@ app.get("/lifecycle/end-and-reconnect", async (req: Request, res: Response) => { // Listen for end event tempConnection.on("end", () => { - console.log("'end' event received"); endEventReceived = true; }); @@ -839,8 +785,6 @@ app.get("/lifecycle/end-and-reconnect", async (req: Request, res: Response) => { return res.status(500).json({ error: endError.message }); } - console.log("Connection ended successfully"); - // Give time for end event to fire setTimeout(() => { res.json({ @@ -860,8 +804,6 @@ app.get("/lifecycle/end-and-reconnect", async (req: Request, res: Response) => { // Test connection.changeUser() app.post("/lifecycle/change-user", async (req: Request, res: Response) => { try { - console.log("Testing connection.changeUser()..."); - // Create a temporary connection for this test const tempConnection = mysql.createConnection({ host: process.env.MYSQL_HOST || "mysql", @@ -892,7 +834,6 @@ app.post("/lifecycle/change-user", async (req: Request, res: Response) => { return res.status(500).json({ error: changeError.message }); } - console.log("User changed successfully"); res.json({ message: "User changed successfully", status: "ok", @@ -909,7 +850,6 @@ app.post("/lifecycle/change-user", async (req: Request, res: Response) => { // Test connection.pause() and resume() app.get("/lifecycle/pause-resume", async (req: Request, res: Response) => { try { - console.log("Testing connection.pause() and resume()..."); const connection = getConnection(); const results: any[] = []; @@ -925,14 +865,12 @@ app.get("/lifecycle/pause-resume", async (req: Request, res: Response) => { // Pause after first result connection.pause(); isPaused = true; - console.log("Connection paused"); // Resume after a short delay resumePromise = new Promise((resolve) => { setTimeout(() => { connection.resume(); isResumed = true; - console.log("Connection resumed"); resolve(); }, 50); }); @@ -949,7 +887,6 @@ app.get("/lifecycle/pause-resume", async (req: Request, res: Response) => { await resumePromise; } - console.log("Pause/Resume query completed"); res.json({ message: "Pause and resume executed", isPaused, @@ -969,8 +906,6 @@ app.get("/lifecycle/pause-resume", async (req: Request, res: Response) => { // Test pool.end() and recreate app.get("/pool/end-and-recreate", async (req: Request, res: Response) => { try { - console.log("Testing pool.end()..."); - // Create a temporary pool for this test const tempPool = mysql.createPool({ host: process.env.MYSQL_HOST || "mysql", @@ -988,8 +923,6 @@ app.get("/pool/end-and-recreate", async (req: Request, res: Response) => { return res.status(500).json({ error: queryError.message }); } - console.log("Test query executed on temp pool"); - // Now end the pool tempPool.end((endError) => { if (endError) { @@ -997,7 +930,6 @@ app.get("/pool/end-and-recreate", async (req: Request, res: Response) => { return res.status(500).json({ error: endError.message }); } - console.log("Pool ended successfully"); res.json({ message: "Pool ended successfully", status: "ok", @@ -1015,8 +947,6 @@ app.get("/pool/end-and-recreate", async (req: Request, res: Response) => { // Test 'connect' event emission app.get("/events/connect", async (req: Request, res: Response) => { try { - console.log("Testing 'connect' event..."); - let connectEventReceived = false; // Create a new connection to test connect event @@ -1030,7 +960,6 @@ app.get("/events/connect", async (req: Request, res: Response) => { // Listen for connect event testConnection.on("connect", () => { - console.log("'connect' event received"); connectEventReceived = true; }); @@ -1043,7 +972,6 @@ app.get("/events/connect", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("Connect event test completed"); res.json({ message: "Connect event tested", connectEventReceived, @@ -1060,8 +988,6 @@ app.get("/events/connect", async (req: Request, res: Response) => { // Test: Connection.destroy() - not patched app.get("/test/connection-destroy", async (req: Request, res: Response) => { try { - console.log("Testing connection.destroy()..."); - // Create a temporary connection for this test const tempConnection = mysql.createConnection({ host: process.env.MYSQL_HOST || "mysql", @@ -1103,7 +1029,6 @@ app.get("/test/connection-destroy", async (req: Request, res: Response) => { // Test Query.prototype.stream() method app.get("/stream/query-stream-method", async (req: Request, res: Response) => { try { - console.log("Testing query.stream() method..."); const connection = getConnection(); const results: any[] = []; @@ -1118,11 +1043,9 @@ app.get("/stream/query-stream-method", async (req: Request, res: Response) => { res.status(500).json({ error: err.message }); }) .on("data", (row) => { - console.log("Received data from stream:", row); results.push(row); }) .on("end", () => { - console.log("Stream ended"); res.json({ message: "Stream query executed", count: results.length, @@ -1137,7 +1060,6 @@ app.get("/stream/query-stream-method", async (req: Request, res: Response) => { app.get("/test/query-object-reuse", async (req: Request, res: Response) => { try { - console.log("Testing query object passed directly..."); const connection = getConnection(); // Create a query object manually using Connection.createQuery @@ -1167,8 +1089,6 @@ app.get("/test/query-object-reuse", async (req: Request, res: Response) => { // Test PoolNamespace.query().stream() app.get("/test/pool-namespace-query-stream", async (req: Request, res: Response) => { try { - console.log("Testing PoolNamespace.query().stream()..."); - // Create a pool cluster const poolCluster = mysql.createPoolCluster(); @@ -1196,11 +1116,9 @@ app.get("/test/pool-namespace-query-stream", async (req: Request, res: Response) res.status(500).json({ error: err.message }); }) .on("data", (row) => { - console.log("Received data from stream:", row); results.push(row); }) .on("end", () => { - console.log("Stream ended"); poolCluster.end(() => {}); res.json({ message: "PoolNamespace.query().stream() test completed", @@ -1217,7 +1135,6 @@ app.get("/test/pool-namespace-query-stream", async (req: Request, res: Response) // Pool connection with beginTransaction(options, callback) signature app.post("/test/pool-connection-transaction-options", async (req: Request, res: Response) => { try { - console.log("Testing pool connection with beginTransaction(options, callback)..."); const pool = getPool(); pool.getConnection((err, connection) => { @@ -1277,7 +1194,6 @@ app.get( "/test/pool-getconnection-query-with-internal-callback", async (req: Request, res: Response) => { try { - console.log("Testing pool.getConnection() then query with pre-created Query object..."); const pool = getPool(); pool.getConnection((err, connection) => { @@ -1297,7 +1213,6 @@ app.get( return res.status(500).json({ error: queryErr.message }); } - console.log("Query callback results:", results); res.json({ message: "pool.getConnection().query with internal callback completed", count: results.length, @@ -1321,10 +1236,6 @@ app.get( "/test/pool-namespace-query-with-internal-callback", async (req: Request, res: Response) => { try { - console.log( - "Testing PoolNamespace.query() with pre-created Query object having internal _callback...", - ); - // Create a pool cluster const poolCluster = mysql.createPoolCluster(); @@ -1348,7 +1259,6 @@ app.get( return res.status(500).json({ error: err.message }); } - console.log("Query callback results:", results); res.json({ message: "PoolNamespace.query with internal callback completed", count: results.length, @@ -1384,9 +1294,7 @@ const knex = Knex({ app.get("/knex/basic-select", async (req: Request, res: Response) => { try { - console.log("Testing knex basic select..."); const results = await knex("cache").select("*").limit(3); - console.log("Knex basic select results:", results); res.json({ message: "Knex basic select completed", count: results.length, @@ -1400,9 +1308,7 @@ app.get("/knex/basic-select", async (req: Request, res: Response) => { app.get("/knex/raw-query", async (req: Request, res: Response) => { try { - console.log("Testing knex raw query..."); const results = await knex.raw("SELECT COUNT(*) as count FROM cache"); - console.log("Knex raw query results:", results); res.json({ message: "Knex raw query completed", data: results[0], diff --git a/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/src/tdInit.ts b/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/src/tdInit.ts index 09b04a42..9b1acad7 100644 --- a/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/src/tdInit.ts +++ b/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/src/tdInit.ts @@ -3,7 +3,6 @@ import { TuskDrift } from '@use-tusk/drift-node-sdk'; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/src/test_requests.mjs b/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/src/test_requests.mjs new file mode 100644 index 00000000..220ec4a4 --- /dev/null +++ b/src/instrumentation/libraries/mysql/e2e-tests/cjs-mysql/src/test_requests.mjs @@ -0,0 +1,39 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('GET', '/connection/query-callback'); +await makeRequest('GET', '/connection/query-params?key=test_key_1'); +await makeRequest('GET', '/connection/query-options'); +await makeRequest('GET', '/connection/query-stream'); +await makeRequest('GET', '/connection/multi-statement'); +await makeRequest('GET', '/pool/query'); +await makeRequest('GET', '/pool/get-connection'); +await makeRequest('POST', '/transaction/commit'); +await makeRequest('POST', '/transaction/rollback'); +await makeRequest('POST', '/test/transaction-with-options'); +await makeRequest('POST', '/crud/insert', { body: { key: 'crud_test_insert', value: 'test_value' } }); +await makeRequest('PUT', '/crud/update', { body: { key: 'test_key_1', value: 'updated_value' } }); +await makeRequest('DELETE', '/crud/delete', { body: { key: 'crud_test_insert' } }); +await makeRequest('GET', '/advanced/join'); +await makeRequest('GET', '/advanced/aggregate'); +await makeRequest('GET', '/advanced/subquery'); +await makeRequest('GET', '/advanced/prepared'); +await makeRequest('GET', '/lifecycle/ping'); +await makeRequest('GET', '/lifecycle/end-and-reconnect'); +await makeRequest('POST', '/lifecycle/change-user'); +await makeRequest('GET', '/lifecycle/pause-resume'); +await makeRequest('GET', '/pool/end-and-recreate'); +await makeRequest('GET', '/test/pool-events'); +await makeRequest('GET', '/test/pool-namespace-query'); +await makeRequest('GET', '/events/connect'); +await makeRequest('GET', '/stream/query-stream-method'); +await makeRequest('GET', '/test/connection-destroy'); +await makeRequest('GET', '/test/query-object-reuse'); +await makeRequest('GET', '/test/pool-namespace-query-stream'); +await makeRequest('POST', '/test/pool-connection-transaction-options'); +await makeRequest('GET', '/test/pool-getconnection-query-with-internal-callback'); +await makeRequest('GET', '/test/pool-namespace-query-with-internal-callback'); +await makeRequest('GET', '/knex/basic-select'); +await makeRequest('GET', '/knex/raw-query'); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/Dockerfile b/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/Dockerfile index 8df6b02a..8eb61a01 100644 --- a/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/Dockerfile +++ b/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/Dockerfile @@ -20,5 +20,10 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ # Expose the server port EXPOSE 3000 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +COPY src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/entrypoint.sh ./ +RUN chmod +x entrypoint.sh +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/docker-compose.yml b/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/docker-compose.yml index d0d1572a..70fc0bb0 100644 --- a/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/docker-compose.yml +++ b/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: mysql: image: mysql:8.0 @@ -22,8 +20,6 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" environment: - PORT=3000 - MYSQL_HOST=mysql @@ -32,17 +28,17 @@ services: - MYSQL_USER=testuser - MYSQL_PASSWORD=testpass - TUSK_ANALYTICS_DISABLED=1 + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-60} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} volumes: # Mount SDK source for hot reload (this is what package.json expects) - ../../../../../..:/sdk:ro # Mount .tusk folder to persist traces - - ./.tusk:/app/.tusk + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro # Mount app source for development - ./src:/app/src working_dir: /app depends_on: mysql: condition: service_healthy - # Keep container running without starting the app - # The CLI will control starting/stopping the app - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/entrypoint.sh b/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/entrypoint.sh new file mode 100755 index 00000000..188f3789 --- /dev/null +++ b/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +SERVER_WAIT_TIME=10 +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/run.sh b/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/run.sh index ac6df36b..09249948 100755 --- a/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/run.sh +++ b/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/run.sh @@ -1,192 +1,51 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -# Generate unique docker compose project name PROJECT_NAME="mysql-esm-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting MySQL ESM E2E test run on port ${APP_PORT}..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker containers (mysql + app) -echo "Step 1: Starting docker containers..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for containers to be ready -echo "Waiting for containers to be ready..." -sleep 5 - -# Wait for MySQL to be healthy -echo "Waiting for MySQL to be healthy..." -until docker compose -p $PROJECT_NAME exec -T mysql mysqladmin ping -h localhost -u testuser -ptestpass > /dev/null 2>&1; do - echo " MySQL is not ready yet..." - sleep 2 -done -echo "MySQL is ready!" - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "npm run build && npm run dev" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 10 - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all MySQL endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null - -echo " - GET /connection/query-callback" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/connection/query-callback > /dev/null - -echo " - GET /connection/query-params" -docker compose -p $PROJECT_NAME exec -T app curl -s "http://localhost:3000/connection/query-params?key=test_key_1" > /dev/null - -echo " - GET /connection/query-options" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/connection/query-options > /dev/null - -echo " - GET /connection/query-stream" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/connection/query-stream > /dev/null - -echo " - GET /connection/multi-statement" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/connection/multi-statement > /dev/null - -echo " - GET /pool/query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/pool/query > /dev/null - -echo " - GET /pool/get-connection" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/pool/get-connection > /dev/null - -echo " - GET /test/pool-namespace-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-namespace-query > /dev/null - -echo " - POST /transaction/commit" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/transaction/commit > /dev/null - -echo " - POST /test/transaction-with-options" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/transaction-with-options > /dev/null - -echo " - POST /transaction/rollback" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/transaction/rollback > /dev/null - -echo " - POST /crud/insert" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key":"crud_test_insert","value":"test_value"}' http://localhost:3000/crud/insert > /dev/null - -echo " - PUT /crud/update" -docker compose -p $PROJECT_NAME exec -T app curl -s -X PUT -H "Content-Type: application/json" -d '{"key":"test_key_1","value":"updated_value"}' http://localhost:3000/crud/update > /dev/null - -echo " - DELETE /crud/delete" -docker compose -p $PROJECT_NAME exec -T app curl -s -X DELETE -H "Content-Type: application/json" -d '{"key":"crud_test_insert"}' http://localhost:3000/crud/delete > /dev/null +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' -echo " - GET /advanced/join" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/advanced/join > /dev/null - -echo " - GET /advanced/aggregate" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/advanced/aggregate > /dev/null - -echo " - GET /advanced/subquery" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/advanced/subquery > /dev/null - -echo " - GET /advanced/prepared" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/advanced/prepared > /dev/null - -echo " - GET /lifecycle/ping" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/lifecycle/ping > /dev/null - -echo " - GET /lifecycle/end-and-reconnect" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/lifecycle/end-and-reconnect > /dev/null - -echo " - POST /lifecycle/change-user" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/lifecycle/change-user > /dev/null - -echo " - GET /lifecycle/pause-resume" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/lifecycle/pause-resume > /dev/null - -echo " - GET /pool/end-and-recreate" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/pool/end-and-recreate > /dev/null - -echo " - GET /test/pool-events" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-events > /dev/null - -echo " - GET /events/connect" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/events/connect > /dev/null - -echo " - GET /stream/query-stream-method" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/stream/query-stream-method > /dev/null - -echo " - GET /test/connection-destroy" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/connection-destroy > /dev/null - -echo " - GET /test/query-object-reuse" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/query-object-reuse > /dev/null - -echo " - GET /test/pool-namespace-query-stream" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-namespace-query-stream > /dev/null - -echo " - POST /test/pool-connection-transaction-options" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/pool-connection-transaction-options > /dev/null - -echo " - GET /test/pool-getconnection-query-with-internal-callback" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-getconnection-query-with-internal-callback > /dev/null - -echo " - GET /test/pool-namespace-query-with-internal-callback" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-namespace-query-with-internal-callback > /dev/null - -echo " - GET /knex/basic-select" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/knex/basic-select > /dev/null - -echo " - GET /knex/raw-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/knex/raw-query > /dev/null - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: MYSQL (ESM)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" +trap cleanup EXIT -# Step 7.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -# Step 8: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "MySQL ESM E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/src/index.ts b/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/src/index.ts index 873386e4..25fb11c2 100644 --- a/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/src/index.ts +++ b/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/src/index.ts @@ -13,8 +13,6 @@ app.use(express.json()); async function initializeDatabase() { const connection = getConnection(); - console.log("Initializing database tables..."); - return new Promise((resolve, reject) => { // Create tables and insert seed data const initScript = ` @@ -54,7 +52,6 @@ async function initializeDatabase() { console.error("Error initializing database:", error); reject(error); } else { - console.log("Database initialization complete"); resolve(); } }); @@ -71,7 +68,6 @@ app.get("/health", (req: Request, res: Response) => { // Test basic query with callback app.get("/connection/query-callback", async (req: Request, res: Response) => { try { - console.log("Testing connection.query with callback..."); const connection = getConnection(); connection.query("SELECT * FROM cache LIMIT 3", (error, results, fields) => { @@ -80,7 +76,6 @@ app.get("/connection/query-callback", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("Query results:", results); res.json({ message: "Query executed with callback", count: results.length, @@ -96,7 +91,6 @@ app.get("/connection/query-callback", async (req: Request, res: Response) => { // Test query with parameters and callback app.get("/connection/query-params", async (req: Request, res: Response) => { try { - console.log("Testing connection.query with parameters..."); const connection = getConnection(); const key = (req.query.key as string) || "test_key_1"; @@ -106,7 +100,6 @@ app.get("/connection/query-params", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("Query results:", results); res.json({ message: "Query executed with parameters", data: results, @@ -121,7 +114,6 @@ app.get("/connection/query-params", async (req: Request, res: Response) => { // Test query with options object app.get("/connection/query-options", async (req: Request, res: Response) => { try { - console.log("Testing connection.query with options object..."); const connection = getConnection(); const options = { @@ -135,7 +127,6 @@ app.get("/connection/query-options", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("Query results:", results); res.json({ message: "Query executed with options object", data: results, @@ -150,7 +141,6 @@ app.get("/connection/query-options", async (req: Request, res: Response) => { // Test query using event emitter mode app.get("/connection/query-stream", async (req: Request, res: Response) => { try { - console.log("Testing connection.query with event emitter..."); const connection = getConnection(); const results: any[] = []; @@ -161,18 +151,10 @@ app.get("/connection/query-stream", async (req: Request, res: Response) => { console.error("Query error:", err); res.status(500).json({ error: err.message }); }) - .on("fields", (fields) => { - console.log( - "Received fields:", - fields.map((f: any) => f.name), - ); - }) .on("result", (row) => { - console.log("Received row:", row); results.push(row); }) .on("end", () => { - console.log("Query completed"); res.json({ message: "Query executed with event emitter", count: results.length, @@ -188,7 +170,6 @@ app.get("/connection/query-stream", async (req: Request, res: Response) => { // Test multi-statement queries app.get("/connection/multi-statement", async (req: Request, res: Response) => { try { - console.log("Testing multi-statement query..."); const connection = getConnection(); const multiQuery = ` @@ -203,7 +184,6 @@ app.get("/connection/multi-statement", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("Multi-statement results:", results); res.json({ message: "Multi-statement query executed", results: results, @@ -220,7 +200,6 @@ app.get("/connection/multi-statement", async (req: Request, res: Response) => { // Test pool query app.get("/pool/query", async (req: Request, res: Response) => { try { - console.log("Testing pool.query..."); const pool = getPool(); pool.query("SELECT * FROM cache ORDER BY created_at DESC LIMIT 2", (error, results, fields) => { @@ -229,7 +208,6 @@ app.get("/pool/query", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("Pool query results:", results); res.json({ message: "Pool query executed", count: results.length, @@ -245,7 +223,6 @@ app.get("/pool/query", async (req: Request, res: Response) => { // Test pool getConnection app.get("/pool/get-connection", async (req: Request, res: Response) => { try { - console.log("Testing pool.getConnection..."); const pool = getPool(); pool.getConnection((error, connection) => { @@ -264,7 +241,6 @@ app.get("/pool/get-connection", async (req: Request, res: Response) => { return res.status(500).json({ error: queryError.message }); } - console.log("Pooled connection query results:", results); res.json({ message: "Query executed on pooled connection", data: results, @@ -279,8 +255,6 @@ app.get("/pool/get-connection", async (req: Request, res: Response) => { app.get("/test/pool-events", async (req: Request, res: Response) => { try { - console.log("Testing pool events..."); - const events: string[] = []; // Create a new pool to track events @@ -294,22 +268,18 @@ app.get("/test/pool-events", async (req: Request, res: Response) => { }); eventPool.on("connection", (connection: any) => { - console.log("Pool event: connection"); events.push("connection"); }); eventPool.on("acquire", (connection: any) => { - console.log("Pool event: acquire"); events.push("acquire"); }); eventPool.on("release", (connection: any) => { - console.log("Pool event: release"); events.push("release"); }); eventPool.on("enqueue", () => { - console.log("Pool event: enqueue"); events.push("enqueue"); }); @@ -343,8 +313,6 @@ app.get("/test/pool-events", async (req: Request, res: Response) => { app.get("/test/pool-namespace-query", async (req: Request, res: Response) => { try { - console.log("Testing PoolNamespace.query()..."); - // Create a pool cluster const poolCluster = mysql.createPoolCluster(); @@ -381,7 +349,6 @@ app.get("/test/pool-namespace-query", async (req: Request, res: Response) => { // Test transaction with commit app.post("/transaction/commit", async (req: Request, res: Response) => { try { - console.log("Testing transaction with commit..."); const connection = getConnection(); connection.beginTransaction((beginError) => { @@ -413,7 +380,6 @@ app.post("/transaction/commit", async (req: Request, res: Response) => { }); } - console.log("Transaction committed successfully"); res.json({ message: "Transaction committed", }); @@ -430,8 +396,6 @@ app.post("/transaction/commit", async (req: Request, res: Response) => { // Test beginTransaction(options, callback) signature app.post("/test/transaction-with-options", async (req: Request, res: Response) => { try { - console.log("Testing transaction with options object..."); - // Create a temporary connection for this test const tempConnection = mysql.createConnection({ host: process.env.MYSQL_HOST || "mysql", @@ -493,7 +457,6 @@ app.post("/test/transaction-with-options", async (req: Request, res: Response) = // Test transaction with rollback app.post("/transaction/rollback", async (req: Request, res: Response) => { try { - console.log("Testing transaction with rollback..."); const connection = getConnection(); connection.beginTransaction((beginError) => { @@ -524,7 +487,6 @@ app.post("/transaction/rollback", async (req: Request, res: Response) => { return res.status(500).json({ error: rollbackError.message }); } - console.log("Transaction rolled back successfully"); res.json({ message: "Transaction rolled back intentionally", }); @@ -543,7 +505,6 @@ app.post("/transaction/rollback", async (req: Request, res: Response) => { // Test INSERT app.post("/crud/insert", async (req: Request, res: Response) => { try { - console.log("Testing INSERT query..."); const connection = getConnection(); const timestamp = Date.now(); @@ -560,7 +521,6 @@ app.post("/crud/insert", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("INSERT results:", results); res.json({ message: "INSERT executed", insertId: results.insertId, @@ -578,7 +538,6 @@ app.post("/crud/insert", async (req: Request, res: Response) => { // Test UPDATE app.put("/crud/update", async (req: Request, res: Response) => { try { - console.log("Testing UPDATE query..."); const connection = getConnection(); const { key, value } = req.body; @@ -594,7 +553,6 @@ app.put("/crud/update", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("UPDATE results:", results); res.json({ message: "UPDATE executed", affectedRows: results.affectedRows, @@ -612,7 +570,6 @@ app.put("/crud/update", async (req: Request, res: Response) => { // Test DELETE app.delete("/crud/delete", async (req: Request, res: Response) => { try { - console.log("Testing DELETE query..."); const connection = getConnection(); const { key } = req.body; @@ -626,7 +583,6 @@ app.delete("/crud/delete", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("DELETE results:", results); res.json({ message: "DELETE executed", affectedRows: results.affectedRows, @@ -644,7 +600,6 @@ app.delete("/crud/delete", async (req: Request, res: Response) => { // Test JOIN query app.get("/advanced/join", async (req: Request, res: Response) => { try { - console.log("Testing JOIN query..."); const connection = getConnection(); const query = ` @@ -661,7 +616,6 @@ app.get("/advanced/join", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("JOIN results:", results); res.json({ message: "JOIN query executed", count: results.length, @@ -677,7 +631,6 @@ app.get("/advanced/join", async (req: Request, res: Response) => { // Test aggregate functions app.get("/advanced/aggregate", async (req: Request, res: Response) => { try { - console.log("Testing aggregate query..."); const connection = getConnection(); const query = ` @@ -695,7 +648,6 @@ app.get("/advanced/aggregate", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("Aggregate results:", results); res.json({ message: "Aggregate query executed", data: results[0], @@ -710,7 +662,6 @@ app.get("/advanced/aggregate", async (req: Request, res: Response) => { // Test subquery app.get("/advanced/subquery", async (req: Request, res: Response) => { try { - console.log("Testing subquery..."); const connection = getConnection(); const query = ` @@ -730,7 +681,6 @@ app.get("/advanced/subquery", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("Subquery results:", results); res.json({ message: "Subquery executed", count: results.length, @@ -746,7 +696,6 @@ app.get("/advanced/subquery", async (req: Request, res: Response) => { // Test prepared statement-like behavior app.get("/advanced/prepared", async (req: Request, res: Response) => { try { - console.log("Testing prepared statement-like query..."); const connection = getConnection(); const params = ["test_key_%", "alice@example.com"]; @@ -764,7 +713,6 @@ app.get("/advanced/prepared", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("Prepared query results:", results); res.json({ message: "Prepared-like query executed", count: results.length, @@ -782,7 +730,6 @@ app.get("/advanced/prepared", async (req: Request, res: Response) => { // Test connection.ping() app.get("/lifecycle/ping", async (req: Request, res: Response) => { try { - console.log("Testing connection.ping()..."); const connection = getConnection(); connection.ping((error) => { @@ -791,7 +738,6 @@ app.get("/lifecycle/ping", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("Ping successful"); res.json({ message: "Ping successful", status: "ok", @@ -806,8 +752,6 @@ app.get("/lifecycle/ping", async (req: Request, res: Response) => { // Test connection.end() and reconnect app.get("/lifecycle/end-and-reconnect", async (req: Request, res: Response) => { try { - console.log("Testing connection.end()..."); - // Create a temporary connection for this test const tempConnection = mysql.createConnection({ host: process.env.MYSQL_HOST || "mysql", @@ -821,7 +765,6 @@ app.get("/lifecycle/end-and-reconnect", async (req: Request, res: Response) => { // Listen for end event tempConnection.on("end", () => { - console.log("'end' event received"); endEventReceived = true; }); @@ -839,8 +782,6 @@ app.get("/lifecycle/end-and-reconnect", async (req: Request, res: Response) => { return res.status(500).json({ error: endError.message }); } - console.log("Connection ended successfully"); - // Give time for end event to fire setTimeout(() => { res.json({ @@ -860,8 +801,6 @@ app.get("/lifecycle/end-and-reconnect", async (req: Request, res: Response) => { // Test connection.changeUser() app.post("/lifecycle/change-user", async (req: Request, res: Response) => { try { - console.log("Testing connection.changeUser()..."); - // Create a temporary connection for this test const tempConnection = mysql.createConnection({ host: process.env.MYSQL_HOST || "mysql", @@ -892,7 +831,6 @@ app.post("/lifecycle/change-user", async (req: Request, res: Response) => { return res.status(500).json({ error: changeError.message }); } - console.log("User changed successfully"); res.json({ message: "User changed successfully", status: "ok", @@ -909,7 +847,6 @@ app.post("/lifecycle/change-user", async (req: Request, res: Response) => { // Test connection.pause() and resume() app.get("/lifecycle/pause-resume", async (req: Request, res: Response) => { try { - console.log("Testing connection.pause() and resume()..."); const connection = getConnection(); const results: any[] = []; @@ -925,14 +862,12 @@ app.get("/lifecycle/pause-resume", async (req: Request, res: Response) => { // Pause after first result connection.pause(); isPaused = true; - console.log("Connection paused"); // Resume after a short delay resumePromise = new Promise((resolve) => { setTimeout(() => { connection.resume(); isResumed = true; - console.log("Connection resumed"); resolve(); }, 50); }); @@ -949,7 +884,6 @@ app.get("/lifecycle/pause-resume", async (req: Request, res: Response) => { await resumePromise; } - console.log("Pause/Resume query completed"); res.json({ message: "Pause and resume executed", isPaused, @@ -969,8 +903,6 @@ app.get("/lifecycle/pause-resume", async (req: Request, res: Response) => { // Test pool.end() and recreate app.get("/pool/end-and-recreate", async (req: Request, res: Response) => { try { - console.log("Testing pool.end()..."); - // Create a temporary pool for this test const tempPool = mysql.createPool({ host: process.env.MYSQL_HOST || "mysql", @@ -988,8 +920,6 @@ app.get("/pool/end-and-recreate", async (req: Request, res: Response) => { return res.status(500).json({ error: queryError.message }); } - console.log("Test query executed on temp pool"); - // Now end the pool tempPool.end((endError) => { if (endError) { @@ -997,7 +927,6 @@ app.get("/pool/end-and-recreate", async (req: Request, res: Response) => { return res.status(500).json({ error: endError.message }); } - console.log("Pool ended successfully"); res.json({ message: "Pool ended successfully", status: "ok", @@ -1015,8 +944,6 @@ app.get("/pool/end-and-recreate", async (req: Request, res: Response) => { // Test 'connect' event emission app.get("/events/connect", async (req: Request, res: Response) => { try { - console.log("Testing 'connect' event..."); - let connectEventReceived = false; // Create a new connection to test connect event @@ -1030,7 +957,6 @@ app.get("/events/connect", async (req: Request, res: Response) => { // Listen for connect event testConnection.on("connect", () => { - console.log("'connect' event received"); connectEventReceived = true; }); @@ -1043,7 +969,6 @@ app.get("/events/connect", async (req: Request, res: Response) => { return res.status(500).json({ error: error.message }); } - console.log("Connect event test completed"); res.json({ message: "Connect event tested", connectEventReceived, @@ -1060,8 +985,6 @@ app.get("/events/connect", async (req: Request, res: Response) => { // Test: Connection.destroy() - not patched app.get("/test/connection-destroy", async (req: Request, res: Response) => { try { - console.log("Testing connection.destroy()..."); - // Create a temporary connection for this test const tempConnection = mysql.createConnection({ host: process.env.MYSQL_HOST || "mysql", @@ -1103,7 +1026,6 @@ app.get("/test/connection-destroy", async (req: Request, res: Response) => { // Test Query.prototype.stream() method app.get("/stream/query-stream-method", async (req: Request, res: Response) => { try { - console.log("Testing query.stream() method..."); const connection = getConnection(); const results: any[] = []; @@ -1118,11 +1040,9 @@ app.get("/stream/query-stream-method", async (req: Request, res: Response) => { res.status(500).json({ error: err.message }); }) .on("data", (row) => { - console.log("Received data from stream:", row); results.push(row); }) .on("end", () => { - console.log("Stream ended"); res.json({ message: "Stream query executed", count: results.length, @@ -1137,7 +1057,6 @@ app.get("/stream/query-stream-method", async (req: Request, res: Response) => { app.get("/test/query-object-reuse", async (req: Request, res: Response) => { try { - console.log("Testing query object passed directly..."); const connection = getConnection(); // Create a query object manually using Connection.createQuery @@ -1167,8 +1086,6 @@ app.get("/test/query-object-reuse", async (req: Request, res: Response) => { // Test PoolNamespace.query().stream() app.get("/test/pool-namespace-query-stream", async (req: Request, res: Response) => { try { - console.log("Testing PoolNamespace.query().stream()..."); - // Create a pool cluster const poolCluster = mysql.createPoolCluster(); @@ -1196,11 +1113,9 @@ app.get("/test/pool-namespace-query-stream", async (req: Request, res: Response) res.status(500).json({ error: err.message }); }) .on("data", (row) => { - console.log("Received data from stream:", row); results.push(row); }) .on("end", () => { - console.log("Stream ended"); poolCluster.end(() => {}); res.json({ message: "PoolNamespace.query().stream() test completed", @@ -1217,7 +1132,6 @@ app.get("/test/pool-namespace-query-stream", async (req: Request, res: Response) // Pool connection with beginTransaction(options, callback) signature app.post("/test/pool-connection-transaction-options", async (req: Request, res: Response) => { try { - console.log("Testing pool connection with beginTransaction(options, callback)..."); const pool = getPool(); pool.getConnection((err, connection) => { @@ -1277,7 +1191,6 @@ app.get( "/test/pool-getconnection-query-with-internal-callback", async (req: Request, res: Response) => { try { - console.log("Testing pool.getConnection() then query with pre-created Query object..."); const pool = getPool(); pool.getConnection((err, connection) => { @@ -1297,7 +1210,6 @@ app.get( return res.status(500).json({ error: queryErr.message }); } - console.log("Query callback results:", results); res.json({ message: "pool.getConnection().query with internal callback completed", count: results.length, @@ -1321,10 +1233,6 @@ app.get( "/test/pool-namespace-query-with-internal-callback", async (req: Request, res: Response) => { try { - console.log( - "Testing PoolNamespace.query() with pre-created Query object having internal _callback...", - ); - // Create a pool cluster const poolCluster = mysql.createPoolCluster(); @@ -1348,7 +1256,6 @@ app.get( return res.status(500).json({ error: err.message }); } - console.log("Query callback results:", results); res.json({ message: "PoolNamespace.query with internal callback completed", count: results.length, @@ -1384,9 +1291,7 @@ const knex = Knex({ app.get("/knex/basic-select", async (req: Request, res: Response) => { try { - console.log("Testing knex basic select..."); const results = await knex("cache").select("*").limit(3); - console.log("Knex basic select results:", results); res.json({ message: "Knex basic select completed", count: results.length, @@ -1400,9 +1305,7 @@ app.get("/knex/basic-select", async (req: Request, res: Response) => { app.get("/knex/raw-query", async (req: Request, res: Response) => { try { - console.log("Testing knex raw query..."); const results = await knex.raw("SELECT COUNT(*) as count FROM cache"); - console.log("Knex raw query results:", results); res.json({ message: "Knex raw query completed", data: results[0], @@ -1419,8 +1322,6 @@ const server = app.listen(PORT, async () => { await connectDb(); await initializeDatabase(); TuskDrift.markAppAsReady(); - console.log(`Server running on port ${PORT}`); - console.log(`TUSK_DRIFT_MODE: ${process.env.TUSK_DRIFT_MODE || "DISABLED"}`); } catch (error) { console.error("Failed to start server:", error); process.exit(1); @@ -1429,7 +1330,6 @@ const server = app.listen(PORT, async () => { // Graceful shutdown async function shutdown() { - console.log("Shutting down gracefully..."); server.close(async () => { try { await closeDb(); diff --git a/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/src/tdInit.ts b/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/src/tdInit.ts index cf11e0f1..d8e4a51f 100644 --- a/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/src/tdInit.ts +++ b/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/src/tdInit.ts @@ -10,7 +10,6 @@ import { TuskDrift } from '@use-tusk/drift-node-sdk'; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/src/test_requests.mjs b/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/src/test_requests.mjs new file mode 100644 index 00000000..220ec4a4 --- /dev/null +++ b/src/instrumentation/libraries/mysql/e2e-tests/esm-mysql/src/test_requests.mjs @@ -0,0 +1,39 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('GET', '/connection/query-callback'); +await makeRequest('GET', '/connection/query-params?key=test_key_1'); +await makeRequest('GET', '/connection/query-options'); +await makeRequest('GET', '/connection/query-stream'); +await makeRequest('GET', '/connection/multi-statement'); +await makeRequest('GET', '/pool/query'); +await makeRequest('GET', '/pool/get-connection'); +await makeRequest('POST', '/transaction/commit'); +await makeRequest('POST', '/transaction/rollback'); +await makeRequest('POST', '/test/transaction-with-options'); +await makeRequest('POST', '/crud/insert', { body: { key: 'crud_test_insert', value: 'test_value' } }); +await makeRequest('PUT', '/crud/update', { body: { key: 'test_key_1', value: 'updated_value' } }); +await makeRequest('DELETE', '/crud/delete', { body: { key: 'crud_test_insert' } }); +await makeRequest('GET', '/advanced/join'); +await makeRequest('GET', '/advanced/aggregate'); +await makeRequest('GET', '/advanced/subquery'); +await makeRequest('GET', '/advanced/prepared'); +await makeRequest('GET', '/lifecycle/ping'); +await makeRequest('GET', '/lifecycle/end-and-reconnect'); +await makeRequest('POST', '/lifecycle/change-user'); +await makeRequest('GET', '/lifecycle/pause-resume'); +await makeRequest('GET', '/pool/end-and-recreate'); +await makeRequest('GET', '/test/pool-events'); +await makeRequest('GET', '/test/pool-namespace-query'); +await makeRequest('GET', '/events/connect'); +await makeRequest('GET', '/stream/query-stream-method'); +await makeRequest('GET', '/test/connection-destroy'); +await makeRequest('GET', '/test/query-object-reuse'); +await makeRequest('GET', '/test/pool-namespace-query-stream'); +await makeRequest('POST', '/test/pool-connection-transaction-options'); +await makeRequest('GET', '/test/pool-getconnection-query-with-internal-callback'); +await makeRequest('GET', '/test/pool-namespace-query-with-internal-callback'); +await makeRequest('GET', '/knex/basic-select'); +await makeRequest('GET', '/knex/raw-query'); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/Dockerfile b/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/Dockerfile index cc26bdfa..bf5edd7a 100644 --- a/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/Dockerfile +++ b/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/Dockerfile @@ -20,5 +20,10 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ # Expose the server port EXPOSE 3000 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +COPY src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/entrypoint.sh ./ +RUN chmod +x entrypoint.sh +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/docker-compose.yml b/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/docker-compose.yml index 9e6c34f2..a766015d 100644 --- a/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/docker-compose.yml +++ b/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: mysql: image: mysql:8.0 @@ -21,8 +19,6 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" environment: - PORT=3000 - MYSQL_HOST=mysql @@ -31,17 +27,17 @@ services: - MYSQL_USER=testuser - MYSQL_PASSWORD=testpass - TUSK_ANALYTICS_DISABLED=1 + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-60} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} volumes: # Mount SDK source for hot reload (this is what package.json expects) - ../../../../../..:/sdk:ro # Mount .tusk folder to persist traces - - ./.tusk:/app/.tusk + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro # Mount app source for development - ./src:/app/src working_dir: /app depends_on: mysql: condition: service_healthy - # Keep container running without starting the app - # The CLI will control starting/stopping the app - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/entrypoint.sh b/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/entrypoint.sh new file mode 100755 index 00000000..ed1dce09 --- /dev/null +++ b/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +SERVER_WAIT_TIME=8 +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/run.sh b/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/run.sh index af5d4310..6044dcb9 100755 --- a/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/run.sh +++ b/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/run.sh @@ -1,177 +1,51 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -# Generate unique docker compose project name PROJECT_NAME="mysql2-cjs-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting MySQL2 E2E test run on port ${APP_PORT}..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker containers (mysql + app) -echo "Step 1: Starting docker containers..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for containers to be ready -echo "Waiting for containers to be ready..." -sleep 5 - -# Wait for MySQL to be healthy -echo "Waiting for MySQL to be healthy..." -until docker compose -p $PROJECT_NAME exec -T mysql mysqladmin ping -h localhost -u testuser -ptestpass > /dev/null 2>&1; do - echo " MySQL is not ready yet..." - sleep 2 -done -echo "MySQL is ready!" - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "npm run build && npm run dev" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 8 - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all MySQL2 endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null - -echo " - GET /test/connection-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/connection-query > /dev/null - -echo " - POST /test/connection-parameterized" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"userId": 1}' http://localhost:3000/test/connection-parameterized > /dev/null - -echo " - GET /test/connection-execute" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/connection-execute > /dev/null - -echo " - POST /test/connection-execute-params" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"userId": 2}' http://localhost:3000/test/connection-execute-params > /dev/null - -echo " - GET /test/pool-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-query > /dev/null - -echo " - POST /test/pool-parameterized" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"userId": 1}' http://localhost:3000/test/pool-parameterized > /dev/null - -echo " - GET /test/pool-execute" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-execute > /dev/null - -echo " - POST /test/pool-execute-params" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"userId": 2}' http://localhost:3000/test/pool-execute-params > /dev/null - -echo " - GET /test/pool-getConnection" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-getConnection > /dev/null - -echo " - GET /test/connection-connect" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/connection-connect > /dev/null - -echo " - GET /test/connection-ping" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/connection-ping > /dev/null - -echo " - GET /test/stream-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/stream-query > /dev/null +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' -echo " - GET /test/sequelize-authenticate" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/sequelize-authenticate > /dev/null - -echo " - GET /test/sequelize-findall" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/sequelize-findall > /dev/null - -echo " - POST /test/sequelize-findone" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"userId": 1}' http://localhost:3000/test/sequelize-findone > /dev/null - -echo " - GET /test/sequelize-complex" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/sequelize-complex > /dev/null - -echo " - GET /test/sequelize-raw" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/sequelize-raw > /dev/null - -echo " - POST /test/sequelize-transaction" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/sequelize-transaction > /dev/null - -echo " - GET /test/promise-connection-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/promise-connection-query > /dev/null - -echo " - GET /test/promise-pool-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/promise-pool-query > /dev/null - -echo " - GET /test/promise-pool-getconnection" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/promise-pool-getconnection > /dev/null - -echo " - GET /test/transaction-methods" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/transaction-methods > /dev/null - -echo " - GET /test/prepare-statement" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/prepare-statement > /dev/null - -echo " - GET /test/change-user" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/change-user > /dev/null - -echo " - GET /test/nested-null-values" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/nested-null-values > /dev/null - -echo " - GET /test/binary-data" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/binary-data > /dev/null - -echo " - GET /test/knex-raw-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/knex-raw-query > /dev/null - -echo " - POST /test/knex-savepoint" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/knex-savepoint > /dev/null - -echo " - GET /test/knex-streaming" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/knex-streaming > /dev/null - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: MYSQL2 (CJS)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" +trap cleanup EXIT -# Step 7.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -# Step 8: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "MySQL2 E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/src/index.ts b/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/src/index.ts index d0b7abc7..9fa86e76 100644 --- a/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/src/index.ts +++ b/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/src/index.ts @@ -130,8 +130,6 @@ const server = http.createServer(async (req, res) => { const url = req.url || "/"; const method = req.method || "GET"; - console.log(`Received request: ${method} ${url}`); - try { // Health check endpoint if (url === "/health" && method === "GET") { diff --git a/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/src/tdInit.ts b/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/src/tdInit.ts index 09b04a42..9b1acad7 100644 --- a/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/src/tdInit.ts +++ b/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/src/tdInit.ts @@ -3,7 +3,6 @@ import { TuskDrift } from '@use-tusk/drift-node-sdk'; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/src/test_requests.mjs b/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/src/test_requests.mjs new file mode 100644 index 00000000..6bb49696 --- /dev/null +++ b/src/instrumentation/libraries/mysql2/e2e-tests/cjs-mysql2/src/test_requests.mjs @@ -0,0 +1,34 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('GET', '/test/connection-query'); +await makeRequest('POST', '/test/connection-parameterized', { body: { userId: 1 } }); +await makeRequest('GET', '/test/connection-execute'); +await makeRequest('POST', '/test/connection-execute-params', { body: { userId: 2 } }); +await makeRequest('GET', '/test/pool-query'); +await makeRequest('POST', '/test/pool-parameterized', { body: { userId: 1 } }); +await makeRequest('GET', '/test/pool-execute'); +await makeRequest('POST', '/test/pool-execute-params', { body: { userId: 2 } }); +await makeRequest('GET', '/test/pool-getConnection'); +await makeRequest('GET', '/test/connection-connect'); +await makeRequest('GET', '/test/connection-ping'); +await makeRequest('GET', '/test/stream-query'); +await makeRequest('GET', '/test/sequelize-authenticate'); +await makeRequest('GET', '/test/sequelize-findall'); +await makeRequest('POST', '/test/sequelize-findone', { body: { userId: 1 } }); +await makeRequest('GET', '/test/sequelize-complex'); +await makeRequest('GET', '/test/sequelize-raw'); +await makeRequest('POST', '/test/sequelize-transaction'); +await makeRequest('GET', '/test/promise-connection-query'); +await makeRequest('GET', '/test/promise-pool-query'); +await makeRequest('GET', '/test/promise-pool-getconnection'); +await makeRequest('GET', '/test/transaction-methods'); +await makeRequest('GET', '/test/prepare-statement'); +await makeRequest('GET', '/test/change-user'); +await makeRequest('GET', '/test/nested-null-values'); +await makeRequest('GET', '/test/binary-data'); +await makeRequest('GET', '/test/knex-raw-query'); +await makeRequest('POST', '/test/knex-savepoint'); +await makeRequest('GET', '/test/knex-streaming'); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/Dockerfile b/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/Dockerfile index 30b345e7..37484c9b 100644 --- a/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/Dockerfile +++ b/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/Dockerfile @@ -20,5 +20,10 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ # Expose the server port EXPOSE 3000 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +COPY src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/entrypoint.sh ./ +RUN chmod +x entrypoint.sh +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/docker-compose.yml b/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/docker-compose.yml index 9a0d2fec..20475f39 100644 --- a/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/docker-compose.yml +++ b/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: mysql: image: mysql:8.0 @@ -21,8 +19,6 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" environment: - PORT=3000 - MYSQL_HOST=mysql @@ -31,17 +27,17 @@ services: - MYSQL_USER=testuser - MYSQL_PASSWORD=testpass - TUSK_ANALYTICS_DISABLED=1 + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-60} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} volumes: # Mount SDK source for hot reload (this is what package.json expects) - ../../../../../..:/sdk:ro # Mount .tusk folder to persist traces - - ./.tusk:/app/.tusk + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro # Mount app source for development - ./src:/app/src working_dir: /app depends_on: mysql: condition: service_healthy - # Keep container running without starting the app - # The CLI will control starting/stopping the app - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/entrypoint.sh b/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/entrypoint.sh new file mode 100755 index 00000000..ed1dce09 --- /dev/null +++ b/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +SERVER_WAIT_TIME=8 +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/run.sh b/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/run.sh index 3a8ebd3f..7867e91c 100755 --- a/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/run.sh +++ b/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/run.sh @@ -1,177 +1,51 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -# Generate unique docker compose project name PROJECT_NAME="mysql2-esm-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting MySQL2 ESM E2E test run on port ${APP_PORT}..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker containers (mysql + app) -echo "Step 1: Starting docker containers..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for containers to be ready -echo "Waiting for containers to be ready..." -sleep 5 - -# Wait for MySQL to be healthy -echo "Waiting for MySQL to be healthy..." -until docker compose -p $PROJECT_NAME exec -T mysql mysqladmin ping -h localhost -u testuser -ptestpass > /dev/null 2>&1; do - echo " MySQL is not ready yet..." - sleep 2 -done -echo "MySQL is ready!" - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "npm run build && npm run dev" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 8 - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all MySQL2 endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null - -echo " - GET /test/connection-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/connection-query > /dev/null - -echo " - POST /test/connection-parameterized" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"userId": 1}' http://localhost:3000/test/connection-parameterized > /dev/null - -echo " - GET /test/connection-execute" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/connection-execute > /dev/null - -echo " - POST /test/connection-execute-params" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"userId": 2}' http://localhost:3000/test/connection-execute-params > /dev/null - -echo " - GET /test/pool-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-query > /dev/null - -echo " - POST /test/pool-parameterized" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"userId": 1}' http://localhost:3000/test/pool-parameterized > /dev/null - -echo " - GET /test/pool-execute" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-execute > /dev/null - -echo " - POST /test/pool-execute-params" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"userId": 2}' http://localhost:3000/test/pool-execute-params > /dev/null - -echo " - GET /test/pool-getConnection" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-getConnection > /dev/null - -echo " - GET /test/connection-connect" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/connection-connect > /dev/null - -echo " - GET /test/connection-ping" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/connection-ping > /dev/null - -echo " - GET /test/stream-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/stream-query > /dev/null +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' -echo " - GET /test/sequelize-authenticate" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/sequelize-authenticate > /dev/null - -echo " - GET /test/sequelize-findall" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/sequelize-findall > /dev/null - -echo " - POST /test/sequelize-findone" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"userId": 1}' http://localhost:3000/test/sequelize-findone > /dev/null - -echo " - GET /test/sequelize-complex" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/sequelize-complex > /dev/null - -echo " - GET /test/sequelize-raw" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/sequelize-raw > /dev/null - -echo " - POST /test/sequelize-transaction" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/sequelize-transaction > /dev/null - -echo " - GET /test/promise-connection-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/promise-connection-query > /dev/null - -echo " - GET /test/promise-pool-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/promise-pool-query > /dev/null - -echo " - GET /test/promise-pool-getconnection" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/promise-pool-getconnection > /dev/null - -echo " - GET /test/transaction-methods" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/transaction-methods > /dev/null - -echo " - GET /test/prepare-statement" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/prepare-statement > /dev/null - -echo " - GET /test/change-user" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/change-user > /dev/null - -echo " - GET /test/nested-null-values" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/nested-null-values > /dev/null - -echo " - GET /test/binary-data" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/binary-data > /dev/null - -echo " - GET /test/knex-raw-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/knex-raw-query > /dev/null - -echo " - POST /test/knex-savepoint" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/knex-savepoint > /dev/null - -echo " - GET /test/knex-streaming" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/knex-streaming > /dev/null - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: MYSQL2 (ESM)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" +trap cleanup EXIT -# Step 7.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -# Step 8: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "MySQL2 ESM E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/src/index.ts b/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/src/index.ts index 330487b6..6214b5bb 100644 --- a/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/src/index.ts +++ b/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/src/index.ts @@ -130,8 +130,6 @@ const server = http.createServer(async (req, res) => { const url = req.url || "/"; const method = req.method || "GET"; - console.log(`Received request: ${method} ${url}`); - try { // Health check endpoint if (url === "/health" && method === "GET") { diff --git a/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/src/tdInit.ts b/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/src/tdInit.ts index cf11e0f1..d8e4a51f 100644 --- a/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/src/tdInit.ts +++ b/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/src/tdInit.ts @@ -10,7 +10,6 @@ import { TuskDrift } from '@use-tusk/drift-node-sdk'; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/src/test_requests.mjs b/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/src/test_requests.mjs new file mode 100644 index 00000000..6bb49696 --- /dev/null +++ b/src/instrumentation/libraries/mysql2/e2e-tests/esm-mysql2/src/test_requests.mjs @@ -0,0 +1,34 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('GET', '/test/connection-query'); +await makeRequest('POST', '/test/connection-parameterized', { body: { userId: 1 } }); +await makeRequest('GET', '/test/connection-execute'); +await makeRequest('POST', '/test/connection-execute-params', { body: { userId: 2 } }); +await makeRequest('GET', '/test/pool-query'); +await makeRequest('POST', '/test/pool-parameterized', { body: { userId: 1 } }); +await makeRequest('GET', '/test/pool-execute'); +await makeRequest('POST', '/test/pool-execute-params', { body: { userId: 2 } }); +await makeRequest('GET', '/test/pool-getConnection'); +await makeRequest('GET', '/test/connection-connect'); +await makeRequest('GET', '/test/connection-ping'); +await makeRequest('GET', '/test/stream-query'); +await makeRequest('GET', '/test/sequelize-authenticate'); +await makeRequest('GET', '/test/sequelize-findall'); +await makeRequest('POST', '/test/sequelize-findone', { body: { userId: 1 } }); +await makeRequest('GET', '/test/sequelize-complex'); +await makeRequest('GET', '/test/sequelize-raw'); +await makeRequest('POST', '/test/sequelize-transaction'); +await makeRequest('GET', '/test/promise-connection-query'); +await makeRequest('GET', '/test/promise-pool-query'); +await makeRequest('GET', '/test/promise-pool-getconnection'); +await makeRequest('GET', '/test/transaction-methods'); +await makeRequest('GET', '/test/prepare-statement'); +await makeRequest('GET', '/test/change-user'); +await makeRequest('GET', '/test/nested-null-values'); +await makeRequest('GET', '/test/binary-data'); +await makeRequest('GET', '/test/knex-raw-query'); +await makeRequest('POST', '/test/knex-savepoint'); +await makeRequest('GET', '/test/knex-streaming'); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/Dockerfile b/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/Dockerfile index bdda178a..5a4bc22a 100644 --- a/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/Dockerfile +++ b/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/Dockerfile @@ -21,5 +21,13 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ # Expose the server port EXPOSE 3000 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +COPY src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/entrypoint.sh ./ +RUN chmod +x entrypoint.sh +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs + +# Copy shared test infrastructure +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh + +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/docker-compose.yml b/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/docker-compose.yml index e44b5389..27850756 100644 --- a/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/docker-compose.yml +++ b/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: app: build: @@ -8,24 +6,22 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" environment: - PORT=3000 - NODE_PATH=/sdk/node_modules - TUSK_ANALYTICS_DISABLED=1 + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-5} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} volumes: # Mount SDK source for hot reload (this is what package.json expects) - ../../../../../..:/sdk:ro # Mount SDK's node_modules into the symlinked package location - ../../../../../../node_modules:/app/node_modules/@use-tusk/drift-node-sdk/node_modules:ro - # Mount .tusk folder to persist traces - - ./.tusk:/app/.tusk + # Mount .tusk config + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro # Mount app source for development - ./src:/app/src # Mount Next.js config files - ./next.config.js:/app/next.config.js working_dir: /app - # Keep container running without starting the app - # The CLI will control starting/stopping the app - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/entrypoint.sh b/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/entrypoint.sh new file mode 100755 index 00000000..b2e6f53a --- /dev/null +++ b/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +SERVER_WAIT_TIME=5 +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/run.sh b/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/run.sh index 71b7ca8a..424260bb 100755 --- a/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/run.sh +++ b/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/run.sh @@ -1,95 +1,51 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -# Generate unique docker compose project name PROJECT_NAME="nextjs-cjs-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting Next.js (CJS) E2E test run on port ${APP_PORT}..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker container -echo "Step 1: Starting docker container..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for container to be ready -echo "Waiting for container to be ready..." -sleep 3 - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Step 3: Build the Next.js app -echo "Step 3: Building Next.js app..." -docker compose -p $PROJECT_NAME exec -T app npm run build - -# Step 4: Start server in RECORD mode -echo "Step 4: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "npm run dev" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 5 - -# Step 5: Hit all endpoints -echo "Step 5: Hitting all endpoints..." +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' -echo " - GET /api/health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/api/health > /dev/null - -echo " - GET /api/weather (default location)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/api/weather > /dev/null - -echo " - GET /api/weather?location=London" -docker compose -p $PROJECT_NAME exec -T app curl -s "http://localhost:3000/api/weather?location=London" > /dev/null - -echo " - POST /api/weather" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"location":"Tokyo"}' http://localhost:3000/api/weather > /dev/null - -echo "All endpoints hit successfully." - -# Step 6: Wait before stopping server -echo "Step 6: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: NEXTJS (CJS)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" -# Step 7: Run tests using tusk CLI -echo "Step 7: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -# Step 8: Log test results -parse_and_display_test_results "$TEST_RESULTS" +trap cleanup EXIT -# Step 8.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -# Step 9: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 9: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 10: Clean up traces and logs -echo "Step 10: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "Next.js (CJS) E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/src/app/api/weather/route.ts b/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/src/app/api/weather/route.ts index 0e25e67b..4d27694e 100644 --- a/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/src/app/api/weather/route.ts +++ b/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/src/app/api/weather/route.ts @@ -14,10 +14,6 @@ export async function GET(request: NextRequest) { `http://wttr.in/${encodeURIComponent(weatherLocation)}?format=j1`, ); - console.log("Weather API call successful", { - location: weatherLocation, - }); - // Extract only the requested fields from current conditions const currentCondition = response.data.current_condition[0]; const current = { @@ -34,14 +30,17 @@ export async function GET(request: NextRequest) { source: "wttr.in", }); } catch (error) { - console.error("Error getting weather data", { error, location: request.nextUrl.searchParams.get("location") }); + console.error("Error getting weather data", { + error, + location: request.nextUrl.searchParams.get("location"), + }); return NextResponse.json( { error: "Failed to fetch weather data", message: error instanceof Error ? error.message : "Unknown error", }, - { status: 500 } + { status: 500 }, ); } } @@ -54,19 +53,10 @@ export async function POST(request: NextRequest) { // Validate that location is provided if (!location) { - return NextResponse.json( - { error: "Location is required in request body" }, - { status: 400 } - ); + return NextResponse.json({ error: "Location is required in request body" }, { status: 400 }); } - const response = await axios.get( - `http://wttr.in/${encodeURIComponent(location)}?format=j1`, - ); - - console.log("Weather API call successful (POST)", { - location: location, - }); + const response = await axios.get(`http://wttr.in/${encodeURIComponent(location)}?format=j1`); // Extract only the requested fields from current conditions const currentCondition = response.data.current_condition[0]; @@ -91,7 +81,7 @@ export async function POST(request: NextRequest) { error: "Failed to fetch weather data", message: error instanceof Error ? error.message : "Unknown error", }, - { status: 500 } + { status: 500 }, ); } } diff --git a/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/src/instrumentation.ts b/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/src/instrumentation.ts index ee6f55fa..c011388f 100644 --- a/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/src/instrumentation.ts +++ b/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/src/instrumentation.ts @@ -1,22 +1,21 @@ export async function register() { if (process.env.NEXT_RUNTIME === "nodejs") { - console.log('[Next.js Instrumentation] Initializing Tusk Drift SDK...'); - console.log('[Next.js Instrumentation] TUSK_DRIFT_MODE:', process.env.TUSK_DRIFT_MODE); - console.log('[Next.js Instrumentation] NODE_ENV:', process.env.NODE_ENV); + console.log("[Next.js Instrumentation] Initializing Tusk Drift SDK..."); + console.log("[Next.js Instrumentation] TUSK_DRIFT_MODE:", process.env.TUSK_DRIFT_MODE); + console.log("[Next.js Instrumentation] NODE_ENV:", process.env.NODE_ENV); const { TuskDrift } = await import("@use-tusk/drift-node-sdk"); TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); - console.log('[Next.js Instrumentation] SDK initialized, marking as ready...'); + console.log("[Next.js Instrumentation] SDK initialized, marking as ready..."); // Mark app as ready immediately TuskDrift.markAppAsReady(); - console.log('[Next.js Instrumentation] SDK marked as ready'); + console.log("[Next.js Instrumentation] SDK marked as ready"); } } diff --git a/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/src/test_requests.mjs b/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/src/test_requests.mjs new file mode 100644 index 00000000..18b3db7e --- /dev/null +++ b/src/instrumentation/libraries/nextjs/e2e-tests/cjs-nextjs/src/test_requests.mjs @@ -0,0 +1,8 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/api/health'); +await makeRequest('GET', '/api/weather'); +await makeRequest('GET', '/api/weather?location=London'); +await makeRequest('POST', '/api/weather', { body: { location: 'Tokyo' } }); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/Dockerfile b/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/Dockerfile index 00f6ad53..65e2c8e9 100644 --- a/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/Dockerfile +++ b/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/Dockerfile @@ -21,5 +21,13 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ # Expose the server port EXPOSE 3000 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +COPY src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/entrypoint.sh ./ +RUN chmod +x entrypoint.sh +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs + +# Copy shared test infrastructure +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh + +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/docker-compose.yml b/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/docker-compose.yml index 5280466d..aef10c0c 100644 --- a/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/docker-compose.yml +++ b/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: app: build: @@ -8,24 +6,22 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" environment: - PORT=3000 - NODE_PATH=/sdk/node_modules - TUSK_ANALYTICS_DISABLED=1 + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-5} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} volumes: # Mount SDK source for hot reload (this is what package.json expects) - ../../../../../..:/sdk:ro # Mount SDK's node_modules into the symlinked package location - ../../../../../../node_modules:/app/node_modules/@use-tusk/drift-node-sdk/node_modules:ro - # Mount .tusk folder to persist traces - - ./.tusk:/app/.tusk + # Mount .tusk config + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro # Mount app source for development - ./src:/app/src # Mount Next.js config files - ./next.config.mjs:/app/next.config.mjs working_dir: /app - # Keep container running without starting the app - # The CLI will control starting/stopping the app - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/entrypoint.sh b/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/entrypoint.sh new file mode 100755 index 00000000..b2e6f53a --- /dev/null +++ b/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +SERVER_WAIT_TIME=5 +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/run.sh b/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/run.sh index 936fd052..78e9dc15 100755 --- a/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/run.sh +++ b/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/run.sh @@ -1,95 +1,51 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -# Generate unique docker compose project name PROJECT_NAME="nextjs-esm-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting Next.js (ESM) E2E test run on port ${APP_PORT}..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker container -echo "Step 1: Starting docker container..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for container to be ready -echo "Waiting for container to be ready..." -sleep 3 - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Step 3: Build the Next.js app -echo "Step 3: Building Next.js app..." -docker compose -p $PROJECT_NAME exec -T app npm run build - -# Step 4: Start server in RECORD mode -echo "Step 4: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "npm run dev" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 5 - -# Step 5: Hit all endpoints -echo "Step 5: Hitting all endpoints..." +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' -echo " - GET /api/health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/api/health > /dev/null - -echo " - GET /api/weather (default location)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/api/weather > /dev/null - -echo " - GET /api/weather?location=London" -docker compose -p $PROJECT_NAME exec -T app curl -s "http://localhost:3000/api/weather?location=London" > /dev/null - -echo " - POST /api/weather" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"location":"Tokyo"}' http://localhost:3000/api/weather > /dev/null - -echo "All endpoints hit successfully." - -# Step 6: Wait before stopping server -echo "Step 6: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: NEXTJS (ESM)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" -# Step 7: Run tests using tusk CLI -echo "Step 7: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -# Step 8: Log test results -parse_and_display_test_results "$TEST_RESULTS" +trap cleanup EXIT -# Step 8.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -# Step 9: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 9: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 10: Clean up traces and logs -echo "Step 10: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "Next.js (ESM) E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/src/instrumentation.ts b/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/src/instrumentation.ts index ee6f55fa..c011388f 100644 --- a/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/src/instrumentation.ts +++ b/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/src/instrumentation.ts @@ -1,22 +1,21 @@ export async function register() { if (process.env.NEXT_RUNTIME === "nodejs") { - console.log('[Next.js Instrumentation] Initializing Tusk Drift SDK...'); - console.log('[Next.js Instrumentation] TUSK_DRIFT_MODE:', process.env.TUSK_DRIFT_MODE); - console.log('[Next.js Instrumentation] NODE_ENV:', process.env.NODE_ENV); + console.log("[Next.js Instrumentation] Initializing Tusk Drift SDK..."); + console.log("[Next.js Instrumentation] TUSK_DRIFT_MODE:", process.env.TUSK_DRIFT_MODE); + console.log("[Next.js Instrumentation] NODE_ENV:", process.env.NODE_ENV); const { TuskDrift } = await import("@use-tusk/drift-node-sdk"); TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); - console.log('[Next.js Instrumentation] SDK initialized, marking as ready...'); + console.log("[Next.js Instrumentation] SDK initialized, marking as ready..."); // Mark app as ready immediately TuskDrift.markAppAsReady(); - console.log('[Next.js Instrumentation] SDK marked as ready'); + console.log("[Next.js Instrumentation] SDK marked as ready"); } } diff --git a/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/src/test_requests.mjs b/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/src/test_requests.mjs new file mode 100644 index 00000000..18b3db7e --- /dev/null +++ b/src/instrumentation/libraries/nextjs/e2e-tests/esm-nextjs/src/test_requests.mjs @@ -0,0 +1,8 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/api/health'); +await makeRequest('GET', '/api/weather'); +await makeRequest('GET', '/api/weather?location=London'); +await makeRequest('POST', '/api/weather', { body: { location: 'Tokyo' } }); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/Dockerfile b/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/Dockerfile index c064cbdb..843986e6 100644 --- a/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/Dockerfile +++ b/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/Dockerfile @@ -20,5 +20,10 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ # Expose the server port EXPOSE 3000 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +COPY src/instrumentation/libraries/pg/e2e-tests/cjs-pg/entrypoint.sh ./ +RUN chmod +x entrypoint.sh +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/docker-compose.yml b/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/docker-compose.yml index 13106fbd..b757d26d 100644 --- a/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/docker-compose.yml +++ b/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: postgres: image: postgres:13 @@ -20,9 +18,10 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" environment: + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-5} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} - PORT=3000 - POSTGRES_HOST=postgres - POSTGRES_PORT=5432 @@ -33,14 +32,11 @@ services: volumes: # Mount SDK source for hot reload (this is what package.json expects) - ../../../../../..:/sdk:ro - # Mount .tusk folder to persist traces - - ./.tusk:/app/.tusk + # Mount .tusk config to persist configuration + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro # Mount app source for development - ./src:/app/src working_dir: /app depends_on: postgres: condition: service_healthy - # Keep container running without starting the app - # The CLI will control starting/stopping the app - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/entrypoint.sh b/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/entrypoint.sh new file mode 100755 index 00000000..ed1dce09 --- /dev/null +++ b/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +SERVER_WAIT_TIME=8 +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/run.sh b/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/run.sh index f1bc0bf7..9b1805cb 100755 --- a/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/run.sh +++ b/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/run.sh @@ -1,123 +1,51 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -# Generate unique docker compose project name PROJECT_NAME="pg-cjs-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting PostgreSQL E2E test run on port ${APP_PORT}..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker containers (postgres + app) -echo "Step 1: Starting docker containers..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for containers to be ready -echo "Waiting for containers to be ready..." -sleep 5 - -# Wait for PostgreSQL to be healthy -echo "Waiting for PostgreSQL to be healthy..." -until docker compose -p $PROJECT_NAME exec -T postgres pg_isready -U testuser -d testdb > /dev/null 2>&1; do - echo " PostgreSQL is not ready yet..." - sleep 2 -done -echo "PostgreSQL is ready!" - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "npm run build && npm run dev" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 8 - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all PostgreSQL endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null - -echo " - GET /test/basic-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/basic-query > /dev/null - -echo " - POST /test/parameterized-query" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"userId": 1}' http://localhost:3000/test/parameterized-query > /dev/null - -echo " - GET /test/client-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/client-query > /dev/null +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' -echo " - GET /test/client-connect" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/client-connect > /dev/null - -echo " - GET /test/client-close" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/client-close > /dev/null - -echo " - GET /test/pool-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-query > /dev/null - -echo " - POST /test/pool-parameterized" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"userId": 2}' http://localhost:3000/test/pool-parameterized > /dev/null - -echo " - GET /test/pool-connect" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-connect > /dev/null - -echo " - GET /test/pool-transaction" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-transaction > /dev/null - -echo " - GET /test/query-rowmode-array" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/query-rowmode-array > /dev/null - -echo " - GET /test/multi-statement" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/multi-statement > /dev/null - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: PG (CJS)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" +trap cleanup EXIT -# Step 7.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -# Step 8: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "PostgreSQL E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/src/index.ts b/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/src/index.ts index f2456a30..1d532cae 100644 --- a/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/src/index.ts +++ b/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/src/index.ts @@ -71,8 +71,6 @@ const server = http.createServer(async (req, res) => { const url = req.url || "/"; const method = req.method || "GET"; - console.log(`Received request: ${method} ${url}`); - try { // Health check endpoint if (url === "/health" && method === "GET") { diff --git a/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/src/tdInit.ts b/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/src/tdInit.ts index 09b04a42..9b1acad7 100644 --- a/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/src/tdInit.ts +++ b/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/src/tdInit.ts @@ -3,7 +3,6 @@ import { TuskDrift } from '@use-tusk/drift-node-sdk'; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/src/test_requests.mjs b/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/src/test_requests.mjs new file mode 100644 index 00000000..7cbb3051 --- /dev/null +++ b/src/instrumentation/libraries/pg/e2e-tests/cjs-pg/src/test_requests.mjs @@ -0,0 +1,16 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('GET', '/test/basic-query'); +await makeRequest('POST', '/test/parameterized-query', { body: { userId: 1 } }); +await makeRequest('GET', '/test/client-query'); +await makeRequest('GET', '/test/client-connect'); +await makeRequest('GET', '/test/client-close'); +await makeRequest('GET', '/test/pool-query'); +await makeRequest('POST', '/test/pool-parameterized', { body: { userId: 2 } }); +await makeRequest('GET', '/test/pool-connect'); +await makeRequest('GET', '/test/pool-transaction'); +await makeRequest('GET', '/test/query-rowmode-array'); +await makeRequest('GET', '/test/multi-statement'); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/pg/e2e-tests/esm-pg/Dockerfile b/src/instrumentation/libraries/pg/e2e-tests/esm-pg/Dockerfile index a6124caf..facdcb78 100644 --- a/src/instrumentation/libraries/pg/e2e-tests/esm-pg/Dockerfile +++ b/src/instrumentation/libraries/pg/e2e-tests/esm-pg/Dockerfile @@ -20,5 +20,10 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ # Expose the server port EXPOSE 3000 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +COPY src/instrumentation/libraries/pg/e2e-tests/esm-pg/entrypoint.sh ./ +RUN chmod +x entrypoint.sh +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/pg/e2e-tests/esm-pg/docker-compose.yml b/src/instrumentation/libraries/pg/e2e-tests/esm-pg/docker-compose.yml index e8c224fc..1a899d4b 100644 --- a/src/instrumentation/libraries/pg/e2e-tests/esm-pg/docker-compose.yml +++ b/src/instrumentation/libraries/pg/e2e-tests/esm-pg/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: postgres: image: postgres:13 @@ -20,9 +18,10 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" environment: + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-5} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} - PORT=3000 - POSTGRES_HOST=postgres - POSTGRES_PORT=5432 @@ -33,14 +32,11 @@ services: volumes: # Mount SDK source for hot reload (this is what package.json expects) - ../../../../../..:/sdk:ro - # Mount .tusk folder to persist traces - - ./.tusk:/app/.tusk + # Mount .tusk config to persist configuration + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro # Mount app source for development - ./src:/app/src working_dir: /app depends_on: postgres: condition: service_healthy - # Keep container running without starting the app - # The CLI will control starting/stopping the app - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/pg/e2e-tests/esm-pg/entrypoint.sh b/src/instrumentation/libraries/pg/e2e-tests/esm-pg/entrypoint.sh new file mode 100755 index 00000000..ed1dce09 --- /dev/null +++ b/src/instrumentation/libraries/pg/e2e-tests/esm-pg/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +SERVER_WAIT_TIME=8 +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/pg/e2e-tests/esm-pg/run.sh b/src/instrumentation/libraries/pg/e2e-tests/esm-pg/run.sh index 81358b05..643d9495 100755 --- a/src/instrumentation/libraries/pg/e2e-tests/esm-pg/run.sh +++ b/src/instrumentation/libraries/pg/e2e-tests/esm-pg/run.sh @@ -1,123 +1,51 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -# Generate unique docker compose project name PROJECT_NAME="pg-esm-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting PostgreSQL ESM E2E test run on port ${APP_PORT}..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker containers (postgres + app) -echo "Step 1: Starting docker containers..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for containers to be ready -echo "Waiting for containers to be ready..." -sleep 5 - -# Wait for PostgreSQL to be healthy -echo "Waiting for PostgreSQL to be healthy..." -until docker compose -p $PROJECT_NAME exec -T postgres pg_isready -U testuser -d testdb > /dev/null 2>&1; do - echo " PostgreSQL is not ready yet..." - sleep 2 -done -echo "PostgreSQL is ready!" - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "npm run build && npm run dev" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 8 - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all PostgreSQL endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null - -echo " - GET /test/basic-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/basic-query > /dev/null - -echo " - POST /test/parameterized-query" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"userId": 1}' http://localhost:3000/test/parameterized-query > /dev/null - -echo " - GET /test/client-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/client-query > /dev/null +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' -echo " - GET /test/client-connect" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/client-connect > /dev/null - -echo " - GET /test/client-close" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/client-close > /dev/null - -echo " - GET /test/pool-query" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-query > /dev/null - -echo " - POST /test/pool-parameterized" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"userId": 2}' http://localhost:3000/test/pool-parameterized > /dev/null - -echo " - GET /test/pool-connect" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-connect > /dev/null - -echo " - GET /test/pool-transaction" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pool-transaction > /dev/null - -echo " - GET /test/query-rowmode-array" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/query-rowmode-array > /dev/null - -echo " - GET /test/multi-statement" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/multi-statement > /dev/null - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: PG (ESM)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" +trap cleanup EXIT -# Step 7.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -# Step 8: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "PostgreSQL ESM E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/pg/e2e-tests/esm-pg/src/index.ts b/src/instrumentation/libraries/pg/e2e-tests/esm-pg/src/index.ts index 64f6fe1d..326cbe60 100644 --- a/src/instrumentation/libraries/pg/e2e-tests/esm-pg/src/index.ts +++ b/src/instrumentation/libraries/pg/e2e-tests/esm-pg/src/index.ts @@ -73,8 +73,6 @@ const server = http.createServer(async (req, res) => { const url = req.url || "/"; const method = req.method || "GET"; - console.log(`Received request: ${method} ${url}`); - try { // Health check endpoint if (url === "/health" && method === "GET") { diff --git a/src/instrumentation/libraries/pg/e2e-tests/esm-pg/src/tdInit.ts b/src/instrumentation/libraries/pg/e2e-tests/esm-pg/src/tdInit.ts index cf11e0f1..d8e4a51f 100644 --- a/src/instrumentation/libraries/pg/e2e-tests/esm-pg/src/tdInit.ts +++ b/src/instrumentation/libraries/pg/e2e-tests/esm-pg/src/tdInit.ts @@ -10,7 +10,6 @@ import { TuskDrift } from '@use-tusk/drift-node-sdk'; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/pg/e2e-tests/esm-pg/src/test_requests.mjs b/src/instrumentation/libraries/pg/e2e-tests/esm-pg/src/test_requests.mjs new file mode 100644 index 00000000..7cbb3051 --- /dev/null +++ b/src/instrumentation/libraries/pg/e2e-tests/esm-pg/src/test_requests.mjs @@ -0,0 +1,16 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('GET', '/test/basic-query'); +await makeRequest('POST', '/test/parameterized-query', { body: { userId: 1 } }); +await makeRequest('GET', '/test/client-query'); +await makeRequest('GET', '/test/client-connect'); +await makeRequest('GET', '/test/client-close'); +await makeRequest('GET', '/test/pool-query'); +await makeRequest('POST', '/test/pool-parameterized', { body: { userId: 2 } }); +await makeRequest('GET', '/test/pool-connect'); +await makeRequest('GET', '/test/pool-transaction'); +await makeRequest('GET', '/test/query-rowmode-array'); +await makeRequest('GET', '/test/multi-statement'); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/Dockerfile b/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/Dockerfile index 6efcba0c..d859bc8f 100644 --- a/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/Dockerfile +++ b/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/Dockerfile @@ -20,5 +20,10 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ # Expose the server port EXPOSE 3000 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +COPY src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/entrypoint.sh ./ +RUN chmod +x entrypoint.sh +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/docker-compose.yml b/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/docker-compose.yml index 710012cd..6858b623 100644 --- a/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/docker-compose.yml +++ b/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: postgres: image: postgres:13 @@ -20,9 +18,10 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" environment: + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-5} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} - PORT=3000 - POSTGRES_HOST=postgres - POSTGRES_PORT=5432 @@ -33,14 +32,11 @@ services: volumes: # Mount SDK source for hot reload (this is what package.json expects) - ../../../../../..:/sdk:ro - # Mount .tusk folder to persist traces - - ./.tusk:/app/.tusk + # Mount .tusk config to persist configuration + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro # Mount app source for development - ./src:/app/src working_dir: /app depends_on: postgres: condition: service_healthy - # Keep container running without starting the app - # The CLI will control starting/stopping the app - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/entrypoint.sh b/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/entrypoint.sh new file mode 100755 index 00000000..188f3789 --- /dev/null +++ b/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +SERVER_WAIT_TIME=10 +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/run.sh b/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/run.sh index ab64e170..33ee0467 100755 --- a/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/run.sh +++ b/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/run.sh @@ -1,168 +1,51 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -# Generate unique docker compose project name PROJECT_NAME="postgres-cjs-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting Postgres (Drizzle + postgres) E2E test run on port ${APP_PORT}..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker containers (postgres + app) -echo "Step 1: Starting docker containers..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for containers to be ready -echo "Waiting for containers to be ready..." -sleep 5 - -# Wait for PostgreSQL to be healthy -echo "Waiting for PostgreSQL to be healthy..." -until docker compose -p $PROJECT_NAME exec -T postgres pg_isready -U testuser -d testdb > /dev/null 2>&1; do - echo " PostgreSQL is not ready yet..." - sleep 2 -done -echo "PostgreSQL is ready!" - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "npm run build && npm run dev" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 10 - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all Postgres + Drizzle endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null - -echo " - GET /cache/all" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/cache/all > /dev/null - -echo " - GET /cache/sample" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/cache/sample > /dev/null - -echo " - GET /cache/raw" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/cache/raw > /dev/null - -echo " - POST /cache/execute-raw" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/cache/execute-raw > /dev/null - -echo " - POST /cache/insert" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key":"test_insert","value":"test_value"}' http://localhost:3000/cache/insert > /dev/null - -echo " - PUT /cache/update" -docker compose -p $PROJECT_NAME exec -T app curl -s -X PUT -H "Content-Type: application/json" -d '{"key":"test_key_1","value":"updated_value"}' http://localhost:3000/cache/update > /dev/null - -echo " - DELETE /cache/delete" -docker compose -p $PROJECT_NAME exec -T app curl -s -X DELETE -H "Content-Type: application/json" -d '{"key":"test_insert"}' http://localhost:3000/cache/delete > /dev/null - -echo " - GET /users/by-email" -docker compose -p $PROJECT_NAME exec -T app curl -s "http://localhost:3000/users/by-email?email=alice@example.com" > /dev/null - -echo " - POST /users/insert" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"name":"Test User","email":"testuser@example.com"}' http://localhost:3000/users/insert > /dev/null - -echo " - GET /cache/dynamic-fragments" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/cache/dynamic-fragments > /dev/null +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' -echo " - POST /cache/update-with-fragments" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/cache/update-with-fragments > /dev/null - -echo " - GET /cache/complex-fragments" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/cache/complex-fragments > /dev/null - -echo " - GET /test/execute-method" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/execute-method > /dev/null - -echo " - GET /test/sql-file" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/sql-file > /dev/null - -echo " - GET /test/pending-query-raw" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pending-query-raw > /dev/null - -echo " - GET /test/sql-reserve" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/sql-reserve > /dev/null - -echo " - GET /test/sql-cursor" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/sql-cursor > /dev/null - -echo " - GET /test/sql-cursor-callback" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/sql-cursor-callback > /dev/null - -echo " - GET /test/sql-foreach" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/sql-foreach > /dev/null - -echo " - GET /test/describe-method" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/describe-method > /dev/null - -echo " - GET /test/savepoint" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/savepoint > /dev/null - -echo " - GET /test/listen-notify" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/listen-notify > /dev/null - -echo " - GET /test/bytea-data" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/bytea-data > /dev/null - -echo " - GET /test/unsafe-cursor" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/unsafe-cursor > /dev/null - -echo " - GET /test/unsafe-foreach" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/unsafe-foreach > /dev/null - -echo " - GET /test/large-object" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/large-object > /dev/null - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: POSTGRES (CJS)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" +trap cleanup EXIT -# Step 7.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -# Step 8: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "Postgres (Drizzle + postgres) E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/src/index.ts b/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/src/index.ts index 8ba9b63d..e7469230 100644 --- a/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/src/index.ts +++ b/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/src/index.ts @@ -97,13 +97,10 @@ app.get("/health", (req: Request, res: Response) => { // Drizzle query builder - select all from cache app.get("/cache/all", async (req: Request, res: Response) => { try { - console.log("Fetching all cache entries using Drizzle query builder..."); const db = getDb(); const result = await db.select().from(cacheTable); - console.log("Cache entries:", result); - res.json({ message: "All cache entries retrieved", count: result.length, @@ -118,7 +115,6 @@ app.get("/cache/all", async (req: Request, res: Response) => { // Drizzle query builder - select with limit app.get("/cache/sample", async (req: Request, res: Response) => { try { - console.log("Fetching cache sample..."); const db = getDb(); // Original Drizzle query builder approach @@ -132,9 +128,6 @@ app.get("/cache/sample", async (req: Request, res: Response) => { LIMIT 3 `); - console.log("Cache sample drizzle result:", drizzleResult); - console.log("Cache sample raw SQL result:", rawResult); - res.json({ message: "Cache sample retrieved", drizzleResult, @@ -149,8 +142,6 @@ app.get("/cache/sample", async (req: Request, res: Response) => { // Raw postgres template string query app.get("/cache/raw", async (req: Request, res: Response) => { try { - console.log("Fetching cache data using raw postgres template string..."); - // Create a postgres client instance const connectionString = process.env.DATABASE_URL || @@ -164,8 +155,6 @@ app.get("/cache/raw", async (req: Request, res: Response) => { LIMIT 2 `; - console.log("Raw postgres query result:", result); - await pgClient.end(); res.json({ @@ -182,7 +171,6 @@ app.get("/cache/raw", async (req: Request, res: Response) => { // Execute raw SQL using drizzle session.execute app.post("/cache/execute-raw", async (req: Request, res: Response) => { try { - console.log("Executing raw SQL using drizzle session.execute..."); const db = getDb(); // This uses db.execute() similar to your setTransaction example @@ -193,8 +181,6 @@ app.post("/cache/execute-raw", async (req: Request, res: Response) => { LIMIT 3 `); - console.log("Execute result:", result); - res.json({ message: "Raw SQL executed using drizzle session.execute", rowCount: result.length, @@ -209,7 +195,6 @@ app.post("/cache/execute-raw", async (req: Request, res: Response) => { // Drizzle insert app.post("/cache/insert", async (req: Request, res: Response) => { try { - console.log("Inserting cache entry using Drizzle..."); const db = getDb(); const { key, value } = req.body; @@ -224,8 +209,6 @@ app.post("/cache/insert", async (req: Request, res: Response) => { }) .returning(); - console.log("Insert result:", result); - res.json({ message: "Cache entry inserted", data: result, @@ -239,7 +222,6 @@ app.post("/cache/insert", async (req: Request, res: Response) => { // Drizzle update app.put("/cache/update", async (req: Request, res: Response) => { try { - console.log("Updating cache entry using Drizzle..."); const db = getDb(); const { key, value } = req.body; @@ -250,8 +232,6 @@ app.put("/cache/update", async (req: Request, res: Response) => { .where(eq(cacheTable.key, key)) .returning(); - console.log("Update result:", result); - res.json({ message: "Cache entry updated", data: result, @@ -265,15 +245,12 @@ app.put("/cache/update", async (req: Request, res: Response) => { // Drizzle delete app.delete("/cache/delete", async (req: Request, res: Response) => { try { - console.log("Deleting cache entry using Drizzle..."); const db = getDb(); const { key } = req.body; const result = await db.delete(cacheTable).where(eq(cacheTable.key, key)).returning(); - console.log("Delete result:", result); - res.json({ message: "Cache entry deleted", data: result, @@ -287,15 +264,12 @@ app.delete("/cache/delete", async (req: Request, res: Response) => { // Users - Drizzle select with where app.get("/users/by-email", async (req: Request, res: Response) => { try { - console.log("Fetching user by email using Drizzle..."); const db = getDb(); const email = (req.query.email as string) || "alice@example.com"; const result = await db.select().from(usersTable).where(eq(usersTable.email, email)); - console.log("User result:", result); - res.json({ message: "User retrieved by email", data: result, @@ -309,7 +283,6 @@ app.get("/users/by-email", async (req: Request, res: Response) => { // Users - Insert using Drizzle app.post("/users/insert", async (req: Request, res: Response) => { try { - console.log("Inserting user using Drizzle..."); const db = getDb(); const { name, email } = req.body; @@ -323,8 +296,6 @@ app.post("/users/insert", async (req: Request, res: Response) => { }) .returning(); - console.log("Insert user result:", result); - res.json({ message: "User inserted", data: result, @@ -338,7 +309,6 @@ app.post("/users/insert", async (req: Request, res: Response) => { // Test dynamic query building with sql() fragment helpers app.get("/cache/dynamic-fragments", async (req: Request, res: Response) => { try { - console.log("Testing dynamic query building with sql() fragment helpers..."); const connectionString = process.env.DATABASE_URL || `postgres://${process.env.POSTGRES_USER || "testuser"}:${process.env.POSTGRES_PASSWORD || "testpass"}@${process.env.POSTGRES_HOST || "postgres"}:${process.env.POSTGRES_PORT || "5432"}/${process.env.POSTGRES_DB || "testdb"}`; @@ -349,8 +319,6 @@ app.get("/cache/dynamic-fragments", async (req: Request, res: Response) => { const columns = ["key", "value"]; const result1 = await pgClient`SELECT ${pgClient(columns)} FROM cache LIMIT 2`; - console.log("Dynamic columns result:", result1); - // Test 2: Conditional WHERE clause using fragment const minId = 1; const useFilter = true; @@ -362,8 +330,6 @@ app.get("/cache/dynamic-fragments", async (req: Request, res: Response) => { LIMIT 2 `; - console.log("Conditional WHERE result:", result2); - // Test 3: Helper function that returns sql fragment (similar to customer's where() helper) const buildWhereClause = (conditions: { field: string; value: any }[]) => { if (conditions.length === 0) return pgClient``; @@ -387,8 +353,6 @@ app.get("/cache/dynamic-fragments", async (req: Request, res: Response) => { ${dynamicWhere} `; - console.log("Dynamic WHERE helper result:", result3); - await pgClient.end(); res.json({ @@ -408,7 +372,6 @@ app.get("/cache/dynamic-fragments", async (req: Request, res: Response) => { // Test UPDATE with dynamic fragments (mimics customer's exact pattern) app.post("/cache/update-with-fragments", async (req: Request, res: Response) => { try { - console.log("Testing UPDATE with dynamic sql() fragments..."); const connectionString = process.env.DATABASE_URL || `postgres://${process.env.POSTGRES_USER || "testuser"}:${process.env.POSTGRES_PASSWORD || "testpass"}@${process.env.POSTGRES_HOST || "postgres"}:${process.env.POSTGRES_PORT || "5432"}/${process.env.POSTGRES_DB || "testdb"}`; @@ -440,8 +403,6 @@ app.post("/cache/update-with-fragments", async (req: Request, res: Response) => WHERE ${where(selectors)} AND value IS NOT NULL `; - console.log("UPDATE with fragments result:", result); - await pgClient.end(); res.json({ @@ -459,7 +420,6 @@ app.post("/cache/update-with-fragments", async (req: Request, res: Response) => // Test complex nested fragments (advanced pattern) app.get("/cache/complex-fragments", async (req: Request, res: Response) => { try { - console.log("Testing complex nested sql() fragments..."); const connectionString = process.env.DATABASE_URL || `postgres://${process.env.POSTGRES_USER || "testuser"}:${process.env.POSTGRES_PASSWORD || "testpass"}@${process.env.POSTGRES_HOST || "postgres"}:${process.env.POSTGRES_PORT || "5432"}/${process.env.POSTGRES_DB || "testdb"}`; @@ -509,8 +469,6 @@ app.get("/cache/complex-fragments", async (req: Request, res: Response) => { LIMIT 3 `; - console.log("Complex fragments result:", result); - await pgClient.end(); res.json({ @@ -527,7 +485,6 @@ app.get("/cache/complex-fragments", async (req: Request, res: Response) => { // Test sql.file() method app.get("/test/sql-file", async (req: Request, res: Response) => { try { - console.log("Testing sql.file() method..."); const connectionString = process.env.DATABASE_URL || `postgres://${process.env.POSTGRES_USER || "testuser"}:${process.env.POSTGRES_PASSWORD || "testpass"}@${process.env.POSTGRES_HOST || "postgres"}:${process.env.POSTGRES_PORT || "5432"}/${process.env.POSTGRES_DB || "testdb"}`; @@ -537,8 +494,6 @@ app.get("/test/sql-file", async (req: Request, res: Response) => { // Execute query from file const result = await pgClient.file("/app/src/test-query.sql"); - console.log("SQL file result:", result); - await pgClient.end(); res.json({ @@ -555,7 +510,6 @@ app.get("/test/sql-file", async (req: Request, res: Response) => { // Test .execute() method for immediate execution app.get("/test/execute-method", async (req: Request, res: Response) => { try { - console.log("Testing .execute() method for immediate query execution..."); const connectionString = process.env.DATABASE_URL || `postgres://${process.env.POSTGRES_USER || "testuser"}:${process.env.POSTGRES_PASSWORD || "testpass"}@${process.env.POSTGRES_HOST || "postgres"}:${process.env.POSTGRES_PORT || "5432"}/${process.env.POSTGRES_DB || "testdb"}`; @@ -565,8 +519,6 @@ app.get("/test/execute-method", async (req: Request, res: Response) => { // Using .execute() forces the query to run immediately const result = await pgClient`SELECT * FROM cache LIMIT 1`.execute(); - console.log("Execute method result:", result); - await pgClient.end(); res.json({ @@ -583,7 +535,6 @@ app.get("/test/execute-method", async (req: Request, res: Response) => { // Test PendingQuery.raw() for raw buffer results app.get("/test/pending-query-raw", async (req: Request, res: Response) => { try { - console.log("Testing PendingQuery.raw() for raw buffer results..."); const connectionString = process.env.DATABASE_URL || `postgres://${process.env.POSTGRES_USER || "testuser"}:${process.env.POSTGRES_PASSWORD || "testpass"}@${process.env.POSTGRES_HOST || "postgres"}:${process.env.POSTGRES_PORT || "5432"}/${process.env.POSTGRES_DB || "testdb"}`; @@ -593,8 +544,6 @@ app.get("/test/pending-query-raw", async (req: Request, res: Response) => { // .raw() returns raw Buffer arrays instead of parsed objects const result = await pgClient`SELECT * FROM cache LIMIT 2`.raw(); - console.log("Raw buffer query result:", result); - await pgClient.end(); res.json({ @@ -612,7 +561,6 @@ app.get("/test/pending-query-raw", async (req: Request, res: Response) => { // Test sql.reserve() for reserved connections app.get("/test/sql-reserve", async (req: Request, res: Response) => { try { - console.log("Testing sql.reserve() for reserved connections..."); const connectionString = process.env.DATABASE_URL || `postgres://${process.env.POSTGRES_USER || "testuser"}:${process.env.POSTGRES_PASSWORD || "testpass"}@${process.env.POSTGRES_HOST || "postgres"}:${process.env.POSTGRES_PORT || "5432"}/${process.env.POSTGRES_DB || "testdb"}`; @@ -625,8 +573,6 @@ app.get("/test/sql-reserve", async (req: Request, res: Response) => { // Execute a query on the reserved connection const result = await reserved`SELECT * FROM cache LIMIT 2`; - console.log("Reserved connection query result:", result); - // Release the connection back to the pool reserved.release(); @@ -646,7 +592,6 @@ app.get("/test/sql-reserve", async (req: Request, res: Response) => { // Test sql.cursor() for cursor-based streaming app.get("/test/sql-cursor", async (req: Request, res: Response) => { try { - console.log("Testing sql.cursor() for cursor-based streaming..."); const connectionString = process.env.DATABASE_URL || `postgres://${process.env.POSTGRES_USER || "testuser"}:${process.env.POSTGRES_PASSWORD || "testpass"}@${process.env.POSTGRES_HOST || "postgres"}:${process.env.POSTGRES_PORT || "5432"}/${process.env.POSTGRES_DB || "testdb"}`; @@ -658,12 +603,9 @@ app.get("/test/sql-cursor", async (req: Request, res: Response) => { const cursor = pgClient`SELECT * FROM cache`.cursor(2); for await (const rows of cursor) { - console.log("Cursor batch:", rows); cursorResults.push(...rows); } - console.log("Cursor complete, total rows:", cursorResults.length); - await pgClient.end(); res.json({ @@ -680,7 +622,6 @@ app.get("/test/sql-cursor", async (req: Request, res: Response) => { // Test sql.cursor() with callback function app.get("/test/sql-cursor-callback", async (req: Request, res: Response) => { try { - console.log("Testing sql.cursor() with callback function..."); const connectionString = process.env.DATABASE_URL || `postgres://${process.env.POSTGRES_USER || "testuser"}:${process.env.POSTGRES_PASSWORD || "testpass"}@${process.env.POSTGRES_HOST || "postgres"}:${process.env.POSTGRES_PORT || "5432"}/${process.env.POSTGRES_DB || "testdb"}`; @@ -690,12 +631,9 @@ app.get("/test/sql-cursor-callback", async (req: Request, res: Response) => { // cursor(rows, fn) with callback - this delegates to original and goes through .then() const cursorResults: any[] = []; await pgClient`SELECT * FROM cache`.cursor(2, (rows) => { - console.log("Cursor callback batch:", rows); cursorResults.push(...rows); }); - console.log("Cursor with callback complete, total rows:", cursorResults.length); - await pgClient.end(); res.json({ @@ -712,7 +650,6 @@ app.get("/test/sql-cursor-callback", async (req: Request, res: Response) => { // Test sql.forEach() for row-by-row processing app.get("/test/sql-foreach", async (req: Request, res: Response) => { try { - console.log("Testing sql.forEach() for row-by-row processing..."); const connectionString = process.env.DATABASE_URL || `postgres://${process.env.POSTGRES_USER || "testuser"}:${process.env.POSTGRES_PASSWORD || "testpass"}@${process.env.POSTGRES_HOST || "postgres"}:${process.env.POSTGRES_PORT || "5432"}/${process.env.POSTGRES_DB || "testdb"}`; @@ -722,12 +659,9 @@ app.get("/test/sql-foreach", async (req: Request, res: Response) => { // forEach processes rows one at a time with a callback const forEachResults: any[] = []; await pgClient`SELECT * FROM cache LIMIT 3`.forEach((row) => { - console.log("forEach row:", row); forEachResults.push(row); }); - console.log("forEach complete, total rows:", forEachResults.length); - await pgClient.end(); res.json({ @@ -743,7 +677,6 @@ app.get("/test/sql-foreach", async (req: Request, res: Response) => { app.get("/test/describe-method", async (req: Request, res: Response) => { try { - console.log("Testing describe() method..."); const connectionString = process.env.DATABASE_URL || `postgres://${process.env.POSTGRES_USER || "testuser"}:${process.env.POSTGRES_PASSWORD || "testpass"}@${process.env.POSTGRES_HOST || "postgres"}:${process.env.POSTGRES_PORT || "5432"}/${process.env.POSTGRES_DB || "testdb"}`; @@ -753,8 +686,6 @@ app.get("/test/describe-method", async (req: Request, res: Response) => { // describe() returns statement metadata without executing the query const result = await pgClient`SELECT id, key, value FROM cache WHERE id = ${1}`.describe(); - console.log("describe() result:", result); - await pgClient.end(); res.json({ @@ -771,7 +702,6 @@ app.get("/test/describe-method", async (req: Request, res: Response) => { app.get("/test/savepoint", async (req: Request, res: Response) => { try { - console.log("Testing savepoint() nested transactions..."); const connectionString = process.env.DATABASE_URL || `postgres://${process.env.POSTGRES_USER || "testuser"}:${process.env.POSTGRES_PASSWORD || "testpass"}@${process.env.POSTGRES_HOST || "postgres"}:${process.env.POSTGRES_PORT || "5432"}/${process.env.POSTGRES_DB || "testdb"}`; @@ -798,8 +728,6 @@ app.get("/test/savepoint", async (req: Request, res: Response) => { return allUsers; }); - console.log("savepoint() result:", result); - // Cleanup await pgClient`DELETE FROM users WHERE email LIKE '%@test.com'`; await pgClient.end(); @@ -818,7 +746,6 @@ app.get("/test/savepoint", async (req: Request, res: Response) => { app.get("/test/listen-notify", async (req: Request, res: Response) => { try { - console.log("Testing listen() / notify()..."); const connectionString = process.env.DATABASE_URL || `postgres://${process.env.POSTGRES_USER || "testuser"}:${process.env.POSTGRES_PASSWORD || "testpass"}@${process.env.POSTGRES_HOST || "postgres"}:${process.env.POSTGRES_PORT || "5432"}/${process.env.POSTGRES_DB || "testdb"}`; @@ -829,7 +756,6 @@ app.get("/test/listen-notify", async (req: Request, res: Response) => { // Set up listener const { unlisten } = await pgClient.listen("test_channel", (payload) => { - console.log("Received notification:", payload); receivedPayload = payload; }); @@ -855,7 +781,6 @@ app.get("/test/listen-notify", async (req: Request, res: Response) => { app.get("/test/bytea-data", async (req: Request, res: Response) => { try { - console.log("Testing bytea/binary data handling..."); const connectionString = process.env.DATABASE_URL || `postgres://${process.env.POSTGRES_USER || "testuser"}:${process.env.POSTGRES_PASSWORD || "testpass"}@${process.env.POSTGRES_HOST || "postgres"}:${process.env.POSTGRES_PORT || "5432"}/${process.env.POSTGRES_DB || "testdb"}`; @@ -868,8 +793,6 @@ app.get("/test/bytea-data", async (req: Request, res: Response) => { SELECT ${binaryData}::bytea as binary_col `; - console.log("Bytea result:", result); - await pgClient.end(); res.json({ @@ -885,7 +808,6 @@ app.get("/test/bytea-data", async (req: Request, res: Response) => { app.get("/test/unsafe-cursor", async (req: Request, res: Response) => { try { - console.log("Testing unsafe.cursor() for cursor on unsafe queries..."); const connectionString = process.env.DATABASE_URL || `postgres://${process.env.POSTGRES_USER || "testuser"}:${process.env.POSTGRES_PASSWORD || "testpass"}@${process.env.POSTGRES_HOST || "postgres"}:${process.env.POSTGRES_PORT || "5432"}/${process.env.POSTGRES_DB || "testdb"}`; @@ -897,12 +819,9 @@ app.get("/test/unsafe-cursor", async (req: Request, res: Response) => { const cursor = pgClient.unsafe("SELECT * FROM cache").cursor(2); for await (const rows of cursor) { - console.log("Unsafe cursor batch:", rows); cursorResults.push(...rows); } - console.log("Unsafe cursor complete, total rows:", cursorResults.length); - await pgClient.end(); res.json({ @@ -918,7 +837,6 @@ app.get("/test/unsafe-cursor", async (req: Request, res: Response) => { app.get("/test/unsafe-foreach", async (req: Request, res: Response) => { try { - console.log("Testing unsafe.forEach() for row-by-row processing on unsafe queries..."); const connectionString = process.env.DATABASE_URL || `postgres://${process.env.POSTGRES_USER || "testuser"}:${process.env.POSTGRES_PASSWORD || "testpass"}@${process.env.POSTGRES_HOST || "postgres"}:${process.env.POSTGRES_PORT || "5432"}/${process.env.POSTGRES_DB || "testdb"}`; @@ -928,12 +846,9 @@ app.get("/test/unsafe-foreach", async (req: Request, res: Response) => { // Use forEach on an unsafe query const forEachResults: any[] = []; await pgClient.unsafe("SELECT * FROM cache LIMIT 3").forEach((row) => { - console.log("Unsafe forEach row:", row); forEachResults.push(row); }); - console.log("Unsafe forEach complete, total rows:", forEachResults.length); - await pgClient.end(); res.json({ @@ -949,7 +864,6 @@ app.get("/test/unsafe-foreach", async (req: Request, res: Response) => { app.get("/test/large-object", async (req: Request, res: Response) => { try { - console.log("Testing largeObject() API..."); const connectionString = process.env.DATABASE_URL || `postgres://${process.env.POSTGRES_USER || "testuser"}:${process.env.POSTGRES_PASSWORD || "testpass"}@${process.env.POSTGRES_HOST || "postgres"}:${process.env.POSTGRES_PORT || "5432"}/${process.env.POSTGRES_DB || "testdb"}`; diff --git a/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/src/tdInit.ts b/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/src/tdInit.ts index 09b04a42..9b1acad7 100644 --- a/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/src/tdInit.ts +++ b/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/src/tdInit.ts @@ -3,7 +3,6 @@ import { TuskDrift } from '@use-tusk/drift-node-sdk'; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/src/test_requests.mjs b/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/src/test_requests.mjs new file mode 100644 index 00000000..21b90207 --- /dev/null +++ b/src/instrumentation/libraries/postgres/e2e-tests/cjs-postgres/src/test_requests.mjs @@ -0,0 +1,31 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('GET', '/cache/all'); +await makeRequest('GET', '/cache/sample'); +await makeRequest('GET', '/cache/raw'); +await makeRequest('POST', '/cache/execute-raw'); +await makeRequest('POST', '/cache/insert', { body: { key: 'test_insert', value: 'test_value' } }); +await makeRequest('PUT', '/cache/update', { body: { key: 'test_key_1', value: 'updated_value' } }); +await makeRequest('DELETE', '/cache/delete', { body: { key: 'test_insert' } }); +await makeRequest('GET', '/users/by-email?email=alice@example.com'); +await makeRequest('POST', '/users/insert', { body: { name: 'Test User', email: 'testuser@example.com' } }); +await makeRequest('GET', '/cache/dynamic-fragments'); +await makeRequest('POST', '/cache/update-with-fragments'); +await makeRequest('GET', '/cache/complex-fragments'); +await makeRequest('GET', '/test/execute-method'); +await makeRequest('GET', '/test/sql-file'); +await makeRequest('GET', '/test/pending-query-raw'); +await makeRequest('GET', '/test/sql-reserve'); +await makeRequest('GET', '/test/sql-cursor'); +await makeRequest('GET', '/test/sql-cursor-callback'); +await makeRequest('GET', '/test/sql-foreach'); +await makeRequest('GET', '/test/describe-method'); +await makeRequest('GET', '/test/savepoint'); +await makeRequest('GET', '/test/listen-notify'); +await makeRequest('GET', '/test/bytea-data'); +await makeRequest('GET', '/test/unsafe-cursor'); +await makeRequest('GET', '/test/unsafe-foreach'); +await makeRequest('GET', '/test/large-object'); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/Dockerfile b/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/Dockerfile index 548dc91e..9e886fbd 100644 --- a/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/Dockerfile +++ b/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/Dockerfile @@ -14,5 +14,10 @@ RUN curl -fsSL https://raw.githubusercontent.com/Use-Tusk/tusk-drift-cli/main/in # Expose the server port EXPOSE 3000 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +COPY src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/entrypoint.sh ./ +RUN chmod +x entrypoint.sh +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/docker-compose.yml b/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/docker-compose.yml index 9cb440e8..29dc749a 100644 --- a/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/docker-compose.yml +++ b/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: postgres: image: postgres:13 @@ -20,9 +18,10 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" environment: + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-5} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} - PORT=3000 - POSTGRES_HOST=postgres - POSTGRES_PORT=5432 @@ -33,14 +32,11 @@ services: volumes: # Mount SDK source for hot reload (this is what package.json expects) - ../../../../../..:/sdk:ro - # Mount .tusk folder to persist traces - - ./.tusk:/app/.tusk + # Mount .tusk config to persist configuration + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro # Mount app source for development - ./src:/app/src working_dir: /app depends_on: postgres: condition: service_healthy - # Keep container running without starting the app - # The CLI will control starting/stopping the app - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/entrypoint.sh b/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/entrypoint.sh new file mode 100755 index 00000000..188f3789 --- /dev/null +++ b/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +SERVER_WAIT_TIME=10 +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/run.sh b/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/run.sh index d86af323..7234fbc7 100755 --- a/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/run.sh +++ b/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/run.sh @@ -1,168 +1,51 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -# Generate unique docker compose project name PROJECT_NAME="postgres-esm-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting Postgres (Drizzle + postgres) ESM E2E test run on port ${APP_PORT}..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker containers (postgres + app) -echo "Step 1: Starting docker containers..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for containers to be ready -echo "Waiting for containers to be ready..." -sleep 5 - -# Wait for PostgreSQL to be healthy -echo "Waiting for PostgreSQL to be healthy..." -until docker compose -p $PROJECT_NAME exec -T postgres pg_isready -U testuser -d testdb > /dev/null 2>&1; do - echo " PostgreSQL is not ready yet..." - sleep 2 -done -echo "PostgreSQL is ready!" - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "npm run build && npm run dev" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 10 - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all Postgres + Drizzle endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null - -echo " - GET /cache/all" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/cache/all > /dev/null - -echo " - GET /cache/sample" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/cache/sample > /dev/null - -echo " - GET /cache/raw" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/cache/raw > /dev/null - -echo " - POST /cache/execute-raw" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/cache/execute-raw > /dev/null - -echo " - POST /cache/insert" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"key":"test_insert","value":"test_value"}' http://localhost:3000/cache/insert > /dev/null - -echo " - PUT /cache/update" -docker compose -p $PROJECT_NAME exec -T app curl -s -X PUT -H "Content-Type: application/json" -d '{"key":"test_key_1","value":"updated_value"}' http://localhost:3000/cache/update > /dev/null - -echo " - DELETE /cache/delete" -docker compose -p $PROJECT_NAME exec -T app curl -s -X DELETE -H "Content-Type: application/json" -d '{"key":"test_insert"}' http://localhost:3000/cache/delete > /dev/null - -echo " - GET /users/by-email" -docker compose -p $PROJECT_NAME exec -T app curl -s "http://localhost:3000/users/by-email?email=alice@example.com" > /dev/null - -echo " - POST /users/insert" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"name":"Test User","email":"testuser@example.com"}' http://localhost:3000/users/insert > /dev/null - -echo " - GET /cache/dynamic-fragments" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/cache/dynamic-fragments > /dev/null +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' -echo " - POST /cache/update-with-fragments" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/cache/update-with-fragments > /dev/null - -echo " - GET /cache/complex-fragments" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/cache/complex-fragments > /dev/null - -echo " - GET /test/execute-method" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/execute-method > /dev/null - -echo " - GET /test/sql-file" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/sql-file > /dev/null - -echo " - GET /test/pending-query-raw" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/pending-query-raw > /dev/null - -echo " - GET /test/sql-reserve" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/sql-reserve > /dev/null - -echo " - GET /test/sql-cursor" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/sql-cursor > /dev/null - -echo " - GET /test/sql-cursor-callback" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/sql-cursor-callback > /dev/null - -echo " - GET /test/sql-foreach" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/sql-foreach > /dev/null - -echo " - GET /test/describe-method" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/describe-method > /dev/null - -echo " - GET /test/savepoint" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/savepoint > /dev/null - -echo " - GET /test/listen-notify" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/listen-notify > /dev/null - -echo " - GET /test/bytea-data" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/bytea-data > /dev/null - -echo " - GET /test/unsafe-cursor" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/unsafe-cursor > /dev/null - -echo " - GET /test/unsafe-foreach" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/unsafe-foreach > /dev/null - -echo " - GET /test/large-object" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/large-object > /dev/null - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: POSTGRES (ESM)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" +trap cleanup EXIT -# Step 7.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -# Step 8: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "Postgres (Drizzle + postgres) ESM E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/src/tdInit.ts b/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/src/tdInit.ts index cf11e0f1..d8e4a51f 100644 --- a/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/src/tdInit.ts +++ b/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/src/tdInit.ts @@ -10,7 +10,6 @@ import { TuskDrift } from '@use-tusk/drift-node-sdk'; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/src/test_requests.mjs b/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/src/test_requests.mjs new file mode 100644 index 00000000..21b90207 --- /dev/null +++ b/src/instrumentation/libraries/postgres/e2e-tests/esm-postgres/src/test_requests.mjs @@ -0,0 +1,31 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('GET', '/cache/all'); +await makeRequest('GET', '/cache/sample'); +await makeRequest('GET', '/cache/raw'); +await makeRequest('POST', '/cache/execute-raw'); +await makeRequest('POST', '/cache/insert', { body: { key: 'test_insert', value: 'test_value' } }); +await makeRequest('PUT', '/cache/update', { body: { key: 'test_key_1', value: 'updated_value' } }); +await makeRequest('DELETE', '/cache/delete', { body: { key: 'test_insert' } }); +await makeRequest('GET', '/users/by-email?email=alice@example.com'); +await makeRequest('POST', '/users/insert', { body: { name: 'Test User', email: 'testuser@example.com' } }); +await makeRequest('GET', '/cache/dynamic-fragments'); +await makeRequest('POST', '/cache/update-with-fragments'); +await makeRequest('GET', '/cache/complex-fragments'); +await makeRequest('GET', '/test/execute-method'); +await makeRequest('GET', '/test/sql-file'); +await makeRequest('GET', '/test/pending-query-raw'); +await makeRequest('GET', '/test/sql-reserve'); +await makeRequest('GET', '/test/sql-cursor'); +await makeRequest('GET', '/test/sql-cursor-callback'); +await makeRequest('GET', '/test/sql-foreach'); +await makeRequest('GET', '/test/describe-method'); +await makeRequest('GET', '/test/savepoint'); +await makeRequest('GET', '/test/listen-notify'); +await makeRequest('GET', '/test/bytea-data'); +await makeRequest('GET', '/test/unsafe-cursor'); +await makeRequest('GET', '/test/unsafe-foreach'); +await makeRequest('GET', '/test/large-object'); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/Dockerfile b/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/Dockerfile index 8712eb51..b46fefba 100644 --- a/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/Dockerfile +++ b/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/Dockerfile @@ -23,5 +23,10 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ # Expose the server port EXPOSE 3000 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +COPY src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/entrypoint.sh ./ +RUN chmod +x entrypoint.sh +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/docker-compose.yml b/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/docker-compose.yml index 4e07fa84..71d24dc8 100644 --- a/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/docker-compose.yml +++ b/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/docker-compose.yml @@ -20,16 +20,16 @@ services: environment: DATABASE_URL: "postgresql://testuser:testpass@postgres:5432/testdb?schema=public" PORT: 3000 - ports: - - "${APP_PORT:-3000}:3000" + BENCHMARKS: ${BENCHMARKS:-} + BENCHMARK_DURATION: ${BENCHMARK_DURATION:-30} + BENCHMARK_WARMUP: ${BENCHMARK_WARMUP:-3} volumes: # Mount the SDK source code (read-only) - ../../../../../../:/sdk:ro - # Mount .tusk directory for persistence - - ./.tusk:/app/.tusk + # Mount .tusk config for read-only access + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro # Mount src for development - ./src:/app/src depends_on: postgres: condition: service_healthy - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/entrypoint.sh b/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/entrypoint.sh new file mode 100755 index 00000000..a33b98e6 --- /dev/null +++ b/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/bash +SERVER_WAIT_TIME=10 +setup_library() { + npx prisma generate + npx prisma db push --force-reset --skip-generate +} +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/run.sh b/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/run.sh index ff605c86..52700583 100755 --- a/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/run.sh +++ b/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/run.sh @@ -1,169 +1,51 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -# Generate unique docker compose project name PROJECT_NAME="prisma-cjs-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting Prisma (CommonJS) E2E test run on port ${APP_PORT}..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker containers (postgres + app) -echo "Step 1: Starting docker containers..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for containers to be ready -echo "Waiting for containers to be ready..." -sleep 5 - -# Wait for PostgreSQL to be healthy -echo "Waiting for PostgreSQL to be healthy..." -until docker compose -p $PROJECT_NAME exec -T postgres pg_isready -U testuser -d testdb > /dev/null 2>&1; do - echo " PostgreSQL is not ready yet..." - sleep 2 -done -echo "PostgreSQL is ready!" - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Step 2.5: Generate Prisma Client -echo "Step 2.5: Generating Prisma Client..." -docker compose -p $PROJECT_NAME exec -T app npx prisma generate - -# Step 2.6: Push database schema -echo "Step 2.6: Pushing database schema..." -docker compose -p $PROJECT_NAME exec -T app npx prisma db push --force-reset --skip-generate - -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "npm run build && npm run dev" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 10 - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all Prisma endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null - -echo " - GET /users/all (findMany)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/users/all > /dev/null - -echo " - GET /users/active (findMany with where)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/users/active > /dev/null - -echo " - GET /users/1 (findUnique)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/users/1 > /dev/null - -echo " - GET /users/first-active (findFirst)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/users/first-active > /dev/null - -echo " - GET /users/by-email/alice@example.com (findUniqueOrThrow)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/users/by-email/alice@example.com > /dev/null - -echo " - POST /users/create (create)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"email":"newuser@example.com","name":"New User","age":28}' http://localhost:3000/users/create > /dev/null - -echo " - POST /users/create-many (createMany)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/users/create-many > /dev/null - -echo " - PUT /users/1 (update)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X PUT -H "Content-Type: application/json" -d '{"name":"Updated Alice","age":31}' http://localhost:3000/users/1 > /dev/null +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' -echo " - PUT /users/bulk-deactivate (updateMany)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X PUT http://localhost:3000/users/bulk-deactivate > /dev/null - -echo " - POST /users/upsert (upsert)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"email":"upsert@example.com","name":"Upsert User","age":29}' http://localhost:3000/users/upsert > /dev/null - -echo " - GET /users/count (count)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/users/count > /dev/null - -echo " - GET /orders/aggregate (aggregate)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/orders/aggregate > /dev/null - -echo " - GET /users/1/with-posts (include relations)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/users/1/with-posts > /dev/null - -echo " - GET /posts/published (deep includes)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/posts/published > /dev/null - -echo " - POST /posts/create-with-author (nested writes)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"title":"Nested Post","content":"Test content","authorEmail":"alice@example.com"}' http://localhost:3000/posts/create-with-author > /dev/null - -echo " - POST /transactions/sequential (\$transaction array)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/transactions/sequential > /dev/null - -echo " - POST /transactions/interactive (\$transaction interactive)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/transactions/interactive > /dev/null - -echo " - POST /raw/query (\$queryRaw)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/raw/query > /dev/null - -echo " - POST /raw/execute (\$executeRaw)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/raw/execute > /dev/null - -echo " - POST /errors/unique-violation (error testing)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/errors/unique-violation > /dev/null - -echo " - GET /errors/not-found (error testing)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/errors/not-found > /dev/null - -echo " - POST /errors/validation (error testing)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/errors/validation > /dev/null - -echo " - DELETE /users/inactive (deleteMany)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X DELETE http://localhost:3000/users/inactive > /dev/null - -# Note: We intentionally skip DELETE /users/:id to keep data for replay testing - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: PRISMA (CJS)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" +trap cleanup EXIT -# Step 7.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -# Step 8: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "Prisma (CommonJS) E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/src/tdInit.ts b/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/src/tdInit.ts index bf94fece..b7f7e9a6 100644 --- a/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/src/tdInit.ts +++ b/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/src/tdInit.ts @@ -3,7 +3,6 @@ import { TuskDrift } from "@use-tusk/drift-node-sdk"; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/src/test_requests.mjs b/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/src/test_requests.mjs new file mode 100644 index 00000000..a27ab93d --- /dev/null +++ b/src/instrumentation/libraries/prisma/e2e-tests/cjs-prisma/src/test_requests.mjs @@ -0,0 +1,28 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('GET', '/users/all'); +await makeRequest('GET', '/users/active'); +await makeRequest('GET', '/users/1'); +await makeRequest('GET', '/users/first-active'); +await makeRequest('GET', '/users/by-email/alice@example.com'); +await makeRequest('POST', '/users/create', { body: { email: 'newuser@example.com', name: 'New User', age: 28 } }); +await makeRequest('POST', '/users/create-many'); +await makeRequest('PUT', '/users/1', { body: { name: 'Updated Alice', age: 31 } }); +await makeRequest('PUT', '/users/bulk-deactivate'); +await makeRequest('POST', '/users/upsert', { body: { email: 'upsert@example.com', name: 'Upsert User', age: 29 } }); +await makeRequest('GET', '/users/count'); +await makeRequest('GET', '/orders/aggregate'); +await makeRequest('GET', '/users/1/with-posts'); +await makeRequest('GET', '/posts/published'); +await makeRequest('POST', '/posts/create-with-author', { body: { title: 'Nested Post', content: 'Test content', authorEmail: 'alice@example.com' } }); +await makeRequest('POST', '/transactions/sequential'); +await makeRequest('POST', '/transactions/interactive'); +await makeRequest('POST', '/raw/query'); +await makeRequest('POST', '/raw/execute'); +await makeRequest('POST', '/errors/unique-violation'); +await makeRequest('GET', '/errors/not-found'); +await makeRequest('POST', '/errors/validation'); +await makeRequest('DELETE', '/users/inactive'); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/Dockerfile b/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/Dockerfile index ef44700a..fd134221 100644 --- a/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/Dockerfile +++ b/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/Dockerfile @@ -23,5 +23,10 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ # Expose the server port EXPOSE 3000 -# Container stays running - CLI will control the app -CMD ["tail", "-f", "/dev/null"] +COPY src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/entrypoint.sh ./ +RUN chmod +x entrypoint.sh +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/docker-compose.yml b/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/docker-compose.yml index de421b9d..00d270f6 100644 --- a/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/docker-compose.yml +++ b/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3.8' - services: postgres: image: postgres:13 @@ -25,16 +23,16 @@ services: environment: DATABASE_URL: "postgresql://testuser:testpass@postgres:5432/testdb?schema=public" PORT: 3000 - ports: - - "${APP_PORT:-3000}:3000" + BENCHMARKS: ${BENCHMARKS:-} + BENCHMARK_DURATION: ${BENCHMARK_DURATION:-30} + BENCHMARK_WARMUP: ${BENCHMARK_WARMUP:-3} volumes: # Mount the SDK source code (read-only) - ../../../../../../:/sdk:ro - # Mount .tusk directory for persistence - - ./.tusk:/app/.tusk + # Mount .tusk config for read-only access + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro # Mount src for development - ./src:/app/src depends_on: postgres: condition: service_healthy - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/entrypoint.sh b/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/entrypoint.sh new file mode 100755 index 00000000..a33b98e6 --- /dev/null +++ b/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/bash +SERVER_WAIT_TIME=10 +setup_library() { + npx prisma generate + npx prisma db push --force-reset --skip-generate +} +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/run.sh b/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/run.sh index c37e95e9..8d2170c6 100755 --- a/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/run.sh +++ b/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/run.sh @@ -1,169 +1,51 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -# Generate unique docker compose project name PROJECT_NAME="prisma-esm-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting Prisma (ESM) E2E test run on port ${APP_PORT}..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker containers (postgres + app) -echo "Step 1: Starting docker containers..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull - -# Wait for containers to be ready -echo "Waiting for containers to be ready..." -sleep 5 - -# Wait for PostgreSQL to be healthy -echo "Waiting for PostgreSQL to be healthy..." -until docker compose -p $PROJECT_NAME exec -T postgres pg_isready -U testuser -d testdb > /dev/null 2>&1; do - echo " PostgreSQL is not ready yet..." - sleep 2 -done -echo "PostgreSQL is ready!" - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Step 2.5: Generate Prisma Client -echo "Step 2.5: Generating Prisma Client..." -docker compose -p $PROJECT_NAME exec -T app npx prisma generate - -# Step 2.6: Push database schema -echo "Step 2.6: Pushing database schema..." -docker compose -p $PROJECT_NAME exec -T app npx prisma db push --force-reset --skip-generate - -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "npm run build && npm run dev" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 10 - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all Prisma endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null - -echo " - GET /users/all (findMany)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/users/all > /dev/null - -echo " - GET /users/active (findMany with where)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/users/active > /dev/null - -echo " - GET /users/1 (findUnique)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/users/1 > /dev/null - -echo " - GET /users/first-active (findFirst)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/users/first-active > /dev/null - -echo " - GET /users/by-email/alice@example.com (findUniqueOrThrow)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/users/by-email/alice@example.com > /dev/null - -echo " - POST /users/create (create)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"email":"newuser@example.com","name":"New User","age":28}' http://localhost:3000/users/create > /dev/null - -echo " - POST /users/create-many (createMany)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/users/create-many > /dev/null - -echo " - PUT /users/1 (update)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X PUT -H "Content-Type: application/json" -d '{"name":"Updated Alice","age":31}' http://localhost:3000/users/1 > /dev/null +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' -echo " - PUT /users/bulk-deactivate (updateMany)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X PUT http://localhost:3000/users/bulk-deactivate > /dev/null - -echo " - POST /users/upsert (upsert)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"email":"upsert@example.com","name":"Upsert User","age":29}' http://localhost:3000/users/upsert > /dev/null - -echo " - GET /users/count (count)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/users/count > /dev/null - -echo " - GET /orders/aggregate (aggregate)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/orders/aggregate > /dev/null - -echo " - GET /users/1/with-posts (include relations)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/users/1/with-posts > /dev/null - -echo " - GET /posts/published (deep includes)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/posts/published > /dev/null - -echo " - POST /posts/create-with-author (nested writes)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST -H "Content-Type: application/json" -d '{"title":"Nested Post","content":"Test content","authorEmail":"alice@example.com"}' http://localhost:3000/posts/create-with-author > /dev/null - -echo " - POST /transactions/sequential (\$transaction array)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/transactions/sequential > /dev/null - -echo " - POST /transactions/interactive (\$transaction interactive)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/transactions/interactive > /dev/null - -echo " - POST /raw/query (\$queryRaw)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/raw/query > /dev/null - -echo " - POST /raw/execute (\$executeRaw)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/raw/execute > /dev/null - -echo " - POST /errors/unique-violation (error testing)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/errors/unique-violation > /dev/null - -echo " - GET /errors/not-found (error testing)" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/errors/not-found > /dev/null - -echo " - POST /errors/validation (error testing)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/errors/validation > /dev/null - -echo " - DELETE /users/inactive (deleteMany)" -docker compose -p $PROJECT_NAME exec -T app curl -s -X DELETE http://localhost:3000/users/inactive > /dev/null - -# Note: We intentionally skip DELETE /users/:id to keep data for replay testing - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: PRISMA (ESM)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" +trap cleanup EXIT -# Step 7.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -# Step 8: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "Prisma (ESM) E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/src/tdInit.ts b/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/src/tdInit.ts index 8582ae2e..c0c40e35 100644 --- a/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/src/tdInit.ts +++ b/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/src/tdInit.ts @@ -11,7 +11,6 @@ import { TuskDrift } from '@use-tusk/drift-node-sdk'; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/src/test_requests.mjs b/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/src/test_requests.mjs new file mode 100644 index 00000000..a27ab93d --- /dev/null +++ b/src/instrumentation/libraries/prisma/e2e-tests/esm-prisma/src/test_requests.mjs @@ -0,0 +1,28 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('GET', '/users/all'); +await makeRequest('GET', '/users/active'); +await makeRequest('GET', '/users/1'); +await makeRequest('GET', '/users/first-active'); +await makeRequest('GET', '/users/by-email/alice@example.com'); +await makeRequest('POST', '/users/create', { body: { email: 'newuser@example.com', name: 'New User', age: 28 } }); +await makeRequest('POST', '/users/create-many'); +await makeRequest('PUT', '/users/1', { body: { name: 'Updated Alice', age: 31 } }); +await makeRequest('PUT', '/users/bulk-deactivate'); +await makeRequest('POST', '/users/upsert', { body: { email: 'upsert@example.com', name: 'Upsert User', age: 29 } }); +await makeRequest('GET', '/users/count'); +await makeRequest('GET', '/orders/aggregate'); +await makeRequest('GET', '/users/1/with-posts'); +await makeRequest('GET', '/posts/published'); +await makeRequest('POST', '/posts/create-with-author', { body: { title: 'Nested Post', content: 'Test content', authorEmail: 'alice@example.com' } }); +await makeRequest('POST', '/transactions/sequential'); +await makeRequest('POST', '/transactions/interactive'); +await makeRequest('POST', '/raw/query'); +await makeRequest('POST', '/raw/execute'); +await makeRequest('POST', '/errors/unique-violation'); +await makeRequest('GET', '/errors/not-found'); +await makeRequest('POST', '/errors/validation'); +await makeRequest('DELETE', '/users/inactive'); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/Dockerfile b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/Dockerfile index fc1c3524..9de940b7 100644 --- a/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/Dockerfile +++ b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/Dockerfile @@ -15,4 +15,11 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ fi EXPOSE 3000 -CMD ["tail", "-f", "/dev/null"] + +COPY src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/entrypoint.sh ./ +RUN chmod +x entrypoint.sh +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/docker-compose.yml b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/docker-compose.yml index 6315fa7d..275b1ba2 100644 --- a/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/docker-compose.yml +++ b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: app: build: @@ -8,16 +6,16 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" environment: - PORT=3000 - UPSTASH_REDIS_REST_URL=${UPSTASH_REDIS_REST_URL} - UPSTASH_REDIS_REST_TOKEN=${UPSTASH_REDIS_REST_TOKEN} - TUSK_ANALYTICS_DISABLED=1 + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-30} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} volumes: - ../../../../../..:/sdk:ro - - ./.tusk:/app/.tusk + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro - ./src:/app/src working_dir: /app - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/entrypoint.sh b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/entrypoint.sh new file mode 100755 index 00000000..ed1dce09 --- /dev/null +++ b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +SERVER_WAIT_TIME=8 +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/run.sh b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/run.sh index c893f420..1d894f03 100755 --- a/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/run.sh +++ b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/run.sh @@ -1,412 +1,51 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -# Generate unique docker compose project name PROJECT_NAME="upstash-redis-js-cjs-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting upstash-redis-js E2E test run on port ${APP_PORT}..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker containers (app only - no Redis needed for Upstash) -echo "Step 1: Starting docker containers..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' -# Wait for containers to be ready -echo "Waiting for containers to be ready..." -sleep 5 - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Check app directory structure -echo "Checking app directory structure..." -echo "Contents of /app:" -docker compose -p $PROJECT_NAME exec -T app ls -la /app -echo "" -echo "Contents of /app/src:" -docker compose -p $PROJECT_NAME exec -T app ls -la /app/src || echo " (src directory not found)" -echo "" -echo "Package.json scripts:" -docker compose -p $PROJECT_NAME exec -T app cat /app/package.json | grep -A 10 '"scripts"' || echo " (could not read package.json)" +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: UPSTASH-REDIS-JS (CJS)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" echo "" -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "cd /app && npm run build >> /tmp/server.log 2>&1 && npm run dev >> /tmp/server.log 2>&1" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 8 - -# Show initial server output -echo "Initial server output:" -docker compose -p $PROJECT_NAME exec -T app cat /tmp/server.log 2>/dev/null || echo " (no output yet)" - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all upstash-redis-js endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null - -# String operations -echo " - POST /test/string/set" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/set > /dev/null - -echo " - GET /test/string/get" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/string/get > /dev/null - -echo " - POST /test/string/mset" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/mset > /dev/null - -echo " - GET /test/string/mget" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/string/mget > /dev/null - -echo " - POST /test/string/setex" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/setex > /dev/null - -echo " - POST /test/string/setnx" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/setnx > /dev/null - -echo " - POST /test/string/getdel" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/getdel > /dev/null - -echo " - POST /test/string/append" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/append > /dev/null - -echo " - POST /test/string/incr" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/incr > /dev/null - -echo " - POST /test/string/incrby" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/incrby > /dev/null - -echo " - POST /test/string/incrbyfloat" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/incrbyfloat > /dev/null - -echo " - POST /test/string/decr" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/decr > /dev/null - -echo " - POST /test/string/decrby" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/decrby > /dev/null - -echo " - GET /test/string/strlen" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/string/strlen > /dev/null - -echo " - GET /test/string/getrange" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/string/getrange > /dev/null - -echo " - POST /test/string/setrange" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/setrange > /dev/null - -# Hash operations -echo " - POST /test/hash/hset" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/hash/hset > /dev/null - -echo " - GET /test/hash/hget" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/hash/hget > /dev/null - -echo " - GET /test/hash/hgetall" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/hash/hgetall > /dev/null - -echo " - POST /test/hash/hmset" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/hash/hmset > /dev/null - -echo " - GET /test/hash/hmget" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/hash/hmget > /dev/null - -echo " - POST /test/hash/hdel" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/hash/hdel > /dev/null - -echo " - GET /test/hash/hexists" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/hash/hexists > /dev/null - -echo " - GET /test/hash/hkeys" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/hash/hkeys > /dev/null - -echo " - GET /test/hash/hvals" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/hash/hvals > /dev/null - -echo " - GET /test/hash/hlen" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/hash/hlen > /dev/null - -echo " - POST /test/hash/hincrby" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/hash/hincrby > /dev/null - -echo " - POST /test/hash/hincrbyfloat" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/hash/hincrbyfloat > /dev/null - -echo " - POST /test/hash/hsetnx" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/hash/hsetnx > /dev/null - -# List operations -echo " - POST /test/list/lpush" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/lpush > /dev/null - -echo " - POST /test/list/rpush" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/rpush > /dev/null - -echo " - GET /test/list/lrange" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/list/lrange > /dev/null - -echo " - POST /test/list/lpop" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/lpop > /dev/null - -echo " - POST /test/list/rpop" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/rpop > /dev/null - -echo " - GET /test/list/llen" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/list/llen > /dev/null - -echo " - GET /test/list/lindex" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/list/lindex > /dev/null - -echo " - POST /test/list/lset" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/lset > /dev/null - -echo " - POST /test/list/linsert" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/linsert > /dev/null - -echo " - POST /test/list/lrem" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/lrem > /dev/null - -echo " - POST /test/list/ltrim" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/ltrim > /dev/null - -echo " - POST /test/list/rpoplpush" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/rpoplpush > /dev/null - -echo " - POST /test/list/lpos" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/lpos > /dev/null - -echo " - POST /test/list/lmove" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/lmove > /dev/null - -# Set operations -echo " - POST /test/set/sadd" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/set/sadd > /dev/null - -echo " - GET /test/set/smembers" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/set/smembers > /dev/null - -echo " - GET /test/set/sismember" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/set/sismember > /dev/null - -echo " - POST /test/set/srem" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/set/srem > /dev/null - -echo " - GET /test/set/scard" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/set/scard > /dev/null +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -echo " - POST /test/set/spop" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/set/spop > /dev/null +trap cleanup EXIT -echo " - GET /test/set/srandmember" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/set/srandmember > /dev/null +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -echo " - POST /test/set/sdiff" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/set/sdiff > /dev/null - -echo " - POST /test/set/sinter" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/set/sinter > /dev/null - -echo " - POST /test/set/sunion" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/set/sunion > /dev/null - -echo " - POST /test/set/smove" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/set/smove > /dev/null - -# Sorted Set operations -echo " - POST /test/zset/zadd" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/zset/zadd > /dev/null - -echo " - GET /test/zset/zrange" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zset/zrange > /dev/null - -echo " - GET /test/zset/zrange-withscores" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zset/zrange-withscores > /dev/null - -echo " - GET /test/zset/zrevrange" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zset/zrevrange > /dev/null - -echo " - GET /test/zset/zscore" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zset/zscore > /dev/null - -echo " - POST /test/zset/zincrby" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/zset/zincrby > /dev/null - -echo " - GET /test/zset/zcard" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zset/zcard > /dev/null - -echo " - GET /test/zset/zcount" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zset/zcount > /dev/null - -echo " - GET /test/zset/zrank" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zset/zrank > /dev/null - -echo " - GET /test/zset/zrevrank" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zset/zrevrank > /dev/null - -echo " - POST /test/zset/zrem" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/zset/zrem > /dev/null - -echo " - POST /test/zset/zpopmin" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/zset/zpopmin > /dev/null - -echo " - POST /test/zset/zpopmax" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/zset/zpopmax > /dev/null - -echo " - GET /test/zset/zrangebyscore" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zset/zrangebyscore > /dev/null - -echo " - GET /test/zset/zrevrangebyscore" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zset/zrevrangebyscore > /dev/null - -echo " - POST /test/zset/zremrangebyrank" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/zset/zremrangebyrank > /dev/null - -echo " - POST /test/zset/zremrangebyscore" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/zset/zremrangebyscore > /dev/null - -# Key operations -echo " - POST /test/key/del" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/key/del > /dev/null - -echo " - GET /test/key/exists" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/key/exists > /dev/null - -echo " - POST /test/key/expire" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/key/expire > /dev/null - -echo " - POST /test/key/expireat" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/key/expireat > /dev/null - -echo " - GET /test/key/ttl" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/key/ttl > /dev/null - -echo " - GET /test/key/pttl" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/key/pttl > /dev/null - -echo " - POST /test/key/persist" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/key/persist > /dev/null - -echo " - GET /test/key/keys" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/key/keys > /dev/null - -echo " - GET /test/key/randomkey" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/key/randomkey > /dev/null - -echo " - POST /test/key/rename" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/key/rename > /dev/null - -echo " - POST /test/key/renamenx" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/key/renamenx > /dev/null - -echo " - GET /test/key/type" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/key/type > /dev/null - -echo " - POST /test/key/touch" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/key/touch > /dev/null - -echo " - POST /test/key/unlink" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/key/unlink > /dev/null - -# Bitmap operations -echo " - POST /test/bitmap/setbit" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/bitmap/setbit > /dev/null - -echo " - GET /test/bitmap/getbit" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/bitmap/getbit > /dev/null - -echo " - GET /test/bitmap/bitcount" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/bitmap/bitcount > /dev/null - -echo " - GET /test/bitmap/bitpos" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/bitmap/bitpos > /dev/null - -echo " - POST /test/bitmap/bitop" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/bitmap/bitop > /dev/null - -# Server operations -echo " - GET /test/server/ping" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/server/ping > /dev/null - -echo " - GET /test/server/dbsize" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/server/dbsize > /dev/null - -echo " - POST /test/server/echo" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/server/echo > /dev/null - -# HyperLogLog operations -echo " - POST /test/hll/pfadd" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/hll/pfadd > /dev/null - -echo " - GET /test/hll/pfcount" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/hll/pfcount > /dev/null - -echo " - POST /test/hll/pfmerge" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/hll/pfmerge > /dev/null - -# Geo operations -echo " - POST /test/geo/geoadd" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/geo/geoadd > /dev/null - -echo " - GET /test/geo/geopos" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/geo/geopos > /dev/null - -echo " - POST /test/geo/geodist" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/geo/geodist > /dev/null - -echo " - GET /test/geo/geohash" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/geo/geohash > /dev/null - -# Cleanup - delete all test keys to save space -echo " - GET /cleanup" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/cleanup > /dev/null - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 - -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) - -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" - -# Step 7.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" - -# Step 8: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "upstash-redis-js E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/src/tdInit.ts b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/src/tdInit.ts index 09b04a42..9b1acad7 100644 --- a/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/src/tdInit.ts +++ b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/src/tdInit.ts @@ -3,7 +3,6 @@ import { TuskDrift } from '@use-tusk/drift-node-sdk'; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/src/test_requests.mjs b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/src/test_requests.mjs new file mode 100644 index 00000000..846c017b --- /dev/null +++ b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/cjs-upstash-redis-js/src/test_requests.mjs @@ -0,0 +1,106 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('POST', '/test/string/set'); +await makeRequest('GET', '/test/string/get'); +await makeRequest('POST', '/test/string/mset'); +await makeRequest('GET', '/test/string/mget'); +await makeRequest('POST', '/test/string/setex'); +await makeRequest('POST', '/test/string/setnx'); +await makeRequest('POST', '/test/string/getdel'); +await makeRequest('POST', '/test/string/append'); +await makeRequest('POST', '/test/string/incr'); +await makeRequest('POST', '/test/string/incrby'); +await makeRequest('POST', '/test/string/incrbyfloat'); +await makeRequest('POST', '/test/string/decr'); +await makeRequest('POST', '/test/string/decrby'); +await makeRequest('GET', '/test/string/strlen'); +await makeRequest('GET', '/test/string/getrange'); +await makeRequest('POST', '/test/string/setrange'); +await makeRequest('POST', '/test/hash/hset'); +await makeRequest('GET', '/test/hash/hget'); +await makeRequest('GET', '/test/hash/hgetall'); +await makeRequest('POST', '/test/hash/hmset'); +await makeRequest('GET', '/test/hash/hmget'); +await makeRequest('POST', '/test/hash/hdel'); +await makeRequest('GET', '/test/hash/hexists'); +await makeRequest('GET', '/test/hash/hkeys'); +await makeRequest('GET', '/test/hash/hvals'); +await makeRequest('GET', '/test/hash/hlen'); +await makeRequest('POST', '/test/hash/hincrby'); +await makeRequest('POST', '/test/hash/hincrbyfloat'); +await makeRequest('POST', '/test/hash/hsetnx'); +await makeRequest('POST', '/test/list/lpush'); +await makeRequest('POST', '/test/list/rpush'); +await makeRequest('GET', '/test/list/lrange'); +await makeRequest('POST', '/test/list/lpop'); +await makeRequest('POST', '/test/list/rpop'); +await makeRequest('GET', '/test/list/llen'); +await makeRequest('GET', '/test/list/lindex'); +await makeRequest('POST', '/test/list/lset'); +await makeRequest('POST', '/test/list/linsert'); +await makeRequest('POST', '/test/list/lrem'); +await makeRequest('POST', '/test/list/ltrim'); +await makeRequest('POST', '/test/list/rpoplpush'); +await makeRequest('POST', '/test/list/lpos'); +await makeRequest('POST', '/test/list/lmove'); +await makeRequest('POST', '/test/set/sadd'); +await makeRequest('GET', '/test/set/smembers'); +await makeRequest('GET', '/test/set/sismember'); +await makeRequest('POST', '/test/set/srem'); +await makeRequest('GET', '/test/set/scard'); +await makeRequest('POST', '/test/set/spop'); +await makeRequest('GET', '/test/set/srandmember'); +await makeRequest('POST', '/test/set/sdiff'); +await makeRequest('POST', '/test/set/sinter'); +await makeRequest('POST', '/test/set/sunion'); +await makeRequest('POST', '/test/set/smove'); +await makeRequest('POST', '/test/zset/zadd'); +await makeRequest('GET', '/test/zset/zrange'); +await makeRequest('GET', '/test/zset/zrange-withscores'); +await makeRequest('GET', '/test/zset/zrevrange'); +await makeRequest('GET', '/test/zset/zscore'); +await makeRequest('POST', '/test/zset/zincrby'); +await makeRequest('GET', '/test/zset/zcard'); +await makeRequest('GET', '/test/zset/zcount'); +await makeRequest('GET', '/test/zset/zrank'); +await makeRequest('GET', '/test/zset/zrevrank'); +await makeRequest('POST', '/test/zset/zrem'); +await makeRequest('POST', '/test/zset/zpopmin'); +await makeRequest('POST', '/test/zset/zpopmax'); +await makeRequest('GET', '/test/zset/zrangebyscore'); +await makeRequest('GET', '/test/zset/zrevrangebyscore'); +await makeRequest('POST', '/test/zset/zremrangebyrank'); +await makeRequest('POST', '/test/zset/zremrangebyscore'); +await makeRequest('POST', '/test/key/del'); +await makeRequest('GET', '/test/key/exists'); +await makeRequest('POST', '/test/key/expire'); +await makeRequest('POST', '/test/key/expireat'); +await makeRequest('GET', '/test/key/ttl'); +await makeRequest('GET', '/test/key/pttl'); +await makeRequest('POST', '/test/key/persist'); +await makeRequest('GET', '/test/key/keys'); +await makeRequest('GET', '/test/key/randomkey'); +await makeRequest('POST', '/test/key/rename'); +await makeRequest('POST', '/test/key/renamenx'); +await makeRequest('GET', '/test/key/type'); +await makeRequest('POST', '/test/key/touch'); +await makeRequest('POST', '/test/key/unlink'); +await makeRequest('POST', '/test/bitmap/setbit'); +await makeRequest('GET', '/test/bitmap/getbit'); +await makeRequest('GET', '/test/bitmap/bitcount'); +await makeRequest('GET', '/test/bitmap/bitpos'); +await makeRequest('POST', '/test/bitmap/bitop'); +await makeRequest('GET', '/test/server/ping'); +await makeRequest('GET', '/test/server/dbsize'); +await makeRequest('POST', '/test/server/echo'); +await makeRequest('POST', '/test/hll/pfadd'); +await makeRequest('GET', '/test/hll/pfcount'); +await makeRequest('POST', '/test/hll/pfmerge'); +await makeRequest('POST', '/test/geo/geoadd'); +await makeRequest('GET', '/test/geo/geopos'); +await makeRequest('POST', '/test/geo/geodist'); +await makeRequest('GET', '/test/geo/geohash'); +await makeRequest('GET', '/cleanup'); + +printRequestSummary(); diff --git a/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/Dockerfile b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/Dockerfile index 7cf1ca14..2cdb8cb0 100644 --- a/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/Dockerfile +++ b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/Dockerfile @@ -15,4 +15,11 @@ RUN if [ "$TUSK_CLI_VERSION" = "latest" ]; then \ fi EXPOSE 3000 -CMD ["tail", "-f", "/dev/null"] + +COPY src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/entrypoint.sh ./ +RUN chmod +x entrypoint.sh +RUN mkdir -p /app/.tusk/traces /app/.tusk/logs +COPY src/instrumentation/libraries/e2e-common/base-entrypoint.sh /app/base-entrypoint.sh +COPY src/instrumentation/libraries/e2e-common/test-utils.mjs /app/test-utils.mjs +RUN chmod +x /app/base-entrypoint.sh +ENTRYPOINT ["./entrypoint.sh"] diff --git a/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/docker-compose.yml b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/docker-compose.yml index 1bf3dde3..26a73103 100644 --- a/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/docker-compose.yml +++ b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: app: build: @@ -8,16 +6,16 @@ services: args: - CACHEBUST=${CACHEBUST:-1} - TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest} - ports: - - "${APP_PORT:-3000}:3000" environment: - PORT=3000 - UPSTASH_REDIS_REST_URL=${UPSTASH_REDIS_REST_URL} - UPSTASH_REDIS_REST_TOKEN=${UPSTASH_REDIS_REST_TOKEN} - TUSK_ANALYTICS_DISABLED=1 + - BENCHMARKS=${BENCHMARKS:-} + - BENCHMARK_DURATION=${BENCHMARK_DURATION:-30} + - BENCHMARK_WARMUP=${BENCHMARK_WARMUP:-3} volumes: - ../../../../../..:/sdk:ro - - ./.tusk:/app/.tusk + - ./.tusk/config.yaml:/app/.tusk/config.yaml:ro - ./src:/app/src working_dir: /app - command: tail -f /dev/null diff --git a/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/entrypoint.sh b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/entrypoint.sh new file mode 100755 index 00000000..ed1dce09 --- /dev/null +++ b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +SERVER_WAIT_TIME=8 +source /app/base-entrypoint.sh diff --git a/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/run.sh b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/run.sh index e72bc381..b6939298 100755 --- a/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/run.sh +++ b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/run.sh @@ -1,412 +1,51 @@ #!/bin/bash - -# Exit on error set -e -# Accept optional port parameter (default: 3000) APP_PORT=${1:-3000} export APP_PORT -# Generate unique docker compose project name PROJECT_NAME="upstash-redis-js-esm-${APP_PORT}" -# Source common E2E helpers -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "$SCRIPT_DIR/../../../e2e-common/e2e-helpers.sh" - -echo "Starting upstash-redis-js ESM E2E test run on port ${APP_PORT}..." - -# Step 0: Clean up traces and logs -echo "Step 0: Cleaning up traces and logs..." -cleanup_tusk_files - -# Step 1: Start docker containers (app only - no Redis needed for Upstash) -echo "Step 1: Starting docker containers..." -docker compose -p $PROJECT_NAME build --no-cache -docker compose -p $PROJECT_NAME up -d --quiet-pull +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' -# Wait for containers to be ready -echo "Waiting for containers to be ready..." -sleep 5 - -# Step 2: Install dependencies (now that /sdk volume is mounted) -echo "Step 2: Installing dependencies..." -docker compose -p $PROJECT_NAME exec -T app npm install - -# Check app directory structure -echo "Checking app directory structure..." -echo "Contents of /app:" -docker compose -p $PROJECT_NAME exec -T app ls -la /app -echo "" -echo "Contents of /app/src:" -docker compose -p $PROJECT_NAME exec -T app ls -la /app/src || echo " (src directory not found)" -echo "" -echo "Package.json scripts:" -docker compose -p $PROJECT_NAME exec -T app cat /app/package.json | grep -A 10 '"scripts"' || echo " (could not read package.json)" +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE}Running Node E2E Test: UPSTASH-REDIS-JS (ESM)${NC}" +echo -e "${BLUE}Port: ${APP_PORT}${NC}" +echo -e "${BLUE}========================================${NC}" echo "" -# Step 3: Start server in RECORD mode -echo "Step 3: Starting server in RECORD mode..." -docker compose -p $PROJECT_NAME exec -d -T -e TUSK_DRIFT_MODE=RECORD app sh -c "cd /app && npm run build >> /tmp/server.log 2>&1 && npm run dev >> /tmp/server.log 2>&1" - -# Wait for server to start -echo "Waiting for server to start..." -sleep 8 - -# Show initial server output -echo "Initial server output:" -docker compose -p $PROJECT_NAME exec -T app cat /tmp/server.log 2>/dev/null || echo " (no output yet)" - -# Step 4: Hit all endpoints -echo "Step 4: Hitting all upstash-redis-js endpoints..." - -echo " - GET /health" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/health > /dev/null - -# String operations -echo " - POST /test/string/set" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/set > /dev/null - -echo " - GET /test/string/get" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/string/get > /dev/null - -echo " - POST /test/string/mset" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/mset > /dev/null - -echo " - GET /test/string/mget" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/string/mget > /dev/null - -echo " - POST /test/string/setex" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/setex > /dev/null - -echo " - POST /test/string/setnx" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/setnx > /dev/null - -echo " - POST /test/string/getdel" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/getdel > /dev/null - -echo " - POST /test/string/append" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/append > /dev/null - -echo " - POST /test/string/incr" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/incr > /dev/null - -echo " - POST /test/string/incrby" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/incrby > /dev/null - -echo " - POST /test/string/incrbyfloat" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/incrbyfloat > /dev/null - -echo " - POST /test/string/decr" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/decr > /dev/null - -echo " - POST /test/string/decrby" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/decrby > /dev/null - -echo " - GET /test/string/strlen" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/string/strlen > /dev/null - -echo " - GET /test/string/getrange" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/string/getrange > /dev/null - -echo " - POST /test/string/setrange" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/string/setrange > /dev/null - -# Hash operations -echo " - POST /test/hash/hset" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/hash/hset > /dev/null - -echo " - GET /test/hash/hget" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/hash/hget > /dev/null - -echo " - GET /test/hash/hgetall" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/hash/hgetall > /dev/null - -echo " - POST /test/hash/hmset" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/hash/hmset > /dev/null - -echo " - GET /test/hash/hmget" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/hash/hmget > /dev/null - -echo " - POST /test/hash/hdel" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/hash/hdel > /dev/null - -echo " - GET /test/hash/hexists" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/hash/hexists > /dev/null - -echo " - GET /test/hash/hkeys" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/hash/hkeys > /dev/null - -echo " - GET /test/hash/hvals" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/hash/hvals > /dev/null - -echo " - GET /test/hash/hlen" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/hash/hlen > /dev/null - -echo " - POST /test/hash/hincrby" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/hash/hincrby > /dev/null - -echo " - POST /test/hash/hincrbyfloat" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/hash/hincrbyfloat > /dev/null - -echo " - POST /test/hash/hsetnx" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/hash/hsetnx > /dev/null - -# List operations -echo " - POST /test/list/lpush" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/lpush > /dev/null - -echo " - POST /test/list/rpush" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/rpush > /dev/null - -echo " - GET /test/list/lrange" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/list/lrange > /dev/null - -echo " - POST /test/list/lpop" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/lpop > /dev/null - -echo " - POST /test/list/rpop" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/rpop > /dev/null - -echo " - GET /test/list/llen" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/list/llen > /dev/null - -echo " - GET /test/list/lindex" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/list/lindex > /dev/null - -echo " - POST /test/list/lset" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/lset > /dev/null - -echo " - POST /test/list/linsert" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/linsert > /dev/null - -echo " - POST /test/list/lrem" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/lrem > /dev/null - -echo " - POST /test/list/ltrim" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/ltrim > /dev/null - -echo " - POST /test/list/rpoplpush" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/rpoplpush > /dev/null - -echo " - POST /test/list/lpos" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/lpos > /dev/null - -echo " - POST /test/list/lmove" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/list/lmove > /dev/null - -# Set operations -echo " - POST /test/set/sadd" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/set/sadd > /dev/null - -echo " - GET /test/set/smembers" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/set/smembers > /dev/null - -echo " - GET /test/set/sismember" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/set/sismember > /dev/null - -echo " - POST /test/set/srem" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/set/srem > /dev/null - -echo " - GET /test/set/scard" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/set/scard > /dev/null +cleanup() { + echo "" + echo -e "${YELLOW}Cleaning up containers...${NC}" + docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true +} -echo " - POST /test/set/spop" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/set/spop > /dev/null +trap cleanup EXIT -echo " - GET /test/set/srandmember" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/set/srandmember > /dev/null +echo -e "${BLUE}Building containers...${NC}" +docker compose -p "$PROJECT_NAME" build --no-cache -echo " - POST /test/set/sdiff" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/set/sdiff > /dev/null - -echo " - POST /test/set/sinter" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/set/sinter > /dev/null - -echo " - POST /test/set/sunion" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/set/sunion > /dev/null - -echo " - POST /test/set/smove" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/set/smove > /dev/null - -# Sorted Set operations -echo " - POST /test/zset/zadd" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/zset/zadd > /dev/null - -echo " - GET /test/zset/zrange" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zset/zrange > /dev/null - -echo " - GET /test/zset/zrange-withscores" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zset/zrange-withscores > /dev/null - -echo " - GET /test/zset/zrevrange" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zset/zrevrange > /dev/null - -echo " - GET /test/zset/zscore" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zset/zscore > /dev/null - -echo " - POST /test/zset/zincrby" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/zset/zincrby > /dev/null - -echo " - GET /test/zset/zcard" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zset/zcard > /dev/null - -echo " - GET /test/zset/zcount" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zset/zcount > /dev/null - -echo " - GET /test/zset/zrank" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zset/zrank > /dev/null - -echo " - GET /test/zset/zrevrank" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zset/zrevrank > /dev/null - -echo " - POST /test/zset/zrem" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/zset/zrem > /dev/null - -echo " - POST /test/zset/zpopmin" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/zset/zpopmin > /dev/null - -echo " - POST /test/zset/zpopmax" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/zset/zpopmax > /dev/null - -echo " - GET /test/zset/zrangebyscore" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zset/zrangebyscore > /dev/null - -echo " - GET /test/zset/zrevrangebyscore" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/zset/zrevrangebyscore > /dev/null - -echo " - POST /test/zset/zremrangebyrank" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/zset/zremrangebyrank > /dev/null - -echo " - POST /test/zset/zremrangebyscore" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/zset/zremrangebyscore > /dev/null - -# Key operations -echo " - POST /test/key/del" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/key/del > /dev/null - -echo " - GET /test/key/exists" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/key/exists > /dev/null - -echo " - POST /test/key/expire" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/key/expire > /dev/null - -echo " - POST /test/key/expireat" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/key/expireat > /dev/null - -echo " - GET /test/key/ttl" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/key/ttl > /dev/null - -echo " - GET /test/key/pttl" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/key/pttl > /dev/null - -echo " - POST /test/key/persist" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/key/persist > /dev/null - -echo " - GET /test/key/keys" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/key/keys > /dev/null - -echo " - GET /test/key/randomkey" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/key/randomkey > /dev/null - -echo " - POST /test/key/rename" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/key/rename > /dev/null - -echo " - POST /test/key/renamenx" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/key/renamenx > /dev/null - -echo " - GET /test/key/type" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/key/type > /dev/null - -echo " - POST /test/key/touch" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/key/touch > /dev/null - -echo " - POST /test/key/unlink" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/key/unlink > /dev/null - -# Bitmap operations -echo " - POST /test/bitmap/setbit" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/bitmap/setbit > /dev/null - -echo " - GET /test/bitmap/getbit" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/bitmap/getbit > /dev/null - -echo " - GET /test/bitmap/bitcount" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/bitmap/bitcount > /dev/null - -echo " - GET /test/bitmap/bitpos" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/bitmap/bitpos > /dev/null - -echo " - POST /test/bitmap/bitop" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/bitmap/bitop > /dev/null - -# Server operations -echo " - GET /test/server/ping" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/server/ping > /dev/null - -echo " - GET /test/server/dbsize" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/server/dbsize > /dev/null - -echo " - POST /test/server/echo" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/server/echo > /dev/null - -# HyperLogLog operations -echo " - POST /test/hll/pfadd" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/hll/pfadd > /dev/null - -echo " - GET /test/hll/pfcount" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/hll/pfcount > /dev/null - -echo " - POST /test/hll/pfmerge" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/hll/pfmerge > /dev/null - -# Geo operations -echo " - POST /test/geo/geoadd" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/geo/geoadd > /dev/null - -echo " - GET /test/geo/geopos" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/geo/geopos > /dev/null - -echo " - POST /test/geo/geodist" -docker compose -p $PROJECT_NAME exec -T app curl -s -X POST http://localhost:3000/test/geo/geodist > /dev/null - -echo " - GET /test/geo/geohash" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/test/geo/geohash > /dev/null - -# Cleanup - delete all test keys to save space -echo " - GET /cleanup" -docker compose -p $PROJECT_NAME exec -T app curl -s http://localhost:3000/cleanup > /dev/null - -echo "All endpoints hit successfully." - -# Step 5: Wait before stopping server -echo "Step 5: Waiting 3 seconds before stopping server..." -sleep 3 - -# Stop the server process -echo "Stopping server..." -docker compose -p $PROJECT_NAME exec -T app pkill -f "node" || true -sleep 2 - -# Step 6: Run tests using tusk CLI -echo "Step 6: Running tests using tusk CLI..." -TEST_RESULTS=$(docker compose -p $PROJECT_NAME exec -T -e TUSK_ANALYTICS_DISABLED=1 app tusk run --print --output-format "json" --enable-service-logs) - -# Step 7: Log test results -parse_and_display_test_results "$TEST_RESULTS" - -# Step 7.5: Check for TCP instrumentation warning in logs -check_tcp_instrumentation_warning "$PROJECT_NAME" - -# Step 8: Clean up +echo -e "${BLUE}Starting test...${NC}" echo "" -echo "Step 8: Cleaning up docker containers..." -docker compose -p $PROJECT_NAME down -# Step 9: Clean up traces and logs -echo "Step 9: Cleaning up traces and logs..." -cleanup_tusk_files +set +e +docker compose -p "$PROJECT_NAME" run --rm app +EXIT_CODE=$? +set -e -echo "upstash-redis-js ESM E2E test run complete." +echo "" +if [ $EXIT_CODE -eq 0 ]; then + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}Test passed!${NC}" + echo -e "${GREEN}========================================${NC}" +else + echo -e "${RED}========================================${NC}" + echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}" + echo -e "${RED}========================================${NC}" +fi exit $EXIT_CODE diff --git a/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/src/tdInit.ts b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/src/tdInit.ts index cf11e0f1..d8e4a51f 100644 --- a/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/src/tdInit.ts +++ b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/src/tdInit.ts @@ -10,7 +10,6 @@ import { TuskDrift } from '@use-tusk/drift-node-sdk'; TuskDrift.initialize({ apiKey: "api-key", env: "dev", - logLevel: "debug", }); export { TuskDrift }; diff --git a/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/src/test_requests.mjs b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/src/test_requests.mjs new file mode 100644 index 00000000..846c017b --- /dev/null +++ b/src/instrumentation/libraries/upstash-redis-js/e2e-tests/esm-upstash-redis-js/src/test_requests.mjs @@ -0,0 +1,106 @@ +import { makeRequest, printRequestSummary } from '/app/test-utils.mjs'; + +await makeRequest('GET', '/health'); +await makeRequest('POST', '/test/string/set'); +await makeRequest('GET', '/test/string/get'); +await makeRequest('POST', '/test/string/mset'); +await makeRequest('GET', '/test/string/mget'); +await makeRequest('POST', '/test/string/setex'); +await makeRequest('POST', '/test/string/setnx'); +await makeRequest('POST', '/test/string/getdel'); +await makeRequest('POST', '/test/string/append'); +await makeRequest('POST', '/test/string/incr'); +await makeRequest('POST', '/test/string/incrby'); +await makeRequest('POST', '/test/string/incrbyfloat'); +await makeRequest('POST', '/test/string/decr'); +await makeRequest('POST', '/test/string/decrby'); +await makeRequest('GET', '/test/string/strlen'); +await makeRequest('GET', '/test/string/getrange'); +await makeRequest('POST', '/test/string/setrange'); +await makeRequest('POST', '/test/hash/hset'); +await makeRequest('GET', '/test/hash/hget'); +await makeRequest('GET', '/test/hash/hgetall'); +await makeRequest('POST', '/test/hash/hmset'); +await makeRequest('GET', '/test/hash/hmget'); +await makeRequest('POST', '/test/hash/hdel'); +await makeRequest('GET', '/test/hash/hexists'); +await makeRequest('GET', '/test/hash/hkeys'); +await makeRequest('GET', '/test/hash/hvals'); +await makeRequest('GET', '/test/hash/hlen'); +await makeRequest('POST', '/test/hash/hincrby'); +await makeRequest('POST', '/test/hash/hincrbyfloat'); +await makeRequest('POST', '/test/hash/hsetnx'); +await makeRequest('POST', '/test/list/lpush'); +await makeRequest('POST', '/test/list/rpush'); +await makeRequest('GET', '/test/list/lrange'); +await makeRequest('POST', '/test/list/lpop'); +await makeRequest('POST', '/test/list/rpop'); +await makeRequest('GET', '/test/list/llen'); +await makeRequest('GET', '/test/list/lindex'); +await makeRequest('POST', '/test/list/lset'); +await makeRequest('POST', '/test/list/linsert'); +await makeRequest('POST', '/test/list/lrem'); +await makeRequest('POST', '/test/list/ltrim'); +await makeRequest('POST', '/test/list/rpoplpush'); +await makeRequest('POST', '/test/list/lpos'); +await makeRequest('POST', '/test/list/lmove'); +await makeRequest('POST', '/test/set/sadd'); +await makeRequest('GET', '/test/set/smembers'); +await makeRequest('GET', '/test/set/sismember'); +await makeRequest('POST', '/test/set/srem'); +await makeRequest('GET', '/test/set/scard'); +await makeRequest('POST', '/test/set/spop'); +await makeRequest('GET', '/test/set/srandmember'); +await makeRequest('POST', '/test/set/sdiff'); +await makeRequest('POST', '/test/set/sinter'); +await makeRequest('POST', '/test/set/sunion'); +await makeRequest('POST', '/test/set/smove'); +await makeRequest('POST', '/test/zset/zadd'); +await makeRequest('GET', '/test/zset/zrange'); +await makeRequest('GET', '/test/zset/zrange-withscores'); +await makeRequest('GET', '/test/zset/zrevrange'); +await makeRequest('GET', '/test/zset/zscore'); +await makeRequest('POST', '/test/zset/zincrby'); +await makeRequest('GET', '/test/zset/zcard'); +await makeRequest('GET', '/test/zset/zcount'); +await makeRequest('GET', '/test/zset/zrank'); +await makeRequest('GET', '/test/zset/zrevrank'); +await makeRequest('POST', '/test/zset/zrem'); +await makeRequest('POST', '/test/zset/zpopmin'); +await makeRequest('POST', '/test/zset/zpopmax'); +await makeRequest('GET', '/test/zset/zrangebyscore'); +await makeRequest('GET', '/test/zset/zrevrangebyscore'); +await makeRequest('POST', '/test/zset/zremrangebyrank'); +await makeRequest('POST', '/test/zset/zremrangebyscore'); +await makeRequest('POST', '/test/key/del'); +await makeRequest('GET', '/test/key/exists'); +await makeRequest('POST', '/test/key/expire'); +await makeRequest('POST', '/test/key/expireat'); +await makeRequest('GET', '/test/key/ttl'); +await makeRequest('GET', '/test/key/pttl'); +await makeRequest('POST', '/test/key/persist'); +await makeRequest('GET', '/test/key/keys'); +await makeRequest('GET', '/test/key/randomkey'); +await makeRequest('POST', '/test/key/rename'); +await makeRequest('POST', '/test/key/renamenx'); +await makeRequest('GET', '/test/key/type'); +await makeRequest('POST', '/test/key/touch'); +await makeRequest('POST', '/test/key/unlink'); +await makeRequest('POST', '/test/bitmap/setbit'); +await makeRequest('GET', '/test/bitmap/getbit'); +await makeRequest('GET', '/test/bitmap/bitcount'); +await makeRequest('GET', '/test/bitmap/bitpos'); +await makeRequest('POST', '/test/bitmap/bitop'); +await makeRequest('GET', '/test/server/ping'); +await makeRequest('GET', '/test/server/dbsize'); +await makeRequest('POST', '/test/server/echo'); +await makeRequest('POST', '/test/hll/pfadd'); +await makeRequest('GET', '/test/hll/pfcount'); +await makeRequest('POST', '/test/hll/pfmerge'); +await makeRequest('POST', '/test/geo/geoadd'); +await makeRequest('GET', '/test/geo/geopos'); +await makeRequest('POST', '/test/geo/geodist'); +await makeRequest('GET', '/test/geo/geohash'); +await makeRequest('GET', '/cleanup'); + +printRequestSummary();