Test your app

Storage and restarts

Where Calendarpit stores data, how to choose between SQLite and in-memory mode, and what persists across restarts and resets.

Calendarpit stores workspace state in a local SQLite file by default. For throwaway test sessions, it can keep state in memory instead.

Choosing a storage mode

By default, Homebrew and standalone executable installs save to .calendarpit/calendarpit.sqlite in the directory where you start Calendarpit. Run it from your project directory to keep that project's data together.

To choose a file or :memory:, use --database <path> or CALENDARPIT_DATABASE. CLI flags override environment variables. Check prerequisites and publication status in Installation.

# Temporary E2E session; no database files are written
calendarpit serve --database :memory: --port 0

# Choose a persistent workspace
calendarpit serve --database ./.calendarpit/manual.sqlite

Modes

  • SQLite file (default): Saves changes after each request that modifies state. It also saves after each webhook delivery finishes. Relative paths resolve against the working directory.
  • In-memory (:memory:): Writes no database or lock files. All state is discarded when the process exits.
  • Docker: The image sets CALENDARPIT_DATABASE=/data/calendarpit.sqlite.
    • To retain data when the container is removed and re-created, mount a named volume with -v calendarpit-data:/data.
    • For a temporary session, pass -e CALENDARPIT_DATABASE=:memory:.
    • The Compose example also uses a named volume. docker compose down preserves data, while docker compose down -v deletes it.

Check the active mode

You can see the active storage mode and path in three places:

  • Startup log: The CLI prints Data <absolute-path> (or Data :memory:). When the server entry loads, it also prints Calendarpit data: <path or :memory:>.
  • Admin UI: The sidebar footer shows SQLite or In-memory. Settings → Test data describes the mode and shows the path (or :memory:).
  • Management API: GET /__calendarpit/health returns storage: "sqlite" | "memory". GET /__calendarpit/state returns storage: { mode, path? }.

What survives a SQLite restart

Reopening an existing SQLite file restores saved data. Short-lived flow state is dropped.

On SIGINT or SIGTERM, Calendarpit:

  1. Aborts in-flight webhook deliveries.
  2. Saves state.
  3. Releases the database lock.

Each kind of state behaves as follows after a restart:

StateSQLite restart behavior
Users, calendars, events, OAuth clientsRestored, including event IDs, timestamps, and etags
Calendar-list settingsRestored; cleared by a reset or fixture load
Reset baseline (startup fixture)Original seed retained
Connections (grants), access and refresh tokensRestored
Watch channelsRestored only under the conditions below
Request history, delivery log, notificationsLatest 200 entries of each restored
Queued or in-flight webhook deliveriesMarked cancelled; never replayed automatically
Sync tokens, calendar tombstonesRestored; cleared by a reset or fixture load
Pending consents, authorization codes, page tokensDiscarded; restart the flow or list query

Details for some rows:

  • Calendar-list settings include per-user overrides, colors, hidden/selected, reminders, and removed entries.
  • Reset baseline: --config and CALENDARPIT_CONFIG file contents are ignored.
  • Tokens and connections:
    • Access tokens keep their original expiry.
    • Refresh tokens do not expire.
    • Revoked connections stay revoked.
  • Watch channels are restored if all of these hold. Restored channels keep their message numbers.
    • The channel is unexpired.
    • The connection is active.
    • The callback host is still allowed.
  • Page tokens are the calendarList and events cursors.

After a restart, refetch calendar state in your app. This reconciles watch notifications interrupted by shutdown.

Seed baseline, fixtures, and reset

A startup fixture seeds only a new database. The startup fixture comes from --config, CALENDARPIT_CONFIG, or the built-in default.

  • Existing SQLite file: Reopens saved data and the original baseline.
    • If --config or CALENDARPIT_CONFIG is set, the fixture's contents are ignored and do not overwrite the database.
    • The file must still exist and contain valid JSON.
  • In-memory mode: Every start is a new database. The startup fixture is applied, and any omitted IDs are regenerated, on every start.
  • Reset (POST /__calendarpit/reset): Restores the original startup baseline, even after restarts.
    • It clears connections, tokens, codes, consents, watch channels, calendar-list settings, sync tokens, page tokens, calendar tombstones, request history, notifications, and deliveries.
    • It saves immediately.
    • It does not delete the SQLite file or modify the fixture file.
  • Load fixture (POST /__calendarpit/fixtures): Replaces current data and clears runtime state without changing the reset baseline. Subsequent resets still restore the original seed. See Test control API.

Locking, files, and parallel runs

One process per file

Only one Calendarpit process can use a SQLite file at a time.

  • While running, Calendarpit holds an exclusive lock on <db>.lock.
  • A second process on the same file fails at startup with Database is already in use: <path>. Use a separate path or :memory:. The CLI prefixes this with Calendarpit: .
  • The OS releases the lock on exit, including after a crash.

Give each parallel test worker --database :memory: or a separate SQLite file.

Sidecar files and permissions

SQLite runs in WAL mode. While running, Calendarpit creates these sidecar files next to the database:

  • <db>-wal
  • <db>-shm
  • <db>.lock
  • <db>.lock-journal

The database, -wal, -shm, and .lock files use mode 0600. Any database directory Calendarpit creates uses mode 0700.

Keep sidecar files with the database while running. Stop the server before copying a database file.

Keep the database out of Git

The database contains local test credentials and client secrets. Keep it out of Git.

In this repository, Git ignores:

  • .calendarpit/
  • The *.sqlite, *.sqlite-wal, *.sqlite-shm, and *.sqlite.lock patterns
  • <db>.lock-journal, but only inside .calendarpit/

On this page