テストに使う

Fixture

JSON の Fixture ファイルで、テストユーザー、カレンダー、イベント、OAuth クライアントを定義します。

Fixture は、Calendarpit の初期テストデータを定義する JSON ファイルです。テストユーザー、カレンダー、イベント、OAuth クライアントを定義できます。ローカルサーバーやテストスイートを、決まったデータで起動したいときに使います。

Fixture で id を明示すると、ユーザー ID、カレンダー ID、イベント ID、OAuth クライアント ID が固定されます。テストのアサーションでは、生成された値を問い合わせずに、決まった ID をそのまま検証できます。

起動時に Fixture を読み込む

Fixture ファイルのパスは、CLI フラグ --config または環境変数 CALENDARPIT_CONFIG で指定します。

どちらも指定しない場合、Calendarpit は組み込みのデフォルト Fixture を読み込みます。内容はリポジトリの fixtures/default.json と同じで、次のデータを含みます。

  • ユーザー alex と sam
  • カレンダー alex-work、alex-personal、sam-work
  • イベント planning 1 件
  • OAuth クライアント sample-app

CLI で起動する

下の JSON の例を calendarpit.fixture.json として保存し、テスト用に編集してください。インストールで公開状況と必要な環境を確認してから、ファイルを保存したディレクトリで起動します。

calendarpit serve \
  --database :memory: --config ./calendarpit.fixture.json

--config と CALENDARPIT_CONFIG の相対パスは、起動したディレクトリを基準に解決されます。

Docker・Compose で起動する

Docker では、作成済みのファイルを読み取り専用でマウントし、コンテナ内のパスを指定します。以下は POSIX シェル用のコマンドです。

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:latest

Compose では、サービスに次の 2 点を設定します。

  • volumes に ./calendarpit.fixture.json:/fixtures/startup.json:ro を追加します。
  • environment に CALENDARPIT_CONFIG: /fixtures/startup.json を設定します。

ホスト上のファイルは、コンテナの非 root ユーザーが読み取れる必要があります。起動オプションは起動と設定を参照してください。

起動時 Fixture が適用される条件

起動時 Fixture が投入されるのは、新しい空のデータベースだけです。

  • --database :memory: モード: プロセスを起動するたびにデータベースが新しくなります。そのため、Fixture は毎回適用されます。
  • 保存済みのデータがある SQLite ファイル: Calendarpit は Fixture を適用せず、警告も表示しません。保存済みのデータと、最初のリセット基準を読み込みます。保存済みのデータには、ユーザーごとのカレンダーリスト設定と、カレンダーの削除済みレコードも含まれます。

Fixture が適用されない場合でも、指定するパスは読み取り可能な JSON ファイルを指している必要があります。そうでない場合は起動に失敗します。この場合、ファイルの内容は検証されません。

詳しくはストレージと再起動を参照してください。

実行中に読み込む・リセットする

テストの合間にデータを切り替えるには、テスト制御 API を使います。

  • POST /__calendarpit/fixtures は、実行中に別の Fixture を読み込みます。
  • POST /__calendarpit/reset は、リセット基準(起動時の状態)に戻します。起動時 Fixture が復元され、カレンダーリストのエントリは起動時の etag を持つデフォルトの状態に戻ります。

どちらも次のデータを消去します。

  • OAuth の接続(grant)とトークン
  • ユーザーごとのカレンダーリスト設定
  • カレンダーの削除済みレコード
  • watch チャネル
  • 配信
  • リクエスト履歴
  • 同期トークン
  • 通知

GET /__calendarpit/state の結果には、クライアントシークレット、カレンダーリスト設定、削除済みレコードは含まれません。この結果を Fixture として読み込み直すことはできません。詳しくはテスト制御 APIを参照してください。

ルート構造と例

Fixture ファイルのルートは JSON オブジェクトである必要があります。使えるキーは users、calendars、events、clients の 4 つで、どれも省略できる配列です。空のオブジェクト({})も有効な空の Fixture です。

  • キーを指定する場合、値は配列にしてください。違反すると "<key> must be an array." で検証に失敗します。
  • それ以外のキーがあると、"Unsupported field: <key>"(reason は unsupportedFeature)で検証に失敗します。ルートオブジェクトと、ユーザー・カレンダー・イベント・クライアントの各エントリが対象です。
{
  "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 のルールと検証

ユーザー、カレンダー、イベント、クライアントの ID は省略できます。ただし、カレンダーを所有するユーザーと、イベントを含むカレンダーには、他のエントリから参照するため明示的な id が必要です。

  • 使える文字: ASCII の英字、数字、@、.、_、- で、最大 128 文字です(^[a-zA-Z0-9@._-]{1,128}$)。
  • 予約済み ID: primary は予約されているため、ID には使えません。
  • 一意性: users、calendars、clients の ID は、それぞれの中で一意である必要があります("Duplicate fixture ID.")。イベント ID はカレンダーごとに一意である必要があります("Duplicate event ID.")。カレンダーが違えば、同じイベント ID を使えます。

ID のエラー

id の値エラー
空文字列、空白だけの文字列、文字列以外"id must be a nonempty string."
パターンに合わない、空でない文字列"Invalid ID."
129〜10,000 文字"Invalid ID."
10,000 文字を超える"id must be a nonempty string."
primary"Invalid ID."

生成される ID とリセット基準

id を省略すると、Calendarpit は UUID を生成します。

生成された ID とイベントの初期メタデータ(created、updated、etag)は、投入時に起動時のリセット基準として記録されます。そのため、POST /__calendarpit/reset を何度呼んでも同じデータが再現されます。

  • SQLite では、このリセット基準がプロセスの再起動後も保持されます。
  • :memory: モードでは、プロセスを起動するたびに新しい UUID とタイムスタンプが生成されます。

検証に失敗したとき

検証はアトミックに行われます。所有者とカレンダーの参照先は、すべて Fixture 内に存在している必要があります。ルールに違反すると、Calendarpit は最初のエラーで処理を止めます。

  • 新しいデータベースで起動したとき: Calendarpit: <message> を出力し、終了コード 1 で終了します。
  • POST /__calendarpit/fixtures のとき: 400(invalidParameter または unsupportedFeature)を返し、既存のデータは変更しません。

セクションリファレンス

users

テストユーザーを定義します。テストユーザーは同意画面でアクセスを承認でき、カレンダーを所有できます。

フィールド必須デフォルトルール
id任意生成された UUID[a-zA-Z0-9@._-] に一致する 1〜128 文字。primary は不可。users 内で一意。
name必須—10,000 文字以内の空でない文字列。
email必須—^[^\s@]+@[^\s@]+$ に一致する、10,000 文字以内の空でない文字列(違反時は "Invalid email.")。

calendars

Fixture のユーザーが所有するカレンダーを定義します。

  • カレンダーを所有するユーザーには、"primary": true のカレンダーがちょうど 1 つ必要です。違反すると "Each user with calendars must have exactly one primary calendar." で検証に失敗します。
  • カレンダーが 1 つだけのユーザーも同様です。"primary": true がなければ検証に失敗します。
  • カレンダーを持たないユーザーも定義できます。そのユーザーでは、primary カレンダーエイリアスが 404 を返します。
フィールド必須デフォルトルール
id任意生成された UUID[a-zA-Z0-9@._-] に一致する 1〜128 文字。primary は不可。calendars 内で一意。
userId必須—users のユーザー id と一致すること(違反時は "Unknown calendar owner.")。
summary必須—10,000 文字以内の空でない文字列。
timeZone任意"UTC"有効な IANA タイムゾーン名(違反時は "Invalid IANA time zone.")。
primary任意false真偽値(違反時は "primary must be boolean.")。

投入されたカレンダーは、デフォルト設定(selected: true、colorId: "1")で所有者のカレンダーリストに入ります。

あとで管理 API でカレンダーやユーザーを変更できます(テスト制御 APIを参照)。そのときの動作は次のとおりです。

  • プライマリカレンダーを削除する: 残りの所有カレンダーのうち、カレンダーリストに残っている最初のものが新しいプライマリになります。リストに残っているものがない場合は、残りの所有カレンダーの最初のものがプライマリになります。新しいプライマリは必要に応じてリストに戻され、その他のリスト設定は保持されます。
  • 別のカレンダーに "primary": true を設定する: フラグがアトミックに移ります。そのカレンダーはカレンダーリストに戻され、その他のリスト設定は保持されます。
  • カレンダーを削除する: そのリスト設定は消え、削除済みレコードが記録されます。このレコードは、showDeleted=true のときに deleted: true とデフォルトのリスト設定で返されます。プライマリカレンダーの削除済みレコードは primary: true を保持します。削除済みの ID でカレンダーを作り直すと、その削除済みレコードと保存されていたリスト設定は消去されます。
  • ユーザーを削除する: そのユーザーの OAuth 接続(grant)が失効し、watch チャネルも停止します。カレンダー、イベント、リスト設定も削除されます。この場合、削除済みレコードは追加されません。

events

初期イベントを定義します。Fixture のイベントでは、Calendar API と同じイベントフィールドと検証ルールに加えて、calendarId を使います。Calendar API の互換性とイベント、繰り返し、時刻の扱いを参照してください。

  • 読み取り専用フィールド(organizer、creator、iCalUID、recurringEventId、originalStartTime)も設定できます。
  • conferenceData には conferenceDataVersion は不要です。
  • リクエスト専用フィールドの attendeesOmitted は拒否されます("Unsupported field: attendeesOmitted")。
  • 投入時には、API で挿入したときの副作用は発生しません。参加者のカレンダーに招待のコピーは作られません。conferenceData.createRequest があっても会議リンクは生成されません。
フィールド必須デフォルトルール
calendarId必須—calendars のカレンダー id と一致すること(違反時は "Unknown event calendar.")。
id任意生成された UUID[a-zA-Z0-9@._-] に一致する 1〜128 文字。primary は不可。カレンダー内で一意。
start, end必須—イベントの開始と終了(dateTime または終日イベントの date)。ルールは Calendar API と同じ。
organizer, creator任意カレンダーの所有者通常の API リクエストでは読み取り専用ですが、Fixture では設定できます。
iCalUID任意<id>@calendarpit.local通常の API リクエストでは読み取り専用ですが、Fixture では設定できます。
recurringEventId, originalStartTime任意なし通常の API リクエストでは読み取り専用ですが、Fixture では設定できます。両方設定するか、どちらも設定しません。
sequence任意00 以上の整数(違反時は "Invalid sequence.")。
created, updated, etag無視投入時に生成指定した値は無視され、Fixture の投入時に生成されます。

読み取り専用フィールドは、たとえば外部の主催者や、保存済みの繰り返しの例外を投入するときに使います。recurringEventId と originalStartTime の片方だけを設定すると、"Instances require recurringEventId and originalStartTime." で失敗します。

clients

ローカル OAuthで使う OAuth クライアントを定義します。クライアントごとの scopes フィールドはありません。どのクライアントも、対応しているすべてのスコープを要求できます。

フィールド必須デフォルトルール
id任意生成された UUID[a-zA-Z0-9@._-] に一致する 1〜128 文字。primary は不可。clients 内で一意。
secret必須—10,000 文字以内の空でない文字列。
redirectUris必須—http または https の絶対 URL(各 10,000 文字以内)の空でない配列。フラグメントと認証情報は含められません。/oauth/authorize では完全一致で照合されます。

目次