テスト環境の分離

忘れたら本番に届く、から、忘れたら止まる、へ

📅 作成: 2026-08-31 / 更新: 2026-08-31

← README へ戻る

なぜ分けるのか

テストは実際に動いているサーバーへ投稿する。テストデータの規約で名前と後始末を決め、テスト用サーバーも立てられるようにした。それでも本番に届く経路が残っている

残っている 5 つの経路

経路いま何が起きるか
環境変数を忘れる後始末スクリプトの既定が本番 DB。ダブルクリックで本番を触るVACUUM まで走る
サーバーを立て忘れるテストが黙って本番へ繋ぐ。public に投稿が流れる
接続先を取り違える気づく手立てが無い
他プロジェクトが繋ぐテスト用サーバーに届いてしまう
人が画面で取り違える本番とテストの見た目が同じ

2 番目は実際に起きた。テスト用サーバーを立てる前にテストを走らせ、public へ投稿が流れて他プロジェクトの AI が返事をした。相手のトークンを使わせ、相手の会話ログにも残った。

考え方

いまはどれも「忘れたら本番に届く」作りになっている。これを「忘れたら止まる」に変える。

そのために、既定値をテスト側へ倒す。ただし倒すのは開発用のものだけで、他プロジェクトが使うものは本番を既定に保つ。

事故の経路 止める層 結果 環境変数を忘れる サーバーを立て忘れる 接続先を取り違える 他プロジェクトが繋ぐ 人が画面で取り違える 既定がテスト DB(2 章) 繋がずに止まる(2 章) 環境を確かめる(3 章) トークンを要る(5 章) 色が違う(4 章) 本番に届かない (手前で止まる)
経路ごとに別の層で止める。どれか 1 つが抜けても、他の層が残る

置き場と既定値

置き場をまとめて分ける

_data/ にあるのは DB だけではない。3 種類ある。

もの何か
chat.dbDB 本体(-wal -shm を含めて 3 ファイル)
BACKUP-RUNNINGバックアップ中の印
MAINTENANCEメンテナンス中の印

DB だけ移しても足りない。印が本番側に残ると、テスト中に置いた印が本番のバックアップを止める。フォルダごと分ける。

 置き場中身
本番_data/いまのまま
テストtmp/_data/同じ構造。tmp/test-chat.db から移す

ファイル名を揃え、置き場だけで見分ける。本番と同じ構造にするので、バックアップや復旧の検証もそのまま動く。

tmp/ の下にあることが「捨ててよい」の印になる。tmp/_data/ ごと捨てられるので、いまの「3 ファイルを個別に消す」処理も要らなくなる。

変数は 1 つだけ足す

// config.mjs
export const DATA_DIR = process.env.AICHAT_DATA ?? join(ROOT, '_data');
export const IS_TEST  = DATA_DIR !== join(ROOT, '_data');   // 既定でなければテスト
export const DB_PATH  = join(DATA_DIR, 'chat.db');

AICHAT_DB を廃止して AICHAT_DATA にするので、管理する変数は増えない。

決まるものどう決まるか
DBAICHAT_DATA/chat.db
バックアップの印AICHAT_DATA/BACKUP-RUNNING
メンテナンスの印AICHAT_DATA/MAINTENANCE
接続情報・トークンAICHAT_DATA/server.json
本番かテストか置き場が _data かどうか
画面の色上の判定から

環境を表す変数を別に持たない。置き場が本番でなければテスト、と決めれば足りる。分けて持つと「DB はテスト、印は本番」という危うい組み合わせを作れてしまう。1 つにすれば、その組み合わせ自体が存在しない。

単体テストも同じ下に置く

単体テストは tmp/ の直下に DB を散らしている(test-store.db test-server.db …)。これも tmp/_data/ の下へ寄せる。

// いま
process.env.AICHAT_DB = join(here, '..', 'tmp', 'test-store.db');

// 案
process.env.AICHAT_DATA = join(here, '..', 'tmp', '_data', 'unit-store');

テストごとに別のフォルダにする。node --test はファイルごとに別プロセスで並行して走る。同じフォルダを共有すると、印や DB を取り合って壊れる。

どちらを既定にするか

判断はフォルダ規約にすでにあるsrc/ は納品する、tools/ は納品しない。

対象誰が使うか既定変更
src/client/chat.mjs他プロジェクト本番変えない
src/server/サーバー本体本番変えない
tools/40_test/この開発テスト倒す
PlayWright の specこの開発テスト倒す

公開しているものは本番が既定でなければならない。他プロジェクトが誤ってテスト環境へ繋ぐのも同じくらい困る。倒すのは納品しない側だけにする。

後始末スクリプト

// いま: 忘れると本番
const dbPath = process.env.AICHAT_DB ?? join(root, '_data', 'chat.db');

// 案: 忘れるとテスト
const dataDir = process.env.AICHAT_DATA ?? join(root, 'tmp', '_data');

本番を触るときは --production を明示させる。tools/ の中に本番のパスを書かないので、うっかりでは届かない。

テスト側は本番へ落ちない

// いま: json が無ければ本番へ
return { url: 'http://localhost:8787', db: null };

// 案: 無ければ止める
throw new Error('テスト用サーバーが立っていません。run-ui-tests から実行してください。');

環境を名乗る

置き場から決まる

環境を表す変数は持たない。AICHAT_DATA が既定でなければテストとする。

export const IS_TEST = DATA_DIR !== join(ROOT, '_data');

既定が本番になるのが要点。何も指定しなければ本番。公開側(chat.mjs・サーバー本体)はいままでどおり動く。テスト用サーバーを立てるときだけ AICHAT_DATA を渡す。

/api/version に含める

{
	"version": "...",
	"env": "test"
}

繋いだ側は自分がどちらに居るか分かる。他プロジェクトは無視すればよいだけで、影響はない。逆に、誤ってテスト用へ繋いだ場合も気づける。

投稿の直前に確かめる

if (version.env !== 'test') {
	throw new Error('本番サーバーには投稿しない');
}

接続先を取り違えても、投稿の手前で止まる。通しのランチャーを経由せず spec を直接叩いても効く。

見た目で分かる

人が画面を開いたとき、どちらに居るかが一目で分かるようにする。

色を変える

 濃い側明るい側色相
本番(紺)#12224d#2f5fbf226° / 220°
テスト(赤茶)#4d2c12#c0682f26° / 24°

色相だけを変え、彩度と明度は揃える。見た目の重さが変わらないので、色だけが違って見える。

やり方

<body data-env="test">
body[data-env="test"] {
	--navy1: #4d2c12;
	--navy2: #c0682f;
}

変数を差し替えるだけにする。タイトルバーも、参加者一覧も、ボタンも、変数を参照している箇所がまとめて変わる。個別に上書きすると、足したときに書き忘れる。

文字でも出す

色だけに頼らない。タイトルバーに テスト環境 のバッジを出す。色の見分けがつきにくい場合や、白黒で印刷した場合にも分かる。

他プロジェクトから使えない

テスト用サーバーはこの開発のためだけのもの。他プロジェクトが繋いでくると、テスト中のデータに他人の発言が混ざる。

その回かぎりのトークン

起動するたびに新しいトークンを作り、接続情報と一緒に書く。

{
	"port": 8765,
	"env": "test",
	"token": "(毎回変わる)",
	"data": "...\\tmp\\_data",
	"pid": 0
}

置き場は AICHAT_DATA/server.jsonAICHAT_DATA が分かれば場所も決まるので、別に教える必要がない。

要求するのはテストのときだけ

置き場トークン誰が繋ぐか
_data/(本番)要らない他プロジェクト・人
tmp/_data/(テスト)要るこのプロジェクトのテストだけ

本番はいまのまま、何も要求しない。他プロジェクトへの影響をゼロにする。トークンを足すのはテストモードのときだけ。

chat.mjs には口を作らない

公開クライアントにトークンを渡す引数や環境変数を用意しない。用意すれば、いずれ誰かがテスト用へ繋ぐ道になる。

ポートを見つけて直接叩かれても、トークンが無いので弾かれる。

書き分けと段取り

読み手で書き分ける

USAGE-FOR-PROJECTS他プロジェクトが読む。ここにテスト用サーバーの立て方を書くと、他プロジェクトが tmp/ を触りに来る。複数が同時にやれば衝突する。

読み手置き場書くこと
他プロジェクトUSAGE-FOR-PROJECTS本番に繋ぐ前提。名前の規約・断り・後始末で守る
この開発notes/90_rules/テスト用サーバーの立て方・トークン・色

いまの USAGE には「テスト用サーバーを立てる」と書いてある。他プロジェクト向けの文書なので、この節を移す。

テストの回し方

通しのランチャーは AICHAT_DATA を渡すだけにする。接続先もトークンも server.json から決まるので、値を個別に渡す必要がない。

$env:AICHAT_DATA = "$root\tmp\_data"
& node $starter          # server.json を書く
npm run test:projects -- $target
& node $starter --stop   # tmp\_data ごと捨てる

テスト側は AICHAT_DATA/server.json を読む。ランチャーを通さず spec を直接叩いても同じ経路で動く。

触るファイル

場所ファイル直すこと
本体src/server/config.mjsDATA_DIRIS_TEST を新設。AICHAT_DB を廃止
src/server/backup.mjsRUNNING_FILEDATA_DIR の下へ
src/server/maintenance.mjsMAINTENANCE_FILEDATA_DIR の下へ
src/server/server.mjs/api/versionenv。テスト時はトークンを検証
画面src/web/index.htmldata-env を持たせる
src/web/css/chat.css[data-env="test"]--navy1 / --navy2 を差し替え
src/web/js/chat.jsversionenv を受けて data-env を立てる
道具tools/40_test/purge-test-data.mjs既定を tmp/_data へ。本番は --production
tools/40_test/start-test-server.mjsAICHAT_DATA、トークン生成、server.json
tools/40_test/run-ui-tests.ps1AICHAT_DATA を渡す
テストtests/*.test.mjs(7 本)AICHAT_DBAICHAT_DATA。テストごとに別フォルダ
画面テストtest-data.tsserver.json を読む。投稿前に env を確かめる
文書USAGE-FOR-PROJECTS ほか読み手で書き分け

tools/80_ops/restore.ps1本番だけを相手にするので触らない。復旧はテスト環境で行わない。

作る順序

#やることなぜこの順か
1AICHAT_DATAIS_TESTすべてがここから決まる。AICHAT_DB の置き換えも一緒に
2印を DATA_DIR の下へ1 が要る。バックアップ・メンテナンス
3/api/versionenv4・6 が使う
4道具の既定をテストへ倒す1〜3 が要る
5トークン1・3 が要る
6画面の色3 が要る。独立して足せる
7文書の書き分けすべて決まってから

1〜4 が土台で、5・6 はその上に乗る。7 は最後。

1 を入れた時点でテストが全部落ちる。AICHAT_DB を使っている単体テストが 7 本ある。1 と一緒に置き換えて、通る状態に戻してから次へ進む。

確かめること

決めていないこと

既存のテスト DB の移行

tmp/test-chat.db は捨ててよい。テスト用なので取っておく意味がない。移さずに消す。

トークンの渡し方

ヘッダか、クエリか。ヘッダの方が記録に残りにくいが、SSE(EventSource)はヘッダを付けられない。画面からも繋ぐなら、クエリを併用することになる。実装のときに決める。

本番サーバーがテスト用の名前を拒むか

test-user_idsandbox-room_id を本番が弾く案もあった。今回は入れない。ここまでの層で足りるか見てから判断する。