Repository navigation
test: add credential-free tests for calls only covered by live tests - #231
Conversation
…e tests Several tests pass while blazer sends the wrong request to B2, because each wire test builds its own fake server and most accept any body. Capture the problem and the outcome for a shared test helper before writing a spec. Refs #191
Spec R1-R12 define the internal/b2wiretest helper: recording, scripted responses, upload URLs and assertions, with no new dependencies and no public API. The plan orders the work, names a test per requirement and the break each test must catch. Refs #191
Assert that Stall returns before its duration when the request context ends. Align R5 with the separate ListKeys capabilities work in #220.
Treat scripted status zero as 200, reject empty JSON assertion paths consistently, and mark the server closed before draining HTTP handlers so late requests cannot report through a completed test. Document the path and header guarantees, the migrated test coverage, and the simulator contract NOT RUN rationale requested in PR #205.
serve and copyRequests both decoded a request body into a JSON map. Share one decodeJSON helper so the two paths cannot drift; a body that is not a JSON object still decodes to nil. The same change was reviewed on #211.
…server' into test-issue-203-credential-free-live-calls
…tial-free-live-calls
…tial-free-live-calls
…s on short bodies Script newest-first and older versions for Reveal, and ensure AfterNBytes does not trigger when a request body ends before its threshold. Part of #203
There was a problem hiding this comment.
🟡 Changes recommended
The AfterNBytes tests do not verify one-shot behavior across requests on the same transport or response-byte counting, allowing regressions in both behaviors to pass unnoticed.
2 open issues
What changed in this PR
2 Important, 0 Nit, 0 Pre-existing.
Adds credential-free coverage for previously live-only B2 calls and transport fault helpers, supporting #203 and the test-integrity audit in #97. Production code and live tests remain unchanged.
Changes:
- Adds wire tests for hide/reveal, download authorization, key listing/deletion, and large-file cancellation.
- Tests upload error classification, cancellation, deadlines, TLS rejection, and transport helpers.
- Records requirements, scope, and verification plans.
Checked the supplied changes against the requirements and transport implementation; tests were not run during this review. A human code owner must review the base-layer wire and authorization coverage.
| File | Description |
|---|---|
x/transport/transport_test.go |
Adds credential-free tests for transport fault helpers. |
intent/credential-free-live-calls/spec.md |
Defines behavior and coverage requirements. |
intent/credential-free-live-calls/plan.md |
Records implementation order and mutation checks. |
intent/credential-free-live-calls/intent.md |
Establishes the test-only scope and constraints. |
base/live_only_calls_test.go |
Covers upload faults, context errors, and untrusted certificates. |
b2/live_only_calls_wire_test.go |
Verifies public API requests and response mappings. |
Review effort: Balanced
david-santacruz-backblaze
left a comment
There was a problem hiding this comment.
The expiry fixture (b2/live_only_calls_wire_test.go:124) should be null, not 0, and the test locks in 1970 for "no expiry". The Hide, Cancel (:35,181) and Reveal no-op (:54) tests can pass for the wrong reason, so assert the specific error and that b2_list_file_versions was called. Please also show the Trigger failing under -race (#176) and add the missing break rows (R9, R10, the second case of R2 and R7, x/transport). Nits: plan.md:4 cites a "lean-code ladder" that doesn't exist, and intent.md is still draft.
…tial-free-live-calls
Pin the scripted service error and refusal paths, use the documented null expiry fixture without asserting the existing epoch mapping, and cover the AfterNBytes callback across uploads and response bodies. Record isolated red-to-green proofs for upload refusal, cancellation, deadlines, certificate verification and transport thresholds. Document the Trigger race under #176 and the deferred null-expiry mapping. Part of #203
|
@david-santacruz-backblaze Thanks for the detailed review. Every point is addressed in fe2843e and 11be84d, and the branch is synced with main:
|
david-santacruz-backblaze
left a comment
There was a problem hiding this comment.
Thanks for the fixes, the earlier review points all look addressed. Can we check for these issues before merge?
- R2: assert exactly one
b2_delete_file_versionand thatb2_list_file_versionswas called.srv.Lastwould pass on a double delete. - R4: with an empty disposition, also assert
Authorizationis still present in the URL. - #244: cite it in
spec.md, in theListKeystest comment and inintent.md(open questions say "None"). - R8 message: link a source for
more than one upload using auth token(restic/restic#1207) inbase/live_only_calls_test.go. - Stacking: confirm #191 has merged and state the base and merge order in the PR description.
Optional:
- The R9 deadline test (1s) and the
Stallcheck (elapsed < 500ms) depend on wall-clock time. I haven't seen either fail. - Paste the captured
-race -count=20output for theTriggerrace (#176).
|
@david-santacruz-backblaze Thanks. All five are addressed in 690a8f5 and 05d3911, and the branch is synced with main:
Optional items: the captured |

Context
Part of the test-integrity audit (#97). Several calls were exercised only by live tests, which skip without B2 credentials, so
go test ./...and CI never ran them:b2ContentDisposition;base.Action.x/transport, whose fault helpers blazer's own tests and downstream users rely on, had no test file at all. Credential-free statement coverage was 75.3%.Changes
Test-only, plus the change's
intent/credential-free-live-calls/folder (intent, spec, plan). No production code changes.b2/live_only_calls_wire_test.go(recording server, #205)Object.Hidesends oneb2_hide_filewithbucketIdandfileName, and a scripted error is returnedBucket.Revealdeletes the newest version when it is ahidemarker, and sends no delete when it is an uploadBucket.AuthTokensendsfileNamePrefixandvalidDurationInSecondswith no disposition, and returns the tokenObject.AuthURLsendsb2ContentDispositionand returns a URL whoseAuthorizationandb2ContentDispositionquery values matchClient.ListKeyssendsmaxKeyCount, maps name, ID and expiry, returns the cursor, sends it back asstartApplicationKeyId, and returnsio.EOFon an empty pageKey.DeletesendsapplicationKeyIdObject.Cancelsendsb2_cancel_large_filefor an unfinished file, and refuses an uploaded one without a requestbase/live_only_calls_test.go(httptest)400, "more than one upload using auth token"). That maps toAttemptNewUpload, as does a connection reset before any response. A context the caller cancelled or let expire staysPunt.context.Canceled, and a deadline returnscontext.DeadlineExceededwith the server observing the cancellationerrors.Asx/transport/transport_test.go(new). One test per helper: pass-through,FailureRate(0)andFailureRate(1),MatchPathSubstring,ResponsewithBody,Stall(returns at its duration, or as soon as the request context ends), andAfterNBytes.All coordination uses channels and context deadlines, with no fixed sleeps.
Split out or left out
ListKeysdropped key capabilities. The capabilities assertion failed on current code, so it was split into its own fix (fix(base): return key capabilities from ListKeys #228); R5 does not assert capabilities here.transport.Triggertest reproduces that issue's data race under-race, so it is left out.Verification
Each break was applied on its own, the test run, and the code restored. Every test passes once restored.
HideFilesends an empty file namefileName got "", want "gone"Revealinverts thehidecheckno request for b2_delete_file_versionb2ContentDisposition missing;got "", want "attachment…"ListKeysdrops the cursorstartApplicationKeyId missing, want "k-3"Key.Deletesends an empty IDapplicationKeyId got "", want "k-1"Cancelinverts the status checkpending is not an unfinished large file: startActionAction(…more than one upload using auth token…) = 3, want AttemptNewUploadAction(b2_upload_file: 0: EOF) = 2, want AttemptNewUploadResponsedrops its bodygot "", want "retry later"Stallignores the request contextreturned after 1.0005s, want before 500ms when the request context endsRevealdeletes when the newest version is an uploadb2_delete_file_version calls = 1, want 0Cancelallows an uploaded fileCancel uploaded file error = <nil>, want "complete is not an unfinished large file: upload";b2_cancel_large_file calls = 1, want 0context.WithoutCancel)cancelled request did not return: context deadline exceededcontext.WithoutCancel)deadline request did not return: context deadline exceededauthorization accepted an unknown certificate authorityAfterNBytescallback re-arms on each requesteffect calls after second upload and both responses = 2, want 1effect calls after short body and response = 0, want 1Revealdeletes the hide marker twiceb2_delete_file_version calls = 2, want 1AuthURLdropsAuthorizationwhen the disposition is emptyempty disposition Authorization = "", want "download-token"go test -race -count=5 -shuffle=on ./b2 ./base ./x/transportpasses withB2_APPLICATION_KEY_ID,B2_APPLICATION_KEY,B2_ACCOUNT_IDandB2_ACCOUNT_KEYunset.make cipasses.Stalltest passed even whenStallignored the context. That is fixed (theStallrow above) and re-verified. Its nit, that the spec claimed capabilities coverage, is corrected.Base and merge order
This PR is based on
main. It originally stacked on #191's shared recording server, which merged as #205, so there is no remaining dependency and it can merge on its own.Triggerrace evidence (#176)A temporary diagnostic (removed after recording) created a client with
WithFailures(nil, Trigger(ctx), Response(503)), cancelledctx, and sent 100 requests.go test -race -count=20reported:#176 fixes it (PR #251), so this PR adds no test that depends on
Trigger(ctx).Notes for reviewers
b2_list_keysfixture uses the documentedexpirationTimestamp: null.Key.Expires()maps it to 1970-01-01 instead of the zero time; that is a product bug, filed as fix(base): Key.Expires() returns 1970-01-01 for keys with no expiration #244. The test asserts the key's name and ID, not the wrong 1970 value.Triggerrace:x/transport.Triggerraces when a request runs after its context is cancelled; it reproduces with-race -count=20. Fix the x/transport data race and RoundTripper contract, the x/window divide-by-zero, and add tests #176 tracks it, so no test here depends onTrigger(ctx).Closes #203