Background
cli-core#49 published shared test scaffolding via @doist/cli-core/testing. twist adopted the mechanical part — createTestProgram + captureConsole — in #251, but deliberately scoped out the stateful token-store fake (matching todoist-cli#361's scope).
This issue tracks the remaining, non-mechanical half.
What
Replace the hand-rolled interaction mocks in src/commands/account/account.test.ts (and where applicable src/commands/auth/auth.test.ts, src/lib/auth.test.ts) with cli-core's stateful fake:
buildTokenStore({ entries, matchAccount }) / buildSingleEntryStore(...) — returns a real in-memory TokenStore + spies + mutable state.entries.
- Ingen account fixtures (
alanGrant / ellieSattler / ianMalcolm) instead of hand-built account objects.
Today these tests hand-roll a storeMocks object of bare vi.fn()s and re-implement ref resolution inline (setDefault/clear via mockImplementation(async (ref) => …), account.test.ts:51-56). That logic can drift from production.
Why this is twist-specific
matchAccount was added to buildTokenStore in cli-core#49 specifically for twist — twist resolves refs by numeric id + case-insensitive label, not cli-core's default id/email/label. twist is the only conceivable caller of matchAccount in any repo (cli-core and todoist both use the default). If twist never adopts, matchAccount is a parameter no code anywhere passes — see the "delete it as YAGNI" alternative below.
Watch-out (don't regress)
Current tests assert the exact ref string reaching the store, e.g. expect(storeMocks.setDefault).toHaveBeenCalledWith('alan grant') (account.test.ts:233). That proves twist's command layer normalizes (lowercases) the ref before calling the store. A stateful fake + matchAccount can absorb that normalization — keep an explicit assertion that the command layer normalizes, so we test prod's matcher, not our mirror of it.
Decision fork
- Adopt →
matchAccount gets its first real caller; twist converges onto shared infra; bespoke inline resolution logic deleted.
- Decline → flag back to cli-core to remove
matchAccount as YAGNI (keep buildTokenStore; cli-core uses it internally).
Recommended: fold into the next PR that already touches account.test.ts/auth.test.ts rather than a dedicated rewrite.
Refs: Doist/cli-core#49, #251
Background
cli-core#49 published shared test scaffolding via
@doist/cli-core/testing. twist adopted the mechanical part —createTestProgram+captureConsole— in #251, but deliberately scoped out the stateful token-store fake (matching todoist-cli#361's scope).This issue tracks the remaining, non-mechanical half.
What
Replace the hand-rolled interaction mocks in
src/commands/account/account.test.ts(and where applicablesrc/commands/auth/auth.test.ts,src/lib/auth.test.ts) with cli-core's stateful fake:buildTokenStore({ entries, matchAccount })/buildSingleEntryStore(...)— returns a real in-memoryTokenStore+ spies + mutablestate.entries.alanGrant/ellieSattler/ianMalcolm) instead of hand-built account objects.Today these tests hand-roll a
storeMocksobject of barevi.fn()s and re-implement ref resolution inline (setDefault/clearviamockImplementation(async (ref) => …),account.test.ts:51-56). That logic can drift from production.Why this is twist-specific
matchAccountwas added tobuildTokenStorein cli-core#49 specifically for twist — twist resolves refs by numeric id + case-insensitive label, not cli-core's default id/email/label. twist is the only conceivable caller ofmatchAccountin any repo (cli-core and todoist both use the default). If twist never adopts,matchAccountis a parameter no code anywhere passes — see the "delete it as YAGNI" alternative below.Watch-out (don't regress)
Current tests assert the exact ref string reaching the store, e.g.
expect(storeMocks.setDefault).toHaveBeenCalledWith('alan grant')(account.test.ts:233). That proves twist's command layer normalizes (lowercases) the ref before calling the store. A stateful fake +matchAccountcan absorb that normalization — keep an explicit assertion that the command layer normalizes, so we test prod's matcher, not our mirror of it.Decision fork
matchAccountgets its first real caller; twist converges onto shared infra; bespoke inline resolution logic deleted.matchAccountas YAGNI (keepbuildTokenStore; cli-core uses it internally).Recommended: fold into the next PR that already touches
account.test.ts/auth.test.tsrather than a dedicated rewrite.Refs: Doist/cli-core#49, #251