test(telemetry): pin the wire format and what a dead collector costs - #736
Open
latekvo wants to merge 1 commit into
Open
test(telemetry): pin the wire format and what a dead collector costs#736latekvo wants to merge 1 commit into
latekvo wants to merge 1 commit into
Conversation
The telemetry tests so far prove the exporter is CONFIGURED correctly - the right url, the right headers, the right timeouts on the constructor. Nothing looks at the request that comes out of it, or at what happens when the collector on the other end does not answer. Both gaps matter because a failing export is silent by design: the batch processor hands errors to OpenTelemetry's global handler, so shutdown() resolves the same way for a delivered batch, a 401 and a dead host. Nothing downstream can tell us the payload stopped matching what the collector reads, either - an OTLP exporter counts any 2xx as delivered. otel-wire.test.ts captures the real exporter's request off a loopback socket and asserts it is uncompressed OTLP/JSON on POST /v1/logs, that service.name, the instrumentation scope, the record body, the severity and the attribute types land where the ingestion schema reads them, and that a batch never exceeds maxExportBatchSize records. otel-unreachable.test.ts drives the same exporter against a socket that accepts and never answers, a refused port, a 401 and a 204, and asserts the drain stays inside its deadline and the stalled socket is destroyed rather than left in the keep-alive pool. Dropping timeoutMillis from createExporter fails two of these; the SDK's own default would let the drain run to 10s. otel-endpoint.test.ts gains the source-side half: the wire test builds its record the way OtelClient.emit does rather than calling it, because the endpoint is deliberately unredirectable and there is no seam to point the client at a test server. So SERVICE_NAME, the logger name, the event-name-as-body mapping and the dropping of null properties are pinned here, against the real emit path.
latekvo
marked this pull request as ready for review
August 5, 2026 12:16
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #570 - base is
feat/otel-telemetry, so the diff here is just the tests.The suite on #570 proves the exporter is configured right: the url, the headers, the timeouts, all read off the constructor. Nothing looks at the request that comes out of it, or at what happens when the collector does not answer.
Both gaps are invisible from inside Argent by design. A failed export never reaches the caller - the batch processor hands the error to OpenTelemetry’s global handler, so
shutdown()resolves identically for a delivered batch, a 401, and a dead host. And an OTLP exporter counts any 2xx as delivered, so a payload the collector maps differently is still a success from here.otel-wire.test.tsCaptures the real exporter’s request off a loopback socket and asserts:
POST /v1/logs- not protobuf, and nocontent-encodingservice.name, the instrumentation scope, the record body, the severity and the attribute value types land where the ingestion schema reads themmaxExportBatchSizerecordsotel-unreachable.test.tsDrives the same exporter against a socket that accepts and never answers, a refused port, a 401 and a 204, and asserts the drain stays inside its deadline and the stalled socket is destroyed rather than parked in the keep-alive pool.
Dropping
timeoutMillisfromcreateExporterfails two of these; the SDK’s own default would let the drain run to 10s.otel-endpoint.test.tsThe wire test mirrors
OtelClient.emitrather than calling it -getClient()resolves its endpoint from the hard-codedOTLP_LOGS_ENDPOINT, and that being unredirectable is the anti-exfiltration property the client is meant to have, so there is deliberately no seam to aim it at a test server. That left the source constants unpinned, so this adds the other half against the real emit path:SERVICE_NAME, the logger name, event-name-as-body, and the dropping of null/undefined properties.Provenance
These were shell rigs used while verifying #570 by hand (a wire probe, a capture receiver with a blackhole mode, a silent TCP server, a proxy). They were parked in the infra repo, where nobody would ever run them. This is them as tests that run on every push.
Verified
npm test -w @argent/telemetry- 304 passed, 19 filestypecheck:tests,eslint --max-warnings 0,prettier --checkall cleanSERVICE_NAMErenamed, logger name changed, body no longer the event,severityTextflipped, the null-property skip removed, andtimeoutMillisdropped. RemovinghttpAgentOptionsdoes not fail these - that option covers a socket stuck in connect, which needs an address whose packets are dropped and is not portably reproducible here; it stays pinned at the constructor inotel-endpoint.test.ts, and the docstring says so rather than implying coverage it does not have.CI does not run on this PR yet
Every substantive workflow in this repo is gated on
pull_request: branches: [main], so a PR based onfeat/otel-telemetrygets zero checks. That resolves itself when #570 merges and GitHub retargets this base tomain.These tests cannot run against
mainat all in the meantime - they exercisecreateExporterand the rest ofotel.ts, which only exist on #570. So the stack is required, not a convenience.Run locally in the meantime, mirroring
unit-tests.ymlon its Node 20:npm test -w @argent/telemetry304 passed,typecheck:testsclean,eslint --max-warnings 0clean,prettier --checkclean.