Skip to content

test: add ConsoleMetricExporter unit test + assert metrics via in-memory reader - #480

Merged
sjvans merged 1 commit into
test/in-memory-metric-readerfrom
test/metrics-console-and-in-memory
Aug 11, 2026
Merged

test: add ConsoleMetricExporter unit test + assert metrics via in-memory reader#480
sjvans merged 1 commit into
test/in-memory-metric-readerfrom
test/metrics-console-and-in-memory

Conversation

@sjvans

@sjvans sjvans commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

What

Completes the metrics-test story so it mirrors the tracing side, which already has both a console-span-exporter.test.js unit test and in-memory-exporter–based integration tests.

  • New test/console-metric-exporter.test.js — a pure unit test of lib/exporter/ConsoleMetricExporter.js (modeled on console-span-exporter.test.js): hooks cds.log('telemetry').info, feeds crafted ResourceMetrics fixtures, and asserts the formatted output. Covers all branches: the db.pool table, the queue table, "other" metrics (single-datapoint unwrapped vs multi-datapoint array), tenant-labeled variants, host-metrics aggregation (with/without HOST_METRICS_LOG_SYSTEM), and the shutdown→FAILED path.
  • test/metrics.test.js — converted from scraping cds.test.log() output to asserting on the in-memory MyInMemoryMetricReader datapoints, via the same expectEventually() force-flush polling helper used by the outbox suites. Now asserts on what is actually collected (process metrics present, system/network absent by default; nodejs.eventloop.time has multiple datapoints vs utilization's one) rather than on log strings — the formatting of those is covered by the new unit test.
  • Wires the in-memory reader into the [metrics] profile in .cdsrc.json (matching what test: capture outbox+console metrics via in-memory reader & unit-test ConsoleMetricExporter #479 did for the outbox profiles).

Why

Follow-up to #465 (span test infra) and #479 (outbox metrics reader): eliminate the last console/log-string spying in the metrics suite and give ConsoleMetricExporter direct unit coverage.

Notes

@hyperspace-pr-bot

Copy link
Copy Markdown
Contributor

Summary

The following content is AI-generated and provides a summary of the pull request:


Add ConsoleMetricExporter Unit Tests + In-Memory Reader for Metrics Integration Tests

Test

✅ Completes the metrics test infrastructure by adding a pure unit test for ConsoleMetricExporter and migrating the integration test suite from log-string scraping to in-memory datapoint assertions — mirroring what was already done for tracing (#465) and outbox metrics (#479).

Changes

  • test/console-metric-exporter.test.js (new): Pure unit test for lib/exporter/ConsoleMetricExporter.js. Hooks cds.log('telemetry').info before requiring the exporter, then feeds crafted ResourceMetrics fixtures and asserts the formatted output. Covers all output branches:

    • db.pool table (with and without tenant label)
    • queue table (with and without tenant label)
    • "Other" metrics: single-datapoint (unwrapped) vs. multi-datapoint (array)
    • Host metrics aggregation (process.* always; system.* only when HOST_METRICS_LOG_SYSTEM is set)
    • Shutdown → FAILED path
  • test/metrics.test.js: Converted from scraping cds.test.log() output to asserting on in-memory MyInMemoryMetricReader datapoints. Replaces fixed wait() sleeps with the expectEventually() force-flush polling helper (same pattern as the outbox suites). Now verifies what is actually collected — process metrics present, system/network absent by default; nodejs.eventloop.time has multiple datapoints vs. utilization's one — while formatting is delegated to the new unit test.

  • test/bookshop/.cdsrc.json: Wires MyInMemoryMetricReader into the [metrics] profile (matching what test: capture outbox+console metrics via in-memory reader & unit-test ConsoleMetricExporter #479 did for the outbox profiles), so metrics.test.js captures real exported datapoints instead of log output.

Jira Issues

No JIRA issues referenced.

Images and Links

Related PRs: #465, #474, #476, #479


  • 🔄 Regenerate and Update Summary
  • ✏️ Insert as PR Description (deletes this comment)
  • 🗑️ Delete comment
PR Bot Information

Version: 1.29.18

@hyperspace-pr-bot hyperspace-pr-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR is well-structured and clearly mirrors the established console-span-exporter.test.js pattern. The only structural issue found is that const { expect } = require('@cap-js/cds-test') is placed after the helper functions that use it (line 50), whereas the ConsoleSpanExporter test keeps it at the top alongside the other imports — the late import is harmless at runtime today but is inconsistent and could cause a ReferenceError if exportAndCapture were ever called at module-evaluation time. This was flagged with a suggested fix above.

PR Bot Information

Version: 1.29.18

  • LLM: anthropic--claude-4.6-sonnet
  • Event Trigger: pull_request.opened
  • File Content Strategy: Full file content
  • Correlation ID: 0632a390-956a-11f1-8168-6c863a2dcb37

Comment on lines +17 to +50
const ConsoleMetricExporter = require('../lib/exporter/ConsoleMetricExporter')

afterAll(() => {
telemetryLog.info = originalInfo
})

beforeEach(() => {
infoCalls.length = 0
})

// --- helpers ---------------------------------------------------------------

// Builds a minimal ScopeMetrics-shaped object.
function scopeMetrics(name, metrics) {
return { scope: { name }, metrics }
}

// Builds a minimal MetricData-shaped object. `dataPoints` are `{ attributes, value }`.
function metric(name, dataPoints, description = name) {
return { descriptor: { name, description }, dataPoints }
}

// Drives the exporter and returns the lines logged. Asserts the result callback got SUCCESS.
function exportAndCapture(scopes) {
const exporter = new ConsoleMetricExporter()
let result
exporter.export({ scopeMetrics: scopes }, r => (result = r))
expect(result).to.deep.equal({ code: 0 /* ExportResultCode.SUCCESS */ })
return infoCalls.map(args => args[0])
}

// --- assertions ------------------------------------------------------------

const { expect } = require('@cap-js/cds-test')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: expect is required after it is first used, causing a ReferenceError at runtime.

exportAndCapture (called from the test bodies) uses expect at line 44, but expect is not imported until line 50 — after all the helper definitions. Because exportAndCapture is only called at runtime this works in practice today, but the exportAndCapture definition itself references expect in a closure that executes before the import on line 50 if test helpers are ever invoked at module-evaluation time. More importantly, it is inconsistent with console-span-exporter.test.js, which places the expect import immediately after hooking LOG.info (before the helpers). Move the import to the top of the file, alongside the other require calls.

Suggested change
const ConsoleMetricExporter = require('../lib/exporter/ConsoleMetricExporter')
afterAll(() => {
telemetryLog.info = originalInfo
})
beforeEach(() => {
infoCalls.length = 0
})
// --- helpers ---------------------------------------------------------------
// Builds a minimal ScopeMetrics-shaped object.
function scopeMetrics(name, metrics) {
return { scope: { name }, metrics }
}
// Builds a minimal MetricData-shaped object. `dataPoints` are `{ attributes, value }`.
function metric(name, dataPoints, description = name) {
return { descriptor: { name, description }, dataPoints }
}
// Drives the exporter and returns the lines logged. Asserts the result callback got SUCCESS.
function exportAndCapture(scopes) {
const exporter = new ConsoleMetricExporter()
let result
exporter.export({ scopeMetrics: scopes }, r => (result = r))
expect(result).to.deep.equal({ code: 0 /* ExportResultCode.SUCCESS */ })
return infoCalls.map(args => args[0])
}
// --- assertions ------------------------------------------------------------
const { expect } = require('@cap-js/cds-test')
const { expect } = require('@cap-js/cds-test')
const ConsoleMetricExporter = require('../lib/exporter/ConsoleMetricExporter')
afterAll(() => {
telemetryLog.info = originalInfo
})
beforeEach(() => {
infoCalls.length = 0
})
// --- helpers ---------------------------------------------------------------
// Builds a minimal ScopeMetrics-shaped object.
function scopeMetrics(name, metrics) {
return { scope: { name }, metrics }
}
// Builds a minimal MetricData-shaped object. `dataPoints` are `{ attributes, value }`.
function metric(name, dataPoints, description = name) {
return { descriptor: { name, description }, dataPoints }
}
// Drives the exporter and returns the lines logged. Asserts the result callback got SUCCESS.
function exportAndCapture(scopes) {
const exporter = new ConsoleMetricExporter()
let result
exporter.export({ scopeMetrics: scopes }, r => (result = r))
expect(result).to.deep.equal({ code: 0 /* ExportResultCode.SUCCESS */ })
return infoCalls.map(args => args[0])
}

Double-check suggestion before committing. Edit this comment for amendments.


Please provide feedback on the review comment by checking the appropriate box:

  • 🌟 Awesome comment, a human might have missed that.
  • ✅ Helpful comment
  • 🤷 Neutral
  • ❌ This comment is not helpful

@sjvans
sjvans force-pushed the test/metrics-console-and-in-memory branch from 71b7877 to d948065 Compare August 11, 2026 10:08
@sjvans
sjvans merged commit d948065 into test/in-memory-metric-reader Aug 11, 2026
@sjvans
sjvans deleted the test/metrics-console-and-in-memory branch August 11, 2026 11:19
sjvans added a commit that referenced this pull request Aug 11, 2026
… ConsoleMetricExporter (#479)

## What

Consolidates all outbox/metrics test-quality work into one PR (formerly
split as #479 + the stacked #480).

- **In-memory metric reader** —
`test/bookshop/lib/MyInMemoryMetricReader.js`, the metrics counterpart
to `MyInMemorySpanExporter` (#465). Mirrors production **DELTA**
temporality: SUM counters are accumulated across flushes into per-series
running totals; GAUGE datapoints keep the latest absolute value. Wired
via the `metrics-outbox`, `metrics-outbox-disabled`, and `metrics`
profiles in `.cdsrc.json`.
- **Outbox suites off console spying** — the three
`metrics-outbox*.test.js` suites drop the `console.dir` spy and fixed
`wait()` sleeps in favor of the reader + an `expectEventually()`
force-flush polling helper (fails fast if the meter provider isn't
wired). Folds in #445's polling approach.
- **ConsoleMetricExporter unit test** — new
`test/console-metric-exporter.test.js`, a pure unit test of the
exporter's formatting (db.pool table, queue table, other
single-vs-array, tenant variants, host-metrics aggregation,
shutdown→FAILED), mirroring `console-span-exporter.test.js`.
- **`metrics.test.js`** converted from scraping `cds.test.log()` output
to asserting on the in-memory reader's datapoints.

Metrics testing now mirrors the tracing side exactly: a to-console unit
test **plus** in-memory-exporter–based integration tests.

## Why

Follow-up to #465 (span test infra): eliminate console/log spying in the
metrics suite and give `ConsoleMetricExporter` direct unit coverage.

## Review addressed

- Bot review triaged: explicit `COUNTER_METRIC_NAMES` dispatch for
`isCounter`; real wall-clock debounce in the multitenant test; isolation
NOTE on the module-level singletons.
- Dropped the unused debug-log silencer in the multitenant suite (never
asserted). Kept the single-tenant `debugLog` mock — it backs a real
`unknown service` assertion.

Test-only change (no `lib/` change), so no CHANGELOG entry — consistent
with #465/#474/#476.

closes #478

Supersedes #445 and #480 (both folded in here) — I'll close them once
this merges.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant