Fixtures
Define test users, calendars, events, and OAuth clients in a JSON fixture file.
A fixture is a JSON file that defines initial test data for Calendarpit: test users, calendars, events, and OAuth clients. Use a fixture to start your local server or test suite with a known dataset.
Set explicit id values in a fixture to give your tests fixed user, calendar, event, and OAuth client IDs. Your test assertions can then check exact IDs instead of querying for generated values.
Load a fixture at startup
Pass a fixture file path with the --config CLI flag or the CALENDARPIT_CONFIG environment variable.
If you omit both, Calendarpit loads a built-in default fixture. It matches fixtures/default.json in the repository and contains:
- Users
alexandsam - Calendars
alex-work,alex-personal, andsam-work - One
planningevent - The
sample-appOAuth client
Start from the CLI
Save the JSON example below as calendarpit.fixture.json and edit it for your test. Check prerequisites and publication status in Installation. Then start Calendarpit from the directory that contains the file:
calendarpit serve \
--database :memory: --config ./calendarpit.fixture.jsonRelative --config and CALENDARPIT_CONFIG paths resolve from the directory where you start Calendarpit.
Start in Docker or Compose
In Docker, mount the existing file read-only and use its container path. This command uses POSIX shell syntax:
docker run --rm -p 127.0.0.1:4173:4173 \
--mount "type=bind,source=$PWD/calendarpit.fixture.json,target=/fixtures/startup.json,readonly" \
-e CALENDARPIT_CONFIG=/fixtures/startup.json \
-e CALENDARPIT_DATABASE=:memory: \
ghcr.io/iktakahiro/calendarpit:latestFor Compose, make two changes to the service:
- Add
./calendarpit.fixture.json:/fixtures/startup.json:roto itsvolumes. - Set
CALENDARPIT_CONFIG: /fixtures/startup.jsonin itsenvironment.
The host file must be readable by the container's non-root user. See Run and configure for startup options.
When the startup fixture applies
A startup fixture seeds only a new, empty database:
--database :memory:mode: The database is new on every process start, so the fixture always applies.- SQLite file with saved data: Calendarpit does not apply the fixture and prints no warning. It loads the saved data and the original reset baseline. The saved data includes per-user calendar-list settings and deleted-calendar tombstones.
Even when Calendarpit skips the fixture, the path you pass must still point to a readable JSON file, or startup fails. Its contents are not validated in this case.
See Storage and restarts for details.
Load or reset at runtime
Use the test control API to change data between tests:
POST /__calendarpit/fixturesloads a different fixture at runtime.POST /__calendarpit/resetresets to the startup baseline. It restores the startup fixture, including default calendar-list entries with their startup etags.
Both endpoints clear:
- OAuth grants and tokens
- Per-user calendar-list settings
- Deleted-calendar tombstones
- Watch channels
- Deliveries
- Request history
- Sync tokens
- Notifications
GET /__calendarpit/state omits client secrets, calendar-list settings, and tombstones. You cannot reload its output as a fixture. See Test control API.
Root structure and example
The root value of a fixture file must be a JSON object. It accepts four optional array keys: users, calendars, events, and clients. An empty object ({}) is a valid empty fixture.
- Each key, if present, must be an array. Otherwise validation fails with
"<key> must be an array.". - Any other key fails validation with
"Unsupported field: <key>"(reasonunsupportedFeature). This applies to the root object and to every user, calendar, event, and client entry.
{
"users": [{ "id": "alex", "name": "Alex", "email": "alex@example.test" }],
"calendars": [
{
"id": "work",
"userId": "alex",
"summary": "Work",
"timeZone": "Asia/Tokyo",
"primary": true
}
],
"events": [
{
"id": "meeting",
"calendarId": "work",
"summary": "Planning",
"start": { "dateTime": "2026-10-19T10:00:00+09:00" },
"end": { "dateTime": "2026-10-19T11:00:00+09:00" }
}
],
"clients": [
{
"id": "sample-app",
"secret": "local-sample-secret",
"redirectUris": ["http://127.0.0.1:4175/callback"]
}
]
}ID rules and validation
IDs are optional for users, calendars, events, and clients. However, users that own calendars and calendars that contain events need an explicit id, so other entries can reference them.
- Allowed characters: ASCII letters, digits,
@,.,_, and-, up to 128 characters (^[a-zA-Z0-9@._-]{1,128}$). - Reserved ID:
primaryis reserved and cannot be used as an ID. - Uniqueness: IDs must be unique within each of
users,calendars, andclients("Duplicate fixture ID."). Event IDs must be unique within each calendar ("Duplicate event ID."). The same event ID can appear in different calendars.
ID errors
id value | Error |
|---|---|
| Empty, whitespace-only, or not a string | "id must be a nonempty string." |
| Nonempty string that does not match the pattern | "Invalid ID." |
| 129 to 10,000 characters | "Invalid ID." |
| Longer than 10,000 characters | "id must be a nonempty string." |
primary | "Invalid ID." |
Generated IDs and the reset baseline
When you omit an id, Calendarpit generates a UUID.
At seed time, Calendarpit captures generated IDs and initial event metadata (created, updated, etag) in the startup reset baseline. As a result, repeated calls to POST /__calendarpit/reset reproduce the same data.
- SQLite preserves that baseline across process restarts.
- In
:memory:mode, every new process generates fresh UUIDs and timestamps.
Validation failures
Validation is atomic. Every owner and calendar reference must exist in the fixture. If any fixture rule fails, Calendarpit stops at the first error:
- At startup with a new database: The process prints
Calendarpit: <message>and exits with code1. - On
POST /__calendarpit/fixtures: Calendarpit returns400(invalidParameterorunsupportedFeature) and leaves existing data unchanged.
Section reference
users
Defines test users. A test user can approve access on the consent page and own calendars.
| Field | Required | Default | Rules |
|---|---|---|---|
id | Optional | Generated UUID | 1–128 chars matching [a-zA-Z0-9@._-]; not primary. Unique across users. |
name | Required | — | Nonempty string up to 10,000 characters. |
email | Required | — | Nonempty string up to 10,000 characters matching ^[^\s@]+@[^\s@]+$ (else "Invalid email."). |
calendars
Defines calendars owned by fixture users.
- Every user that owns calendars must have exactly one calendar with
"primary": true. Otherwise validation fails with"Each user with calendars must have exactly one primary calendar.". - This includes a user with a single calendar. That calendar fails validation without
"primary": true. - Users without calendars are allowed. For those users, the
primarycalendar alias returns404.
| Field | Required | Default | Rules |
|---|---|---|---|
id | Optional | Generated UUID | 1–128 chars matching [a-zA-Z0-9@._-]; not primary. Unique across calendars. |
userId | Required | — | Must match a user id in users (else "Unknown calendar owner."). |
summary | Required | — | Nonempty string up to 10,000 characters. |
timeZone | Optional | "UTC" | Valid IANA time zone name (else "Invalid IANA time zone."). |
primary | Optional | false | Boolean (else "primary must be boolean."). |
Seeded calendars start on their owner's calendar list with default settings (selected: true, colorId: "1").
You can later modify calendars or users through the management API (see Test control API). Calendarpit then behaves as follows:
- Delete a primary calendar: The first remaining owned calendar on the calendar list becomes the new primary calendar. If none are on the list, the first remaining owned calendar becomes primary. The new primary calendar is put back on the list if needed, keeping its other list settings.
- Set
"primary": trueon another calendar: The flag transfers atomically. That calendar is put back on the calendar list, keeping its other list settings. - Delete a calendar: Its list settings are dropped, and Calendarpit records a deleted-calendar tombstone. With
showDeleted=true, the tombstone appears asdeleted: truewith default list settings. A deleted primary calendar's tombstone keepsprimary: true. Recreating a calendar with a previously deleted ID clears that tombstone and any saved list settings. - Delete a user: Calendarpit revokes the user's OAuth grants, which stops their watch channels. It removes their calendars, events, and list settings without adding tombstones.
events
Defines initial events. Fixture events use the Calendar API event fields and validation rules, plus calendarId. See Calendar API compatibility and Events, recurrence and time.
- Read-only fields (
organizer,creator,iCalUID,recurringEventId,originalStartTime) may be set. conferenceDatadoes not requireconferenceDataVersion.- The request-only field
attendeesOmittedis rejected ("Unsupported field: attendeesOmitted"). - Seeding does not trigger API insert side effects. Attendees do not receive invitation copies in their own calendars. A
conferenceData.createRequestdoes not generate a meeting link.
| Field | Required | Default | Rules |
|---|---|---|---|
calendarId | Required | — | Must match a calendar id in calendars (else "Unknown event calendar."). |
id | Optional | Generated UUID | 1–128 chars matching [a-zA-Z0-9@._-]; not primary. Unique within the calendar. |
start, end | Required | — | Event start and end (dateTime or all-day date), same rules as the Calendar API. |
organizer, creator | Optional | Calendar owner | Read-only in normal API requests, but may be set in fixtures. |
iCalUID | Optional | <id>@calendarpit.local | Read-only in normal API requests, but may be set in fixtures. |
recurringEventId, originalStartTime | Optional | None | Read-only in normal API requests, but may be set in fixtures. Set both or neither. |
sequence | Optional | 0 | Non-negative integer (else "Invalid sequence."). |
created, updated, etag | Ignored | Generated at seed time | Provided values are ignored; new values are generated when seeding the fixture. |
Setting read-only fields is useful, for example, to seed an external organizer or stored recurring exceptions. Setting only one of recurringEventId or originalStartTime fails with "Instances require recurringEventId and originalStartTime.".
clients
Defines OAuth clients for Local OAuth. There is no scopes field per client. Any client may request any supported scope.
| Field | Required | Default | Rules |
|---|---|---|---|
id | Optional | Generated UUID | 1–128 chars matching [a-zA-Z0-9@._-]; not primary. Unique across clients. |
secret | Required | — | Nonempty string up to 10,000 characters. |
redirectUris | Required | — | Nonempty array of absolute http or https URLs (up to 10,000 characters each), without fragments or credentials. Matched exactly at /oauth/authorize. |