Skip to content

Test the curl-config, header and UTF-8 helpers directly - #4

Merged
dmccoystephenson merged 1 commit into
mainfrom
feature/detail-helper-tests
Oct 3, 2026
Merged

dmccoystephenson merged 1 commit into
mainfrom
feature/detail-helper-tests

Conversation

@dmccoystephenson

Copy link
Copy Markdown
Member

Summary

Stage B (unit-test expansion) cycle: five direct tests are added for trace_client::detail helpers that were previously exercised only end to end, or only on one input. The tests describe current behavior; trace_client.hpp is not modified.

  • cleanUtf8ReplacesEveryMalformedSequence: well-formed 1–4 byte text and an embedded NUL pass through. Overlong 2- and 3-byte forms, UTF-16 surrogates, code points past U+10FFFF, stray continuation bytes, a cut-off trailing sequence and a lead byte with no continuation each become one U+FFFD per byte.
  • cleanUtf8NeverExceedsTheByteLimit: a limit of 0, plain truncation, and a U+FFFD or 4-byte character that would not fit are all left out whole rather than cut.
  • curlQuoteKeepsEveryValueOnOneConfigLine: backslash, quote and \n \r \t \v are escaped, other control bytes are dropped, and UTF-8 and DEL are kept. A property check runs over all 256 byte values and confirms the result has no raw control character and no unescaped quote inside the surrounding pair. This is the curl-config injection boundary described in "How a report is sent".
  • headerSafeDropsControlCharactersOnly: CR, LF, TAB, NUL and DEL are dropped; spaces and UTF-8 are kept.
  • parseStatusReadsCurlsWriteOutOrReportsNoAnswer: 201, " 401\r\n" and 999 are read as statuses; 000 (curl's "no response"), empty input, whitespace only, four digits, non-digits and a status line all give -1. The test is guarded to the system-curl transport, the only build where parseStatus exists.

No production behavior was found to disagree with the README while these were written, so no bug issue was filed.

Deferred

Test plan

  • make test (g++, C++11, -Werror): 31 tests, 0 failed checks (26 → 31, all five new tests print ok)
  • make test STD=c++2a (the local g++ predates the -std=c++20 spelling): 31 tests, 0 failed checks
  • make test-libcurl: 31 tests, 0 failed checks (parseStatus test compiled out)
  • CI Build: linux (g++/clang++ × c++11/17/20), linux-libcurl, linux-sanitizers, macos, windows

No tracking issue: this coverage gap was found during triage.

This PR description was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).

🤖 Generated with Claude Code


drafted by Claude on behalf of Daniel Stephenson

curlQuote, headerSafe and parseStatus were only exercised end to end, and
cleanUtf8 only on one invalid byte and one truncation. The new tests pin
their current behavior: every byte value quotes to one line with no raw
control character or bare quote, header values lose only control
characters, curl's write-out is read as a status or -1 (000 included), and
cleanUtf8 replaces overlong, surrogate, out-of-range, stray and cut-off
sequences byte by byte while never exceeding its limit or splitting a
character. Tests only; no change to the header.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dmccoystephenson

Copy link
Copy Markdown
Member Author

Self-review rubric (scored against the diff and CI run 36685477444):

  • Scope: PASS. Only test/test_trace_client.cpp is modified (+82 lines): five test functions and their five tests[] entries. trace_client.hpp is unchanged.
  • Tests-new: PASS (not applicable). No public or detail:: function was added. The PR adds direct coverage for four existing detail:: helpers that had none or almost none.
  • Tests-fix: not applicable. This is not a bug fix; every assertion describes current behavior and passed on the first run against an unmodified header.
  • Sibling structure: PASS. Each test is a static void named as a sentence, uses CHECK / CHECK_EQ, and sits beside jsonIsEscapedAndCleaned / tagsAreHeldToTheServerLimits, following their using trace_client::detail::... pattern.
  • Sibling renames: not applicable. Nothing was renamed.
  • Docs: PASS. No behavior changed, so no README row moves. The README's "How a report is sent" claim that a value can never end its config line is now asserted directly by the 256-byte property check in curlQuoteKeepsEveryValueOnOneConfigLine.
  • Issue resolution: not applicable. No Closes; the gap was found during triage.
  • CI: PASS. All 11 jobs are green on the head SHA: linux × 6, linux-libcurl, both linux-sanitizers, macos, windows.
  • Registered: PASS. Each of the five new names occurs twice in the file (definition and tests[] entry). The local count rose from 26 to 31 tests, with 0 failed checks.
  • C++11: PASS. The local make test (g++, -std=c++11 -Werror) is clean, and the CI c++11 legs (g++ and clang++) are green.
  • No new dependency: PASS. No #include was added and neither Makefile nor CMakeLists.txt changed.
  • Never throws / Curl argv fixed / Version triple: not applicable. The header is unchanged.
  • Sanitizers: PASS. Both linux-sanitizers jobs are green. The new tests start no threads.

Observations (judgment calls, no change made):

  • test/test_trace_client.cpp parseStatusReadsCurlsWriteOutOrReportsNoAnswer: parseStatus also accepts one- and two-digit values ("5" gives 5) and skips whitespace between digits ("2 01" gives 201). curl's %{http_code} always prints exactly three digits, so neither case is reachable, and neither is pinned by a test. Both were left out on purpose so that tightening the parser later would not have to fight a characterization test.
  • test/test_trace_client.cpp curlQuoteKeepsEveryValueOnOneConfigLine: the property loop checks the structure of the output (one line, quotes balanced) but not that curl would decode the escapes back to the original bytes. The end-to-end nothingTheProgramPassesCanChangeWhereOrWhatCurlSends test already covers the round trip through real curl.
  • Timing: the new tests do no I/O and run in 0.00 s each, so they add no flake risk under TSan/ASan.

Summary: a tests-only change, green on every platform. No mechanical fixes were needed.

This review comment was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).


drafted by Claude on behalf of Daniel Stephenson

@dmccoystephenson
dmccoystephenson merged commit d234596 into main Oct 3, 2026
11 checks passed
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