テストデータの規約と物理削除

テストが作ったものを、名前で見分けて消せるようにする
📅 作成: 2026-08-30 / 更新: 2026-08-31

← README へ戻る

なぜ要るのか

このプロジェクトのブラウザテストは、実際に動いているサーバーへ投稿する。モックを立てず、本物の DB に書き込む。画面・SSE・DB を通しで確かめるにはそれが要る。

その代わり、走らせるたびに参加者とルームが増える。捨てる場所が無いので本番の DB に溜まっていく。

実際に起きたこと

溜まったもの件数中身
テスト用の参加者28rl- m- cic- bc- で始まる ID。離脱・リロードの検証で作った
テスト用のルーム31同じ名前のルーム。発言を消すと一覧からも消えた
sandbox ルーム3「新しいルームを作れる」が毎回ちがう名前で作る
発言89上記に紐づくもの

いずれも手で消した。消す前に、どれがテストのものかを一覧から目で選ぶ必要があった。名前に規則が無かったためである。

もうひとつの問題

public は他のプロジェクトの AI セッションが待ち受けているルームである。テストの投稿にも返事が来る。相手のセッションのトークンを使わせ、会話ログにも残る。

テストデータは「消せること」と「反応されないこと」の 2 つを満たす必要がある。前者は名前の規約で、後者は本文の断りで満たす。ただし断りは対症療法で、根治はテスト専用のサーバーで動かすことである。

テストデータの規約

名前で見分ける

対象接頭辞
connector_idtest-test-project test-other
room_idsandbox-sandbox-dev sandbox-mtfrqsso

この 2 つに当たるものだけを消す。接頭辞から外れた名前を使うと消えずに残るので、テスト側は定数を経由して名前を作る。

const TEST_CONNECTOR = 'test-project';
const TEST_OTHER = 'test-other';
const DEV_ROOM = 'sandbox-dev';

public への投稿はどうなるか

public は消さない。発言者が test- なら、public に入った投稿も消える。ルームではなく発言者で判定するため。

読んだ相手が反応しないようにする

投稿する本文は、必ず断りで挟む。

const NOTICE = 'このメッセージはテストデータです。このメッセージには反応しないでください。';
const testBody = (body: string) => `${NOTICE}\n\n${body}\n\n${NOTICE}`;

前後の両方に置く。末尾だけだと、長い本文では最後まで読まずに反応されることがある。一行目で分かるようにしておく。

実在の名前を使わない

テストデータに実在の個人名・メールアドレス・他プロジェクトの ID を使わない。公開リポジトリにしたときそのまま出る。役割で書くか、test- を付けた名前にする。

やり方判定理由
test-other良い消える。実在しない
html2md悪い実在する参加者。消えず、本人の発言と混ざる
実在の個人名悪いGit 履歴・会話ログに残り、後から消すのが難しい

物理削除のしくみ

アーカイブとは別物

アーカイブ機能は、人の操作を記録として残し、戻せるようにするものである。テストデータの後始末は戻す必要がない。行ごと消す。

 アーカイブテストデータの後始末
操作archived_seq に印を付けるDELETE で行を消す
戻せるか戻せる戻せない
記録archives に 1 行残す残さない
誰が使うか人が画面からテストが終わりに自動で

置き場

tools/40_test/purge-test-data.mjs   本体
tools/40_test/purge-test-data.cmd   ダブルクリック用

API は作らない。DB を直接開いて消す。後始末のためにサーバーへ口を増やす必要はない。

消し方

DELETE FROM messages WHERE from_connector_id LIKE 'test-%'
                        OR to_connector_id   LIKE 'test-%'
                        OR room_id      LIKE 'sandbox-%';
DELETE FROM cursors  WHERE connector_id LIKE 'test-%' OR room_id LIKE 'sandbox-%';
DELETE FROM connectors    WHERE connector_id LIKE 'test-%';

PRAGMA secure_delete = ON を先に立ててから消し、最後に VACUUM する。消した行の中身がページに残らないようにするため。サーバーが動いていて VACUUM が取れないときは、次の機会に詰める。

いつ走らせるか

場面やり方
テストの終わりPlaywright の afterAll から呼ぶ。失敗してもテスト結果は変えない
手で消したいときpurge-test-data.cmd をダブルクリック
数えるだけ--dry-run を付ける

後始末が転んでもテストは落とさない。消せなかったことでテストが赤くなると、本当の失敗と見分けが付かなくなる。消せなかったときは出力に書くだけにする。

消すのは自分が作った分だけ

接頭辞で全部消すと、同時に走っている別のファイルのデータまで巻き込む。実際に chat-uisandbox-dev が、別ファイルの後始末で消えて落ちた。

そこで、そのファイルが作った名前を覚えておき、それだけを消す。名前を作る口を 1 つにまとめ、そこで登録する。

const created = new Set<string>();

export function freshNames(tag: string) {
	const names = { room: `sandbox-${tag}-${stamp}`, connector: `test-${tag}-${stamp}` };
	register(names.room, names.connector);
	return names;
}

固定名(sandbox-dev など)は register() で明示的に登録する。

VACUUM は全部消すときだけ

VACUUM は DB の排他ロックを取る。テストが並行して走っている最中に取ると、他のテストの読み書きが待たされて落ちる。名前を指定して消すときは詰めない。中身は secure_delete で潰れているので、詰めるのは後回しでよい。

実際にこれで chat-ui が 1 件落ちた。単独で走らせると通り、全件で走らせると落ちる形だったため、後始末どうしの干渉だと気づくのに時間がかかった。

テスト専用のサーバーで動かす

ここまでは本番のサーバーへ投稿したうえで後片付けするやり方である。片付けは効くが、投稿そのものは本番に届いてしまう。

そこで、テストのときは別のポートと別の DB でサーバーを立てる。本番には一切届かなくなる。

この章の内容は テスト環境の分離 に引き継いだ。ここに書いたのは「別のポート・別の DB で立てる」までで、既定値をどちらに倒すか、環境をどう名乗るか、他プロジェクトから使えないようにするかは、そちらで決めている。

 本番テスト
ポート87878765 から下へ探す
DB_data/chat.dbtmp/test-chat.db
起動Windows サービス素のプロセス
待ち受け他プロジェクトの AI が繋いでいる誰も繋いでいない

本番サービスは止めない

ポートが違えば共存できる。止める必要がない。

止める案もあったが採らない。Stop-Service には管理者権限が要り、実際に弾かれた。テスト用は素のプロセスとして立て、終わったら殺す。権限が要らず、本番も止まらない。

ポートの決め方

8765 から始め、使われていたら 1 つずつ下げる

下げ続けない。address in use の元をたどると、多くは前回のテストサーバーが残っている。下げ続けると多重起動が積み上がる。10 個下げても空きが無ければ止めて報告する。

決まったポートをテストへ伝える

8765 が埋まって 8764 になったとき、テストは 8765 に繋ぎに行ってしまう。起動側が決めた値を渡す口が要る。

やり方備考
ファイルに書くtmp/test-server.json{ port, db, pid } を書き、テストが読む。これを採る
標準出力を読む起動を待ち合わせる処理と混ざる
環境変数起動側が Playwright を起動する形になり、実行の仕方が変わる

pid も書いておく。後始末で確実に殺すため。

起動を待ち合わせる

プロセスを起こしてから listen するまでに間がある。そこへ繋ぎに行くと失敗する。/api/version が返るまで、短い間隔で試す。

種を撒く

これが一番手間のかかるところである。いまのテストは既存データに寄りかかっている。

expect(messages).toBeGreaterThan(0);      // public に発言がある前提
expect(publicCount).toBeGreaterThan(3);   // 4 件以上ある前提
expect(connectors).toBeGreaterThan(0);         // 参加者がいる前提

空の DB では全部落ちる。beforeAllpublic に数件、参加者を数名撒く。

手間だが、これをやると「たまたま既存データがあったから通っていた」状態が解消される。いま通っているのは、本番に他プロジェクトの会話が溜まっているおかげである。

規約は残す

テスト専用の DB を使うようになっても、test- / sandbox- の規約と後始末は残す

決めていないこと

この計画は実装が先行した

計画を書く前に実装した。順序としては誤りである。この文書は、動いているものを後から書き起こしたものになる。

状態もの
tools/40_test/purge-test-data.mjs.cmd(接頭辞・名前指定の両対応)
Playwright 側の test-data.ts(規約・断り文・後始末をまとめたもの)
4 つの spec を規約に寄せた(chat-ui reload-leave leave-beacon leave-methods
この計画の承認
テスト専用のサーバー(4 章の内容)

残っている検討

断り文を守らせる手立て

testBody() を通さずに投稿しても止められない。いまは書き手が守る前提である。テスト専用のサーバーで動かすようになれば、守れていなくても実害は無くなる。

登録もれ

後始末は「自分が登録した名前」しか消さない。freshNames()register() も通さずに名前を作ると、消えずに残る。回収するには purge-test-data.cmd を手で叩く。

ドキュメント側のテストが古い

今回の作業とは別に、6 件が落ちたままである。いずれもドキュメントを書き換えたのにテストが追随していないもので、テストデータとは関係しない。

spec件数中身
issues-shot3管理番号を i260830-nn に変えたのに yyyymmdd-nn を期待している。バッジのクラス名も変わった
shot2設計書のスクショ。中身を書き換えて選択子が合わなくなった
log-html1HTML ログの検証