basecamp / basecamp/basecamp-sdk

Conformance fixtures have no clock, so SPEC §6's positive HTTP-date branch is unfalsifiable

Closed
#780 2 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Go
Stars
49
Forks
12
Avg merge
20h 47m
Merged PRs (30d)
89

Description

Fell out of #564. SPEC §6 step 2 says a `Retry-After` HTTP-date becomes `max(0, date - now())` seconds
and is honoured when that is **positive**. Conformance can pin the negative half of that rule — a past
date must fall through to backoff — and cannot pin the positive half at all, because a fixture is a
static JSON literal and the harness has no clock.

The bind is exact and has no fixture-level escape:

- A date far enough in the future to stay future-dated for the life of the repo makes every compliant
SDK sleep for years. `Retry-After` is explicitly exempt from SPEC §7's backoff ceiling, so nothing
clamps it.
- A date close enough to assert a small `delayBetweenRequests` against is past-dated within seconds of
being written, at which point the case silently reverts to testing the fall-through path.

So "compliant" and "non-compliant" are only distinguishable for a date a few seconds ahead of the
run's own `now()`, and no runner can produce one. #564 therefore pins the positive half in per-SDK
unit tests and says so in the fixture's `description`, which is the honest shape but leaves the one
branch that actually regressed (Kotlin's, ignored for the SDK's whole life) without a cross-SDK guard.

### What a fix looks like

A substitution token in `mockResponses[].headers` values, resolved **at response-serving time** to an
IMF-fixdate N seconds ahead — e.g. `"Retry-After": "{{httpdate+3s}}"` — plus a fixture asserting
`delayBetweenRequests` with a `min` between the backoff term (1000 ms) and the guaranteed floor
(~2000 ms for the SDKs that truncate the difference to whole seconds; Go and Ruby do, Swift and
TypeScript round up).

Five of the six runners can take this as a transform at the point where they copy the fixture's header
map into the response:

| Runner | Site |
|---|---|
| Go | `conformance/runner/go/main.go:403-410` |
| Python | `conformance/runner/python/runner.py:1090-1092` |
| Swift | `conformance/runner/swift/Sources/ConformanceRunner/ScriptedTransport.swift:121-124` |
| TypeScript | `conformance/runner/typescript/runner.test.ts:1159-1166` |
| Kotlin | `kotlin/conformance/src/main/kotlin/com/basecamp/sdk/conformance/Main.kt:478-483` |

Ruby needs one extra step: it builds the entire response queue eagerly, headers included, *before* any
request arrives (`conformance/runner/ruby/runner.rb:1136-1145`), so the header map has to move into the
`to_return` block at `runner.rb:1158` first — otherwise the substitution resolves against the wrong
`now`.

`conformance/schema.json:95-101` types header values as plain strings today and would need to document
the token. The repo's convention would also want the resolver unit-tested per runner, the way
`checkDelayGaps` is.

### Why it was not done in #564

It is a six-runner change to the harness inside a two-SDK parsing fix, and `make check` runs all six
conformance suites, so it carries its own verification surface. Worth doing deliberately, on its own,
rather than as a rider — but it should be done, because "the fixture cannot fail" is the reason #564's
Kotlin defect survived a green suite in the first place, and the positive-date branch is still in
exactly that position.

Contributor guide

Open the contributing guide

Research direction

Start with the runner sites listed in the issue, then inspect conformance/schema.json and the existing checkDelayGaps resolver tests. Implement and test response-time HTTP-date substitution across all six runners, including Ruby's to_return path, and add a fixture that asserts the positive Retry-After delay with make check.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, kotlin, python, ruby, swift, typescript
Domain
testing-qa, tooling
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.