Skip to content

refactor: split oversized mock API into focused domain modules (Closes #383) - #397

Merged
Lakes41 merged 1 commit into
Adamantine-guild:mainfrom
vijay11149:refactor/split-mock-api
Aug 20, 2026
Merged

refactor: split oversized mock API into focused domain modules (Closes #383)#397
Lakes41 merged 1 commit into
Adamantine-guild:mainfrom
vijay11149:refactor/split-mock-api

Conversation

@vijay11149

Copy link
Copy Markdown
Contributor

Summary

Closes #383lib/api/mock.ts had grown to ~1,800 lines and contained every mock API domain (the in-memory store, persistence orchestration, SIWE/session simulation, member/resource/policy reads, webhooks, approvals, social graph, moderation, governance, analytics, fault-injection controls, and developer scenario presets) in a single file. Any structural mistake anywhere in it blocked the entire API layer from compiling, and the blast radius extended to consumers such as lib/api/index.ts and components/nav.tsx.

This PR reorganizes the mock implementation into 12 small, focused modules under lib/api/mock/, each with a single, clearly documented responsibility, and reduces lib/api/mock.ts to a ~400-line aggregation point that composes MockAccessApi and preserves every existing export. No API behavior is removed and no consumer import needs to change.

Before / After

Before After
lib/api/mock.ts 1,807 lines, all domains mixed 409 lines (aggregator + thin MockAccessApi forwards)
Domain modules — (everything in one file) 12 modules under lib/api/mock/, each 34–413 lines
Compile errors in the file 9 (invalid_state ×5, Cannot assign to import ×3, legacy-state shape ×1) 0
Public exports MockAccessApi, getCommunityState(), communityStates, developer controls, etc. All preserved (re-exported)

New module layout (lib/api/mock/)

Module Responsibility
fixtures.ts (edited) Seeded fixture data (communities, 50k members, resources, policies, webhook events, connections, reports) + the mutable social/moderation stores with ESM-safe setters
state.ts (new) In-memory per-community store: communityStates, getCommunityState(), ensureAddress(), initPromise, schedulePersist(), createMockStreamEvent(), beforeunload persistence
session.ts (new) SIWE endpoints (getNonce/siweVerify/siweRefresh/siweLogout/getSessionStatus), nonce stores/TTL, and the cookie-auth-mode session-cookie simulation
core.ts (new) Meta/version, community, resource & policy reads, wallet verification
members.ts (new) Member reads and the self-service profile mutation (with ownership + validation semantics)
analytics.ts (new) Admin analytics summary endpoint + the AnalyticsDataSource surface
webhooks.ts (new) Live webhook feed, replayEvent, standalone replayMockEvent(), admin event log pagination
approvals.ts (new) Role/policy mutations (assignRole/removeRole/updatePolicy) + the multi-approval pending-action flow
social.ts (new) Connections, privacy settings, blocking/unblocking
moderation.ts (new) Moderation report queue and state transitions
governance.ts (new) Proposals (create/update/publish/close/resolve/delete) and weighted voting
controls.ts (new) Fault-injection knobs (setMockRoleMutationFailure, setMockResourceFetchFailure, setMockResourceFetchDelay) and the API-version override
scenarios.ts (new) Scenario presets (applyMockScenario) and resetMockData()

How MockAccessApi composes the modules

Each class method forwards to its domain module through a fresh per-call MockApiContext (address, communityId, and the current authMode read from config at call time). Building the context per call — rather than capturing it at construction — is deliberate: it keeps config-derived values fresh across module reloads, which the cookie-mode session tests (test/mock-cookie-session.test.ts, test/session-cookie-mode.test.ts) rely on when they invalidate the lib/config and lib/api/mock require caches.

No circular imports exist: the dependency graph is strictly layered — errors/fixturesstate → domain modules → mock.ts aggregator.

Behavioral parity & correctness

  • Every method body was moved verbatim — same state access, same error codes/messages, same delays, same persistence triggers. A method-coverage comparison confirmed no public method was dropped or altered.
  • All historical exports preserved, so these continue to work unchanged:
    • lib/api/index.ts (via mock-boundary.ts)
    • lib/api/mock-boundary.ts
    • lib/billing/mock.ts (getCommunityState)
    • 20+ test files importing MockAccessApi, resetMockData, applyMockScenario, replayMockEvent, setMockRoleMutationFailure, setMockResourceFetchFailure, setMockResourceFetchDelay, setMockMetaVersion directly from lib/api/mock
  • replayMockEvent vs MockAccessApi.replayEvent kept distinct — the standalone dev-tool export still applies member-store side effects; the class method does not (matching the original).

Latent bugs fixed in the process (within the refactored file)

  1. invalid_state not a valid ApiErrorCode — 5 governance methods constructed ApiErrors with code: 'invalid_state', which failed the typecheck and prevented compilation. Added 'invalid_state' to the union in lib/api/errors.ts (behavior preserving — runtime code values unchanged).
  2. Legacy persisted-state load missing proposals/votes — the backward-compat branch of the store initializer didn't satisfy the CommunityState shape. It now initializes both to {}, matching getCommunityState()'s defaults.
  3. blockMember crashed at runtime — reassigning an imported binding (mockConnections = mockConnections.filter(…)) throws under both ESM semantics and the CJS test build (“Cannot set property mockConnections … which has only a getter”). The original code failed on main as soon as the social-graph domain was exercised in a test build. Reassignment now happens inside the owning module (fixtures.ts) via explicit setters (setMockConnections/setMockPrivacySettings/setMockReports), restoring the intended behavior.
  4. MOCK_META_VERSION_OVERRIDE / control flags remain module-scoped inside controls.ts, with read-only accessors, so resetMockData() semantics are unchanged (meta override intentionally not reset, matching the original).

Verification

Check Result
tsc --noEmit (root) Mock-layer errors: 9 → 0. Total pre-existing app errors: 93 → 84 (all remaining are unrelated, pre-existing issues in lib/wallet, app/…, components/…).
tsc -p test/tsconfig.json Mock/test-layer errors: gone (lib/api compiles cleanly).
ESLint (next lint --file …) Clean on every changed/new file.
Mock-domain test batch (302 tests) 301 pass; the 1 miss is a flaky 40 ms setTimeout timing assertion (mock-controls), passing on re-run with an identical code path to main.
Full suite (784 tests) 779 pass / 5 fail — the 5 failures were verified pre-existing on main via an A/B stash run: analytics-flag (wagmi http import), api-mock-boundary (missing setup-env alias registration), check-env/portfolio-analytics (env-dependent), siwe-threat-model (docs/env). None touch the mock API.
End-to-end smoke test Exercised governance (proposal lifecycle incl. draft→active→closed→resolved, voting weights, re-vote rejection, draft delete), social graph (privacy private/public, block/unblock, connection accept/reject), moderation (report state transitions), approvals (2-of-2 pending-action flow), webhooks (replay, pagination), analytics (summary + data source), and scenarios (multiple-communities). All passed — the same script crashes on main at blockMember, demonstrating the latent social-graph bug.
next build (root) Fails only on pre-existing errors (lint react/no-unescaped-entities, SiweAuthContextType type errors) that exist identically on main; the refactored mock layer is not among them.

Design decisions worth flagging for review

  1. MockApiContext carries authMode — session functions read the cookie-vs-bearer flag from the call context instead of importing config themselves. This sidesteps a stale-module-binding problem that would break the cookie-mode tests, and keeps config a single, fresh read site (mock.ts).
  2. Composition over mixins — splitting MockAccessApi via standalone domain functions + thin forwards was chosen over TS mixins: it's type-safe, keeps the class declaration readable, avoids private-field plumbing (#nonceStore passes explicitly to session functions), and makes each domain independently testable.
  3. scenarios.ts re-exports through mock-boundary.ts unchangedlib/api/index.ts's public developer surface is byte-for-byte identical.
  4. Docs updateddocs/mock-api-boundaries.md now documents the module table, the new fixture mutation rules (ownership of let bindings), and updated behavior-implementation guidance.

Files changed

Modified:

  • lib/api/mock.ts — rewritten as aggregator/MockAccessApi composition (1,807 → 409 lines)
  • lib/api/mock/fixtures.ts — added mutable-store setters (keeping the original let bindings)
  • lib/api/errors.ts — added 'invalid_state' to ApiErrorCode
  • docs/mock-api-boundaries.md — documents the new module structure and rules

Added:

  • lib/api/mock/{state,session,core,members,analytics,webhooks,approvals,social,moderation,governance,controls,scenarios}.ts (12 files)

Unchanged (by design):

  • lib/api/index.ts, lib/api/mock-boundary.ts, lib/billing/mock.ts — no consumer changes required

Out of scope / follow-ups

  • The remaining pre-existing root typecheck errors (SiweAuthContextType, wallet bundle, etc.) predate this PR and are tracked separately.
  • lib/api/live.ts (~1,400 lines) could benefit from the same domain-module treatment in a follow-up.
  • Governance/social/moderation currently lack dedicated unit tests; a follow-up could codify the smoke-tested behaviors.

Testing commands run: tsc --noEmit · tsc -p test/tsconfig.json --skipLibCheck · next lint (changed files) · node --test (full suite + focused mock batch) · end-to-end smoke script · next build --no-lint (verify no mock-layer compile errors).

…Adamantine-guild#383)

Organize the ~1,800-line lib/api/mock.ts into focused modules under
lib/api/mock/ (state, session, core, members, analytics, webhooks,
approvals, social, moderation, governance, controls, scenarios) and turn
mock.ts into a thin aggregator that composes MockAccessApi and re-exports
every historical symbol, so no consumer imports change.

Also fix compile errors that lived in this file: add 'invalid_state' to
ApiErrorCode (used by governance methods), complete the legacy
persisted-state load with proposals/votes, and route social-store
reassignment through owning-module setters (fixes a latent blockMember
runtime crash under ESM/CJS module semantics).

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@Lakes41
Lakes41 merged commit 43bccbe into Adamantine-guild:main Aug 20, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Split oversized mock API implementation into focused modules

2 participants