Reusable engineering practices for short-lived bearer-token caches, expiry-race recovery, portable tests and transferring small reviewed changes into a restricted or disconnected Git checkout.
When minimising token requests is important, treat a file cache as valid while cache_mtime + expires_in > now. If the token is a JWT with an earlier numeric exp claim, use the earlier deadline. A configurable safety margin can be useful, but zero is a legitimate default when the API client handles the rare expiry race correctly.
Bind cached data to the token endpoint and client/configuration identity. Otherwise a valid-looking cache can be reused against the wrong issuer or environment. Cache the complete successful response when fields such as token_type and expires_in are needed later; never cache an error response.
The component that makes the protected API request owns retry behaviour. It should make the request with the cached token and, on the first HTTP 401 only, invalidate or bypass the cache, acquire and atomically save a replacement, and replay the original request once. A second 401 is returned normally. Do not loop.
Do not interpret HTTP 403 as token expiry: it usually represents authorisation, policy or subscription failure, so refreshing commonly adds load without changing the outcome. Likewise, transport and server errors need their own retry policy. Retrying a rejected POST is safest where the server has clearly rejected authentication before processing and the operation also has an idempotency key.
Use a separate lock file and an exclusive lock around check-and-refresh, then recheck the cache after acquiring the lock. Write the new JSON to a private temporary file in the same directory, close it successfully, and atomically rename it over the cache. Restrict cache and lock permissions and verify ownership/type on platforms that expose Unix metadata. Never print or log the token.
Do not make an expiry test sleep for the real lifetime. Write a fixture with expires_in=3600, set its modification time to 3601 seconds in the past using the language's timestamp API, and execute the normal code. Assert that the old token was not returned, exactly one refresh occurred, and the replacement was saved. This tests the production calculation in milliseconds rather than making the suite take an hour.
A fake HTTP executable or local loopback server is generally better than borrowing credentials from an unrelated provider. It can record the exact number of calls and return controlled success, 401, 403, malformed JSON and provider-error responses without network access or secrets. Use a local server for the full request/retry boundary: cached token gets 401, token endpoint supplies a replacement, replay succeeds; also verify second-401 termination and no refresh on 403.
To prove token reuse and eventual replacement without disclosing the token, log a short prefix of SHA-256(token), the cache source, remaining lifetime and UTC timestamp. The same token produces a stable fingerprint; refresh produces one visible change. Protect the log anyway and never include the raw token.
A test that calls chmod does not necessarily create different permission bits on a Windows-backed or mounted filesystem. Read the resulting mode and skip only the assertions whose prerequisite cannot be represented. Keep those security assertions active on the Unix deployment filesystem. This is more precise than skipping based only on the operating-system name.
Distinguish application dependencies from runner dependencies. If a test harness command is broken because an optional harness module is absent, running the Test::More-style file directly with Perl can still validate the application without installing unrelated packages. Preserve pipeline status when teeing output, for example with shell pipefail and PIPESTATUS.
For a small offline bundle, package genuinely new files in tar.gz and base64-encode the archive for text-safe transport. Represent modifications to existing files as unified diffs. The receiving script should: verify it is at the Git top level; verify payload checksums and archive paths; reject differing existing files; dry-run every patch; apply with patch; run tests; tee complete output; preserve the test exit code; and finish with git status.
Never silently overwrite a locally modified file. Make the initial bundle rerunnable when files are identical, and publish later corrections as numbered incremental patches against the previously installed state. Git remains the review and recovery mechanism; the transfer script should not reset, clean or revert unrelated work.
| Area | Minimum cases |
|---|---|
| Cache | Absent; fresh; exactly expired; stale; malformed; wrong endpoint/client identity; insecure type/permissions; concurrent refresh. |
| Token response | Success; missing token; missing/invalid lifetime; malformed JSON; provider error; transport failure; failed response not cached. |
| Protected API | Cached success; first 401 then refreshed success; second 401; 403; retry count exactly one; POST idempotency. |
| Observability | Stable fingerprint for reuse; changed fingerprint on refresh; no raw token in stdout/logs; clean interrupt; transient failure retained in log. |
| Portability | Unix permissions enforced where representable; capability-based skip elsewhere; direct test execution when the optional runner is unavailable. |
| Transfer | Checksum failure; unsafe archive member; collision with differing file; patch dry-run failure; test failure propagated; unrelated Git changes preserved. |