From c867ca715ac8291046d697389f0f1474d8c58cc4 Mon Sep 17 00:00:00 2001 From: Sebastian Van Syckel Date: Mon, 17 Aug 2026 14:13:44 +0200 Subject: [PATCH 1/2] test: convert remote/span-name tracing tests to in-memory span exporter; drop tracing-attributes profile (#478) --- test/bookshop/.cdsrc.json | 11 ----------- test/tracing-remote-cloudsdk.test.js | 12 ++++++++---- test/tracing-remote-native.test.js | 12 ++++++++---- test/tracing-span-names.test.js | 19 +++++++++++++------ 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/test/bookshop/.cdsrc.json b/test/bookshop/.cdsrc.json index 7b046bcf..842dfd24 100644 --- a/test/bookshop/.cdsrc.json +++ b/test/bookshop/.cdsrc.json @@ -75,17 +75,6 @@ } } }, - "[tracing-attributes]": { - "requires": { - "telemetry": { - "tracing": { - "exporter": { - "module": "@opentelemetry/sdk-trace-node" - } - } - } - } - }, "[tracing-in-memory]": { "requires": { "telemetry": { diff --git a/test/tracing-remote-cloudsdk.test.js b/test/tracing-remote-cloudsdk.test.js index 4adfcede..16b5b79e 100644 --- a/test/tracing-remote-cloudsdk.test.js +++ b/test/tracing-remote-cloudsdk.test.js @@ -1,17 +1,21 @@ const cds = require('@sap/cds') -const { expect } = cds.test(__dirname + '/bookshop', '--profile', 'tracing-attributes') +const { expect } = cds.test(__dirname + '/bookshop', '--profile', 'tracing-in-memory') const http = require('http') +// The tracing-in-memory profile (see test/bookshop/.cdsrc.json) configures +// MyInMemorySpanExporter as the trace exporter. We read the captured ReadableSpan +// objects directly out of its shared buffer — no console spy. +const { captured, reset } = require('./bookshop/lib/MyInMemorySpanExporter') + // Cloud SDK path: with @sap-cloud-sdk/http-client installed (as in the bookshop) and // cds.env.remote.native_fetch NOT set, CAP routes outbound remote calls through // getCloudSdk().executeHttpRequestWithOrigin(...). lib/tracing/cloud_sdk.js wraps that // export so the outbound call produces a @cap-js/telemetry CLIENT span carrying // the sap.btp.destination attribute. describe('tracing remote via cloud sdk', () => { - const log = vi.spyOn(console, 'dir') - beforeEach(log.mockClear) + beforeEach(reset) - const getSpans = () => log.mock.calls.map(c => c[0]).filter(Boolean) + const getSpans = () => captured const getCapSpans = () => getSpans().filter(s => s.instrumentationScope?.name === '@cap-js/telemetry') let server, port diff --git a/test/tracing-remote-native.test.js b/test/tracing-remote-native.test.js index f930a5d8..857c5f19 100644 --- a/test/tracing-remote-native.test.js +++ b/test/tracing-remote-native.test.js @@ -3,19 +3,23 @@ process.env.cds_remote_native__fetch = 'true' const cds = require('@sap/cds') -const { expect } = cds.test(__dirname + '/bookshop', '--profile', 'tracing-attributes') +const { expect } = cds.test(__dirname + '/bookshop', '--profile', 'tracing-in-memory') const http = require('http') +// The tracing-in-memory profile (see test/bookshop/.cdsrc.json) configures +// MyInMemorySpanExporter as the trace exporter. We read the captured ReadableSpan +// objects directly out of its shared buffer — no console spy. +const { captured, reset } = require('./bookshop/lib/MyInMemorySpanExporter') + // Native fetch path: when cds.env.remote.native_fetch === true (or no cloud sdk is // installed), CAP routes outbound remote calls through native fetch, which is // instrumented by @opentelemetry/instrumentation-undici. The outbound span therefore // comes from that instrumentation scope (NOT @opentelemetry/instrumentation-http, and // NOT our cloud_sdk wrapper) and carries the standard http.* / url.* / server.* attributes. describe('tracing remote via native fetch', () => { - const log = vi.spyOn(console, 'dir') - beforeEach(log.mockClear) + beforeEach(reset) - const getSpans = () => log.mock.calls.map(c => c[0]).filter(Boolean) + const getSpans = () => captured let server, port diff --git a/test/tracing-span-names.test.js b/test/tracing-span-names.test.js index 8ff39d39..8ea16737 100644 --- a/test/tracing-span-names.test.js +++ b/test/tracing-span-names.test.js @@ -1,14 +1,21 @@ const cds = require('@sap/cds') -const { expect, data } = cds.test(__dirname + '/bookshop', '--profile', 'tracing-attributes') +const { expect, data } = cds.test(__dirname + '/bookshop', '--profile', 'tracing-in-memory') const http = require('http') -describe('span names', () => { - beforeEach(data.reset) +// The tracing-in-memory profile (see test/bookshop/.cdsrc.json) configures +// MyInMemorySpanExporter as the trace exporter. We read the captured ReadableSpan +// objects directly out of its shared buffer — no console spy. +const { captured } = require('./bookshop/lib/MyInMemorySpanExporter') - const log = vi.spyOn(console, 'dir') - beforeEach(log.mockClear) +describe('span names', () => { + beforeEach(async () => { + // data.reset is itself heavily traced (it runs DELETEs + INSERTs for the seed data) — + // run it first, THEN clear the buffer so the test only sees its own spans. + await data.reset() + captured.length = 0 + }) - const getSpans = () => log.mock.calls.map(c => c[0]).filter(Boolean) + const getSpans = () => captured // Spans from our tracer only (excludes HTTP instrumentation spans) const getCapSpans = () => getSpans().filter(s => s.instrumentationScope?.name === '@cap-js/telemetry') From f5b5d88253fcdcdf09153e1b9638c2db287dde95 Mon Sep 17 00:00:00 2001 From: Sebastian Van Syckel Date: Mon, 17 Aug 2026 14:20:55 +0200 Subject: [PATCH 2/2] test: use reset() helper for buffer clear (review nit) --- test/tracing-span-names.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/tracing-span-names.test.js b/test/tracing-span-names.test.js index 8ea16737..73a30903 100644 --- a/test/tracing-span-names.test.js +++ b/test/tracing-span-names.test.js @@ -5,14 +5,14 @@ const http = require('http') // The tracing-in-memory profile (see test/bookshop/.cdsrc.json) configures // MyInMemorySpanExporter as the trace exporter. We read the captured ReadableSpan // objects directly out of its shared buffer — no console spy. -const { captured } = require('./bookshop/lib/MyInMemorySpanExporter') +const { captured, reset } = require('./bookshop/lib/MyInMemorySpanExporter') describe('span names', () => { beforeEach(async () => { // data.reset is itself heavily traced (it runs DELETEs + INSERTs for the seed data) — // run it first, THEN clear the buffer so the test only sees its own spans. await data.reset() - captured.length = 0 + reset() }) const getSpans = () => captured