Commit aab1847
committed
feat+fix: address issue #8 (diagnostic + rate-limit + env-file)
Addresses all three asks from GitHub issue #8 plus a follow-up retry on
the documented sandbox rate-limit surface.
== Ask 1: log raw response body on meta-failure throws ==
CERTInextClient.cs throws "CERTInext ... failed: <ErrorMessage>. See
gateway logs for details." on every non-success meta block, but most
sites had no Logger.LogXxx call before the throw — the "see gateway
logs" instruction was misleading because no detail was ever logged.
Added a shared LogApiFailure(operationContext, resp, errorCode, errorMsg)
helper that emits a structured LogWarning capturing HTTP status, the
CERTInext error code + message, and a 4 KB-capped raw response body.
Wired into every meta-failure throw site:
- ValidateCredentials, PlaceOrderAsync, SubmitCsrAsync (HTTP fail),
TrackOrderAsync, DownloadCertificateAsync, RevokeCertificateAsync,
VerifyDcvAsync, GetDcvAsync.
The exception text is unchanged (kept sanitized for the gateway UI per
the prior C2 audit finding), but operators now actually have the raw
body in gateway logs to diagnose. Closes the audit's deferred C2 item.
== Ask 2: Troubleshooting section ==
New docsource/overview.md + mirrored "Troubleshooting" section in
README.md covering:
- "Inactive Account User." as a sandbox rate-limit surface
- Enrollment returning EXTERNALVALIDATION (deferred to next sync)
- EMS-956 from GetDcv (deferred to next sync)
- Issue #7 type-load failure (closed in v1.0)
== Ask 3: LoadEnvFile quote stripping ==
IntegrationTestFixture.LoadEnvFile passed quoted env values through with
the quote characters included, so:
CERTINEXT_REQUESTOR_NAME="Keyfactor Plugin Test"
was sent to CERTInext as the 23-char literal `"Keyfactor Plugin Test"`.
The integration tests have been sending the literal quotes for the
entire project history.
Extracted the value-parsing into IntegrationTestFixture.ParseEnvValue
(internal static), added matching-pair double/single quote stripping
per the reporter's snippet, and pinned the contract with 13 unit tests
in IntegrationTestFixtureTests covering quoted, unquoted, whitespace,
mismatched-quote, empty, and embedded-quote cases.
== Follow-up: exponential-backoff retry on rate-limit surface ==
PlaceOrderAsync now auto-retries when meta.ErrorMessage matches the
documented "Inactive Account User." rate-limit surface (case-insensitive
substring match via IsRateLimitSurface). Up to 5 attempts with exponential
backoff + ±25% jitter — nominal delays 1s / 2s / 4s / 8s / 16s, max
total wait ~31s. Each retry refreshes the meta block so txn IDs stay
unique. After all attempts exhaust, the original exception is propagated
unchanged so a genuinely-inactive account surfaces identically to today.
ComputeRateLimitBackoffSeconds + IsRateLimitSurface are exposed internal
so unit tests can pin both the predicate (15 inline-data cases) and the
backoff schedule (50 samples per attempt window).
== Verification ==
- Release build: 0 warnings, 0 errors.
- Unit tests: 171/171 pass (156 prior + 15 new in RateLimitRetryTests
+ IntegrationTestFixtureTests).
- Live ping/sync against the sandbox still authenticates.
- Issue #7's CERTInextCAPluginPublicSurfaceTests still passes — the
refactor of PlaceOrderAsync didn't expose any v3.3-only types.1 parent 054b2c3 commit aab1847
6 files changed
Lines changed: 441 additions & 33 deletions
File tree
- CERTInext.IntegrationTests
- CERTInext.Tests
- CERTInext/Client
- docsource
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
159 | 159 | | |
160 | 160 | | |
161 | 161 | | |
162 | | - | |
| 162 | + | |
163 | 163 | | |
164 | 164 | | |
165 | 165 | | |
| |||
176 | 176 | | |
177 | 177 | | |
178 | 178 | | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
179 | 201 | | |
180 | 202 | | |
181 | 203 | | |
| |||
Lines changed: 53 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
0 commit comments