Found while implementing #10681. Filed unassigned; deliberately not fixed in that PR — rewriting the internals of a fixture that pins an unrelated card (#3624 / #3690) is not that card's scope, and the duplication is inert today.
What
#10681 needed a real TOTP to confirm a 2FA enrolment (generate-backup-codes refuses an unconfirmed one with 400 TWO_FACTOR_NOT_ENABLED). Rather than inline a second copy, it extracted packages/qa/dogfood/test/totp.ts exporting base32Decode, totp and secretFromTotpUri.
packages/qa/dogfood/test/two-factor-lockout.dogfood.test.ts still defines its own private base32Decode and totp — byte-for-byte the same algorithm, since the new module was extracted from it. So the package now has two spellings of RFC 6238 where it previously had one.
Why this is small, and why it is still worth recording
Both copies are correct and neither is a behaviour risk: RFC 6238 does not drift, and the hand-rolled implementation is deliberate in both places for the reason the lockout file already documents — @better-auth/utils/otp is a transitive dependency, and taking a direct dependency on it just to generate six digits would tie these fixtures to an internal package's resolution. That rationale is unchanged and should survive any consolidation.
The cost is the ordinary one: the next author needing a TOTP copies whichever file they happen to open, and a third copy appears. This is the same shape as #10348 one layer down — N copies of a helper is the state that grows.
Suggested shape
Point two-factor-lockout.dogfood.test.ts at ./totp.js and delete its two local functions, keeping its header note about why the implementation is hand-rolled (move it to the module if it is not already there — it is, in substance). Purely mechanical, no assertion changes; the file's own suite is the check.
Note secretFromTotpUri in the new module also subsumes the URI-parsing the lockout file does inline (new URL(totpURI.replace('otpauth://','https://')) plus the secret param lookup and its toBeTruthy guard), so that block can collapse too.
Generated by Claude Code
Found while implementing #10681. Filed unassigned; deliberately not fixed in that PR — rewriting the internals of a fixture that pins an unrelated card (#3624 / #3690) is not that card's scope, and the duplication is inert today.
What
#10681 needed a real TOTP to confirm a 2FA enrolment (
generate-backup-codesrefuses an unconfirmed one with400 TWO_FACTOR_NOT_ENABLED). Rather than inline a second copy, it extractedpackages/qa/dogfood/test/totp.tsexportingbase32Decode,totpandsecretFromTotpUri.packages/qa/dogfood/test/two-factor-lockout.dogfood.test.tsstill defines its own privatebase32Decodeandtotp— byte-for-byte the same algorithm, since the new module was extracted from it. So the package now has two spellings of RFC 6238 where it previously had one.Why this is small, and why it is still worth recording
Both copies are correct and neither is a behaviour risk: RFC 6238 does not drift, and the hand-rolled implementation is deliberate in both places for the reason the lockout file already documents —
@better-auth/utils/otpis a transitive dependency, and taking a direct dependency on it just to generate six digits would tie these fixtures to an internal package's resolution. That rationale is unchanged and should survive any consolidation.The cost is the ordinary one: the next author needing a TOTP copies whichever file they happen to open, and a third copy appears. This is the same shape as #10348 one layer down — N copies of a helper is the state that grows.
Suggested shape
Point
two-factor-lockout.dogfood.test.tsat./totp.jsand delete its two local functions, keeping its header note about why the implementation is hand-rolled (move it to the module if it is not already there — it is, in substance). Purely mechanical, no assertion changes; the file's own suite is the check.Note
secretFromTotpUriin the new module also subsumes the URI-parsing the lockout file does inline (new URL(totpURI.replace('otpauth://','https://'))plus thesecretparam lookup and itstoBeTruthyguard), so that block can collapse too.Generated by Claude Code