Skip to content

Modernize Smocker to a 2026 stack (mock format & API preserved)#320

Merged
gwleclerc merged 14 commits into
mainfrom
chore/modernize-2026
Jul 18, 2026
Merged

Modernize Smocker to a 2026 stack (mock format & API preserved)#320
gwleclerc merged 14 commits into
mainfrom
chore/modernize-2026

Conversation

@gwleclerc

@gwleclerc gwleclerc commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

Refreshes the whole toolchain and dependencies to a current, maintained stack while keeping two hard invariants: the mock format (YAML/JSON serialization, struct tags, matcher names, on-disk mocks.yml/history.yml/sessions.yml layout) and the HTTP/CLI API (routes, payloads, status codes, flag names, SMOCKER_* env vars) are unchanged. Guarded by byte-exact golden serialization tests, the venom API suite, and a Playwright UI non-regression suite.

✨ Features

  • Schema-complete mock editor with a form ⇆ YAML toggle — the visual editor now covers the whole mock format (all matchers, response/proxy delay, every HTTP method, path/method/body matchers, multi-value keys); switching to the form is gated on the YAML being a single schema-valid mock. "Create a new mock from entry" now opens the editor in place.
  • Shareable pagination — History/Mocks keep the page in the URL (?page/?page-size), with directional page scrolling.

🔧 Backend (Go)

  • Go 1.15 → 1.26; dropped unmaintained deps: logrus → log/slog, namsral/flag → stdlib flag, facebookgo/grace → stdlib graceful serve(), teris-io/shortid → crypto/rand.
  • Behavioral libs bumped under equivalence tests (echo, sprig, objx, smarty/assertions, gopher-lua/luar); yaml.v3 kept (goccy rejected — byte-incompatible).
  • Persistence hardening: resilient per-session loading (a missing file no longer drops every session) + bounded concurrency (fixes "too many open files").

🎨 Frontend

  • Parcel → Vite, React 16 → 19, react-router 5 → 7, redux/rxjs/io-ts → TanStack Query + zod, antd 4 → 6, CodeMirror 5 → 6, mermaid 8 → 11, lodash → es-toolkit; dropped axios/react-hot-loader. Jest → Vitest, ESLint → oxlint (TS 7 native), TS 3 → 7.
  • Restored antd 4 visual fidelity under antd 6 and themed the sequence diagram to the app palette.

📦 Build / CI / Tests

  • Canonical docs/mock.schema.json (draft 2020-12), validated against the fixtures in CI.
  • Byte-exact golden serialization tests; Playwright TNR (22 specs) wired into CI.
  • All generated output consolidated under build/; CI switched yarn → npm + oxlint + vitest.

Mock edit & delete (issues #299, #266, #303) lives in a separate branch (feat/mock-edit-delete) stacked on top of this one.

gwleclerc and others added 14 commits July 17, 2026 18:03
Backend/toolchain modernization with the mock format and HTTP/CLI API kept
byte-identical (guarded by new golden serialization tests + the venom suite).

- go 1.15 -> 1.26; io/ioutil -> io; rand.Seed -> math/rand/v2
- drop facebookgo/grace -> stdlib graceful shutdown (signal.NotifyContext +
  http.Server.Shutdown + errgroup), TLS preserved (SIGHUP restart dropped)
- drop namsral/flag -> stdlib flag (flag names + SMOCKER_* env preserved)
- drop logrus -> log/slog
- drop teris-io/shortid -> zero-dep crypto/rand base62 id (server/types/id.go)
- bump echo v4.15, sprig v3.3, objx v0.5, smarty/assertions v1.16, gopher-lua,
  gopher-luar, yaml.v3 v3.0.1 (goccy evaluated, rejected: alters output)
- golangci-lint config v1 -> v2 (Makefile pin 2.12.2), lint clean
- Dockerfile GO_VERSION 1.26
- add golden serialization tests locking the exact YAML/JSON mock format

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tack

Full frontend overhaul with the HTTP API the client consumes kept identical.

Build/tooling:
- Parcel -> Vite 8 (Go index.html template + ./assets/ layout preserved)
- yarn -> npm; TypeScript -> 7; ESLint -> oxlint (typescript-eslint is
  incompatible with the native TS compiler); Jest -> Vitest; Node 24
- pin exact dependency versions

Libraries:
- redux + redux-observable + rxjs + io-ts/fp-ts -> TanStack Query + zod
- React 16 -> 19 (createRoot), react-router 5 -> 7, antd 4 -> 6,
  CodeMirror 5 -> 6, mermaid 8 -> 11, lodash -> es-toolkit
- drop axios and react-hot-loader

Verified: tsc --noEmit, vite build, vitest, oxlint all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tion

Move the serialization golden fixtures out of server/types/testdata/golden into
tests/serialization (alongside the other test fixtures under tests/), and rename
server/types/serialization_golden_test.go to serialization_test.go. The test stays
in package types so it keeps running under `go test ./server/...` (make test).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
21 framework-agnostic specs (tests/e2e/smocker.spec.ts) locking the observable
behaviour of the web UI: shell, routing, links, all pages, navigation, the mock
editor (open/cancel, prefill, create via form and raw YAML), lock/unlock,
autorefresh, session new/select/reset/rename, and file import. Assertions target
stable text / hrefs / URLs / SVG (never framework DOM), so the same suite passes
against the pre-migration and post-migration UIs. Output goes under build/e2e.

Requires @playwright/test (in devDependencies) and a running server pointed at
via SMOCKER_E2E_URL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…/vitest

- Makefile: coverage -> build/coverage, integration sessions -> build/sessions,
  integration test binary -> build/smocker.test, clean removes build/ only, and
  the release tarball packages only `smocker` + `client` (not test/runtime output).
- Playwright output dir -> build/e2e (see playwright.config.ts).
- sonar coverage report path -> build/coverage/cover.out.
- CI: yarn -> npm (cache + `npm ci`), lint via `npm run lint` (oxlint), unit tests
  via `npm test` (vitest); ::set-output -> $GITHUB_OUTPUT.
- .gitignore: everything generated now lives under build/, so ignore just build/
  and node_modules/ (plus editor/OS files); drop the stale root-level entries.

The repository root now stays clean: builds, coverage, e2e reports and runtime
sessions all land under build/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… use

Two robustness fixes for the recurring persistence reports (#284, #292, #318):

- Make LoadSessions best-effort per session. Previously a single missing or
  unreadable per-session file (e.g. a partial session directory after a
  mid-write restart or a K8s volume race) made the whole load fail and dropped
  EVERY persisted session. Now each session's history and mocks load
  independently: a missing/corrupt file is logged and skipped, other sessions
  and the session name/metadata still load.
- Bound persistence concurrency with errgroup SetLimit(16) on both the load and
  the store paths, so a large number of sessions can no longer exhaust the
  process's file descriptors ("too many open files").

Tests (server/services/persistence_test.go, pass under -race):
- TestLoadSessionsResilientToMissingFiles: a partial session dir no longer wipes
  the others.
- TestStoreAndLoadManySessions: round-trips more sessions than the limit without
  deadlocking or losing data.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a `make test-e2e` target that builds the client and backend, serves the
built client through smocker (seeded with tests/sessions), runs the Playwright
suite against it, then stops the server. Wire it into CI as a dedicated `e2e`
job (installs the Chromium browser via `npx playwright install --with-deps`)
and make `deploy` depend on it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add docs/mock.schema.json (draft 2020-12), derived from server/types. It is
validated on the Go side by server/types/schema_test.go (santhosh-tekuri): every
fixture under tests/data must match it, and clearly-invalid mocks are rejected.
README documents it and the yaml-language-server $schema usage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
go get for installing binaries was removed in Go 1.17, so 'make start-caddy'
could not install Caddy on Go 1.26. Use 'go install …/caddy@$(CADDYVERSION)'
(v2.8.4), consistent with the venom/golangci-lint pinned installs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Restore the antd 4 look lost in the antd 6 migration: base font/family, code
line numbers in read-only previews, sidebar/navbar/page-header spacing, and the
History filter rendered as an inline link. Theme the mermaid sequence diagram
with the app palette and make the Visualize customize form a compact inline row.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rework mock creation and make the visual editor cover the whole mock format:

- Extract the creation drawer to NewMock and mount it on both Mocks and History;
  History's 'Create a new mock from entry' now opens it in place (no navigation).
- Toggle between the visual form and raw YAML: the YAML is the source of truth,
  and switching to the form is gated on it being a single mock valid against the
  schema (ajv). Validate before saving and keep the drawer open on error so the
  mock is never lost. Drop the now-redundant read-only preview.
- Complete the form vs the schema: response/proxy delay (fixed or random), all 17
  matchers, every HTTP method plus method-as-matcher, path and body matchers for
  all methods, multi-value query/header keys, numeric/bool values coerced to
  strings. Reverse conversion (mock -> form) with round-trip tests.
- Keep pagination in the URL (?page/?page-size) so a page is shareable, with
  directional page scroll; the mock link preserves ?session and back returns to
  the list.
- Only sync the ?session param on /pages/* routes so it no longer clobbers the
  '*' -> /pages/history redirect (which left the root on a blank page).

Tests: vitest 64 (conversion, rendered component, YAML validation, utils) and the
Playwright TNR grows to 22. New deps (pinned, audit-clean): ajv (runtime), and
@testing-library/react + user-event (dev).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reducing crypto/rand bytes with % over a 62-char alphabet favored the first 8
characters (256 % 62 = 8). Reject bytes >= 248 (the largest multiple of 62 that
fits in a byte) so every character is equally likely. IDs stay opaque and the
persisted format is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Run the repo's prettier config over the client so the committed frontend matches
'npm run format' (no behavior change).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The lint job installed golangci-lint via the install.sh from the master branch,
whose embedded checksums drifted from the v2.12.2 release tarball, so the download
failed checksum verification ("did not verify"). Fetch install.sh from the
matching v$(GOLANGCILINTVERSION) tag instead, so its checksums always match the
release being installed. golangci-lint run itself is clean (0 issues).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@gwleclerc
gwleclerc force-pushed the chore/modernize-2026 branch from 93a695f to 5274d5e Compare July 18, 2026 03:43
@gwleclerc
gwleclerc merged commit 15fab70 into main Jul 18, 2026
5 checks passed
@gwleclerc
gwleclerc deleted the chore/modernize-2026 branch July 18, 2026 03:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant