| 承認 | 確認 | 作成 |
|---|---|---|
|
印 |
印 |
印 |
| 版数 | 日付 | 内容 |
|---|---|---|
| 1.0 | 2026-07-03 | 初版発行 |
| 2.0 | 2026-07-27 | 全項目を再実行し実測値で更新。pytestの想定外成功(XPASS)の内訳をXFAIL/XPASSに分けて正確化。 フルスイート実行時のみ発生するテスト間の状態汚染(No.7)を確認。 テスト実行中に実DB(読み取り専用)への接続とスキーマ不整合を新規検出(不具合No.2として追記)。 静的検査(ESLint/flake8/black)を再計測し件数を更新。 |
| 2.1 | 2026-07-27 | 不具合No.1(レポートAPIの権限不備)を修正(backend/src/routers/reports.py の require_role から "school" を削除)し、 全テストを再実行。93 passed / 2 xfailed(FAILED 0件)となりpytestのFAILが解消したことを確認。 No.1の失敗を起点としていたテスト間の状態汚染(XPASS化・RuntimeError併発)も再実行では発生せず解消。 不具合No.2(DBスキーマ不整合)は引き続き要確認。 |
| 2.2 | 2026-07-27 | 不具合No.2の調査を実施し、本番障害であることを特定(本番 GET /api/courses が2026-07-07頃から500エラーを継続、 原因は準備プラン機能のAlembicマイグレーション2件がDB未適用のままコードのみデプロイされていたこと)。 同日 alembic upgrade head で2件(verification_subscriptionsテーブル作成・chapters.is_verify_includedカラム追加)を適用し、 本番 /api/courses が200で復旧したことを実アカウントで確認。適用後の全テスト再実行は 93 passed / 1 xfailed / 1 xpassed。 |
※前回版比: ESLint 91→94件(+3)/flake8 117→112件(-5)/black 未フォーマット99→106ファイル(+7、新規追加ファイル分を含む)。詳細は「3. 静的検査結果」参照。
pytest 9.0.2(Python 3.13.11、backend/.venv環境)、frontendは Node v20.19.6 / npm 11.18.0 にて実施。
実行ブランチ: feature/activity-checker-requirements(commit 39ea579、未コミット変更あり)。全て実測値であり、捏造・省略は行っていません。
実行コマンド: python -m pytest tests/ -v --tb=short(backend/ ディレクトリ、.venv環境)
| No | カテゴリ(ファイル) | テスト項目(関数名 / 観点) | 結果 | 備考 |
|---|---|---|---|---|
| test_health.py — ヘルスチェック | ||||
| 1 | test_health.py | test_health GET /health が 200 を返すこと | PASS | |
| test_auth.py — 認証・権限(ロールベースアクセス制御) | ||||
| 2 | test_auth.py | test_unauthenticated_access 未認証でAPIアクセスすると401が返ること | PASS | |
| 3 | test_auth.py | test_authenticated_access 認証済みでAPIアクセスすると401以外で通過すること | XFAIL | xfail指定通りの失敗(想定内)。ただし内部原因はxfail理由文言「greenlet未インストール」ではなく、実DBへの読み取り専用クエリが発行され chapters.is_verify_included カラムが存在しないスキーマ不整合であることを確認(不具合No.2・特記事項参照) |
| 4 | test_auth.py | test_admin_access_denied_for_member 一般会員が管理APIにアクセスすると403が返ること | PASS | |
| 5 | test_auth.py | test_admin_access_allowed_for_owner オーナーが管理APIにアクセスできること(403でないこと) | PASS | |
| 6 | test_auth.py | test_reports_denied_for_school スクール生(role=school)がレポートAPIにアクセスすると403が返ること | PASS | v2.0時点ではFAIL(200が返却・不具合No.1)だったが、2026-07-27に backend/src/routers/reports.py の require_role から "school" を削除する修正を実施し、修正後の再実行で403が返ることを確認(PASS) |
| 7 | test_auth.py | test_reports_allowed_for_school_plus スクール+がレポートAPIにアクセスできること | XFAIL | xfail指定通りの失敗(想定内)。内部原因はNo.3と同じスキーマ不整合(不具合No.2)。v2.0時点で観測されたフルスイート実行時のXPASS化(直前のNo.6失敗による状態汚染)は、No.6の修正により再実行では発生せず解消した |
| 8 | test_auth.py | test_content_admin_denied_for_operator オペレーターがコンテンツ管理にアクセスすると403が返ること | PASS | |
| test_users.py — ユーザープロフィール | ||||
| 9 | test_users.py | test_get_me GET /api/users/me が認証ユーザー情報を返すこと | PASS | |
| 10 | test_users.py | test_get_me_member 一般会員のプロフィール取得 | PASS | |
| 11 | test_users.py | test_get_me_unauthenticated 未認証では401が返ること | PASS | |
| test_chatwork.py — Chatwork送信サービス(送信は全てモック化) | ||||
| 12 | test_chatwork.py | test_send_message_success 送信成功時にTrueが返ること | PASS | httpx.AsyncClientをモック化。実送信なし |
| 13 | test_chatwork.py | test_send_message_retry_on_failure 失敗時にリトライしてFalseが返ること | PASS | asyncio.sleepもモック化。実送信なし |
| 14 | test_chatwork.py | test_notify_helpers 各通知ヘルパー関数が正しいtrigger_typeで呼び出すこと | PASS | send_messageをモック化。実送信なし |
| test_deposit_approval.py — デポジット承認トークン(署名検証) | ||||
| 15 | test_deposit_approval.py | test_generate_token_format トークンは payload.signature 形式であること | PASS | |
| 16 | test_deposit_approval.py | test_verify_token_success 生成したトークンを正常に検証できること | PASS | |
| 17 | test_deposit_approval.py | test_verify_token_with_expected_values expected_candidate_id / expected_user_id 指定時も通ること | PASS | |
| 18 | test_deposit_approval.py | test_verify_token_wrong_signature 署名改ざん時にTokenInvalidErrorが発生すること | PASS | |
| 19 | test_deposit_approval.py | test_verify_token_wrong_secret 別シークレット生成トークンでTokenInvalidErrorが発生すること | PASS | |
| 20 | test_deposit_approval.py | test_verify_token_malformed 不正形式(ドット無し・空文字)でTokenInvalidErrorが発生すること | PASS | |
| 21 | test_deposit_approval.py | test_verify_token_expired 有効期限切れでTokenExpiredErrorが発生すること | PASS | |
| 22 | test_deposit_approval.py | test_verify_token_mismatch_cid candidate_id不一致でTokenMismatchErrorが発生すること | PASS | |
| 23 | test_deposit_approval.py | test_verify_token_mismatch_uid user_id不一致でTokenMismatchErrorが発生すること | PASS | |
| 24 | test_deposit_approval.py | test_secret_required SECRET未設定時にgenerate_tokenがTokenInvalidErrorとなること | PASS | |
| 25 | test_deposit_approval.py | test_token_jti_uniqueness 同一秒に複数生成してもトークンが一意(jti)になること | PASS | |
| 26 | test_deposit_approval.py | test_build_approve_url 承認用URLが正しく生成されること | PASS | |
| 27 | test_deposit_approval.py | test_build_skip_url_appends_action スキップ用URLにactionパラメータが付与されること | PASS | |
| 28 | test_deposit_approval.py | test_round_trip_with_real_uuid 実UUID文字列で生成〜検証が成立すること | PASS | |
| test_deposit_approval_routes.py — デポジット承認API(Chatwork送信はモック化) | ||||
| 29 | test_deposit_approval_routes.py | test_get_confirmation_approve_success 承認確認画面が正常表示されること | PASS | |
| 30 | test_deposit_approval_routes.py | test_get_confirmation_skip_action スキップ確認画面が正常表示されること | PASS | |
| 31 | test_deposit_approval_routes.py | test_get_confirmation_invalid_token 不正トークンでエラー画面が表示されること | PASS | |
| 32 | test_deposit_approval_routes.py | test_get_confirmation_expired_token 期限切れトークンでエラー画面が表示されること | PASS | |
| 33 | test_deposit_approval_routes.py | test_get_confirmation_wrong_uid user_id不一致でエラー画面が表示されること | PASS | |
| 34 | test_deposit_approval_routes.py | test_get_confirmation_already_sent 送信済み候補の確認画面表示 | PASS | |
| 35 | test_deposit_approval_routes.py | test_get_confirmation_candidate_not_found 候補が存在しない場合のエラー画面表示 | PASS | |
| 36 | test_deposit_approval_routes.py | test_post_execute_approve_success 承認実行が成功すること | PASS | |
| 37 | test_deposit_approval_routes.py | test_post_execute_skip_success スキップ実行が成功すること | PASS | |
| 38 | test_deposit_approval_routes.py | test_post_execute_already_processed status='sent'の候補への再実行が処理済み画面となること(再送防止) | PASS | |
| 39 | test_deposit_approval_routes.py | test_post_execute_chatwork_failure Chatwork送信失敗時にエラー画面となること | PASS | send_messageをモック化。実送信なし |
| 40 | test_deposit_approval_routes.py | test_post_execute_db_expired_at_in_past DB側token_expires_atが過去日時なら署名OKでも410となること | PASS | |
| test_deposit_messenger_v2.py — デポジット督促メッセージ生成(Chatwork送信はモック化) | ||||
| 41 | test_deposit_messenger_v2.py | test_notify_uses_send_as_assistant_not_send_message 伊藤bot名義(send_as_assistant)で送信されること | PASS | send_as_assistant/send_messageをモック化。実送信なし |
| 42 | test_deposit_messenger_v2.py | test_notify_skips_when_user_name_is_null user_nameがNULLの候補はスキップされskip_reasonが設定されること | PASS | |
| 43 | test_deposit_messenger_v2.py | test_notify_message_body_does_not_contain_email_as_name 平部宛メッセージに宛名としてemailが含まれないこと | PASS | |
| 44 | test_deposit_messenger_v2.py | test_notify_returns_zero_when_no_pending pending候補ゼロ件で安全に終了すること | PASS | |
| 45 | test_deposit_messenger_v2.py | test_default_message_does_not_mention_zhang build_default_messageに[To:3301051]が含まれないこと | PASS | |
| 46 | test_deposit_messenger_v2.py | test_notify_missing_settings_returns_error 必須設定未設定時にearly returnすること | PASS | |
| test_loan_documents.py — 融資申請書類(月次推移シミュレーション計算ロジック) | ||||
| 47 | test_loan_documents.py | test_length_and_actuals_preserved 実績4ヶ月+予測12ヶ月=16行、実績値がそのまま保持されること | PASS | |
| 48 | test_loan_documents.py | test_year_month_continues_across_boundary 予測の年月が実績末尾から連続し、12月→翌1月をまたぐこと | PASS | |
| 49 | test_loan_documents.py | test_early_and_mature_growth_taper 初期6ヶ月+20%、以降+10%の段階的逓減となること(プラトー未到達域) | PASS | |
| 50 | test_loan_documents.py | test_muzai_plateau_never_exceeds_cap 無在庫はプラトー(¥2,000,000)を超えないこと | PASS | |
| 51 | test_loan_documents.py | test_forecast_profit_is_rate_of_total 予測月の利益=総売上×利益率20%となること | PASS | |
| 52 | test_loan_documents.py | test_arizai_off_by_default 既定(seed=0)では有在庫が立ち上がらず必要在庫資金もゼロであること | PASS | |
| 53 | test_loan_documents.py | test_arizai_starts_after_plateau_when_seeded seed指定時、無在庫プラトー到達後に有在庫が立ち上がること | PASS | |
| 54 | test_loan_documents.py | test_peak_is_last_forecast_month_when_monotonic プラトー未到達で単調増加ならピークが予測最終月となること | PASS | |
| 55 | test_loan_documents.py | test_peak_ties_at_plateau_returns_first プラトーで頭打ちの場合、最初に上限到達した月をピークとすること | PASS | |
| 56 | test_loan_documents.py | test_observed_growth_median 実績成長率の中央値を返すこと(参考値) | PASS | |
| 57 | test_loan_documents.py | test_empty_history_raises 実績データが空の場合に例外が発生すること | PASS | |
| test_onboarding_stall.py — 申込フロー放置検知(サイレント障害の再発防止) | ||||
| 58 | test_onboarding_stall.py | test_tamai_case_flagged_payment_pending 両者署名済み・未決済・12日経過でpayment_pendingとして検知されること | PASS | |
| 59 | test_onboarding_stall.py | test_paid_user_not_flagged 決済済み(payment_confirmationsあり)は検知しないこと | PASS | |
| 60 | test_onboarding_stall.py | test_recent_not_flagged 署名直後(閾値未満)は検知しないこと | PASS | |
| 61 | test_onboarding_stall.py | test_owner_pending_flagged 本人署名済み・甲未署名放置がowner_pendingとして検知されること | PASS | |
| 62 | test_onboarding_stall.py | test_sent_only_not_signed_ignored 本人未署名(送付止まり)は対象外となること | PASS | |
| 63 | test_onboarding_stall.py | test_non_qualifying_types_ignored 付随契約種別のみの場合は検知しないこと | PASS | |
| 64 | test_onboarding_stall.py | test_threshold_boundary_inclusive 閾値日数ちょうどで検知、1日未満手前では検知しないこと | PASS | |
| 65 | test_onboarding_stall.py | test_mixed_owner_signed_is_owner_pending 一部契約のみ甲署名済みの場合owner_pendingとなること | PASS | |
| 66 | test_onboarding_stall.py | test_payment_clock_starts_at_owner_signed payment_pendingの経過日数は最後の甲署名時点を起点とすること | PASS | |
| 67 | test_onboarding_stall.py | test_multiple_users_only_stalled_returned 複数ユーザー中、放置対象のみ抽出されること | PASS | |
| 68 | test_onboarding_stall.py | test_sorted_oldest_first 経過日数の古い順にソートされること | PASS | |
| 69 | test_onboarding_stall.py | test_empty_input 入力が空の場合に空配列を返すこと | PASS | |
| 70 | test_onboarding_stall.py | test_internal_domain_excluded 自社ドメイン(rerise-japan.co.jp)の内部アカウントは検知しないこと | PASS | |
| 71 | test_onboarding_stall.py | test_exclude_emails_param exclude_emails指定の会員は検知しないこと(大文字小文字無視) | PASS | |
| 72 | test_onboarding_stall.py | test_internal_excluded_but_real_customer_still_flagged 内部除外をかけても実顧客は確実に残ること | PASS | |
| 73 | test_onboarding_stall.py | test_qualifying_types_constant 検知対象契約種別の定数が正しいこと | PASS | |
| 74 | test_onboarding_stall.py | test_alert_message_contains_key_info アラートメッセージに主要情報が含まれること | PASS | |
| 75 | test_onboarding_stall.py | test_alert_message_owner_pending_label owner_pendingラベルがメッセージに含まれること | PASS | |
| test_schemas.py — Pydanticスキーマバリデーション | ||||
| 76 | test_schemas.py | test_course_create コース作成スキーマのバリデーション | PASS | |
| 77 | test_schemas.py | test_chapter_create 章作成スキーマのバリデーション | PASS | |
| 78 | test_schemas.py | test_lesson_create_video 動画レッスン作成スキーマのバリデーション | PASS | |
| 79 | test_schemas.py | test_lesson_create_text テキストレッスン作成スキーマのバリデーション | PASS | |
| 80 | test_schemas.py | test_progress_update 進捗更新スキーマのバリデーション | PASS | |
| 81 | test_schemas.py | test_archive_create アーカイブ作成スキーマのバリデーション | PASS | |
| 82 | test_schemas.py | test_notice_create お知らせ作成スキーマのバリデーション | PASS | |
| 83 | test_schemas.py | test_notice_update_partial お知らせ部分更新スキーマのバリデーション | PASS | |
| 84 | test_schemas.py | test_question_create Q&A質問作成スキーマのバリデーション | PASS | |
| 85 | test_schemas.py | test_answer_create Q&A回答作成スキーマのバリデーション | PASS | |
| test_unanswered_messages.py — 未返信メッセージ検知 | ||||
| 86 | test_unanswered_messages.py | test_customer_last_old_flagged 顧客最終発言から48時間未返信で検知されること | PASS | |
| 87 | test_unanswered_messages.py | test_staff_last_not_flagged 運営が最後に返信済みの場合は検知しないこと | PASS | |
| 88 | test_unanswered_messages.py | test_customer_last_recent_not_flagged 顧客発言が閾値未満(10h)は検知しないこと | PASS | |
| 89 | test_unanswered_messages.py | test_threshold_boundary 閾値ちょうどで検知、わずかに手前で非検知となること | PASS | |
| 90 | test_unanswered_messages.py | test_missing_fields_skipped 必須フィールド欠損時はスキップされること | PASS | |
| 91 | test_unanswered_messages.py | test_sorted_oldest_first 経過時間の古い順にソートされること | PASS | |
| 92 | test_unanswered_messages.py | test_mixed_only_unanswered_customers 未返信の顧客のみ抽出されること | PASS | |
| 93 | test_unanswered_messages.py | test_excerpt_truncated メッセージ抜粋が適切に切り詰められること | PASS | |
| 94 | test_unanswered_messages.py | test_alert_message_contains_room_and_hours アラートメッセージにルームIDと経過時間が含まれること | PASS | |
| 95 | test_unanswered_messages.py | test_empty 入力が空の場合に空配列を返すこと | PASS | |
| 検査項目 | 対象 | 実行コマンド | 結果 | 指摘件数 |
|---|---|---|---|---|
| TypeScript型検査 | frontend | npx tsc --noEmit |
合格 | 0件 |
| Lint(ESLint) | frontend | npm run lint |
要対応 | 94件(91エラー・3警告)/前回91件(88エラー・3警告)から+3 |
| ビルド | frontend | npm run build |
合格 | 0件(500KB超のチャンクに関する警告のみ、ビルド自体は成功) |
| Lint(flake8) | backend / src | flake8 src --max-line-length=120 --count |
要対応 | 112件(E501:60・F401:23・E402:9・F841:7・F541:2・E741:2・E731:2・E302:2・その他4)/前回117件から-5 |
| フォーマット(black) | backend / src | black --check --line-length=120 src |
要対応 | 85ファイルが要フォーマット(21ファイルは適合、計106ファイル)/前回80ファイル(99ファイル中)から+7。
※backend/.venv には black が未インストールのため、システムにグローバルインストール済みの black 26.1.0( /opt/homebrew/bin/black)で実行。今後の再現性のため requirements-dev への追加を推奨。
|
| No | 内容 | 重要度 | 状態 |
|---|---|---|---|
| 1 |
レポートAPI(/api/reports)で role=school(スクールプラン)のアクセスが403で拒否されない。 test_reports_denied_for_school が FAIL。要件定義上、売上・利益レポートは school_plus のみ閲覧可能(school は学習コンテンツのみ)だが、 backend/src/routers/reports.py の require_role("owner","operator","school_plus","school") に school が含まれているため、 role=school のユーザーでも GET /api/reports が200で通過してしまう(実測: 200 == 403 のアサーション失敗)。 【v2.1修正】2026-07-27、require_role から "school" を削除する修正を実施。再実行で test_reports_denied_for_school がPASSに転じたことを確認(本番反映は別途デプロイ承認後)。 |
中 | 修正済み(2026-07-27・デプロイ待ち) |
| 2 |
DBスキーマ不整合: chapters テーブルに is_verify_included カラムが存在しない。 test_authenticated_access / test_reports_allowed_for_school_plus 実行時、GET /api/courses が backend/.env.local 設定の実DB(読み取り専用SELECTのみ・書き込みは一切発生せず)に接続し、 UndefinedColumnError: column chapters.is_verify_included does not exist が発生することを新規に確認した。
SQLAlchemyモデル側は当該カラムを前提としているため、対象DBに未適用のマイグレーションが存在する可能性がある。
両テストは@pytest.mark.xfail(理由文言「DB未接続(greenlet未インストール)」)により見かけ上は想定内の失敗として処理されているが、
実際にはgreenletはインストール済み(3.5.0)であり、xfail理由文言は事実と異なる。alembicのマイグレーション適用状況はCLIの設定ファイル解析エラー(DB接続文字列中の特殊文字によるconfigparserの補間エラー)のため本タスクでは確認できておらず、別途の確認を推奨する。
【v2.2解消】2026-07-27に調査を実施した結果、本番DBそのもののスキーマ齟齬であり、本番 GET /api/courses(会員向けコース一覧)が 2026-07-07頃から500エラーを返し続けていたことをCloud Runログと実アカウントで確認(直近7日間で毎日5〜24件発生)。 同日 alembic upgrade head により未適用マイグレーション2件(c2b0d2d037aa・46f8ad00e07d、いずれも追加系DDLのみ)を適用し、 本番 /api/courses が200で復旧(7コース・32章を正常取得)したことを確認した。 |
高(本番障害・約3週間継続) | 解消済み(2026-07-27・マイグレーション適用) |
合格基準は「pytest成功率100%・静的検査指摘0件」だが、いずれも未達である。
所見: 機能面(pytestで検証されるロジック)は自動テストされている範囲において高い健全性を維持している。 前回指摘から残存していたアクセス制御の不具合(No.1)は本日修正・再テスト済みであり、pytestのFAILはゼロとなった(本番環境への反映はデプロイ承認後)。 一方、テストがxfailの影に隠れる形で実DBのスキーマ不整合(不具合No.2)を検出しており、これは会員向け本番機能(コース一覧表示)に影響し得る内容のため、pytestの合否とは独立に運営側での事実確認を推奨する。 静的検査の指摘は大半が軽微(未使用import・フォーマット崩れ・行長)で緊急性は低いものの、単一性・刹那性の原則(不要なコードを残さない)に照らし計画的な解消を推奨する。
test_authenticated_access / test_reports_allowed_for_school_plus の2件は「DB未接続でValueErrorが発生する」ことを前提に@pytest.mark.xfailが付与されているが、
実測したところ実際にはgreenletはインストール済み(3.5.0)であり、xfail理由文言(「greenlet未インストール」)は事実と異なっていた。
2件とも実際には backend/.env.local 設定の実DBへ GET /api/courses の読み取り専用クエリが発行され、
sqlalchemy.exc.ProgrammingError: UndefinedColumnError: column chapters.is_verify_included does not exist というスキーマ不整合が発生してテストが失敗していることを確認した(不具合No.2)。
v2.0時点では、tests/ 配下を一括実行した場合に直前の test_reports_denied_for_school(不具合No.1)の失敗の影響でDBエンジンの状態が変化し、 test_reports_allowed_for_school_plus のみXPASS(想定外成功)となる事象(テスト間の独立性の課題)を確認していたが、 No.1の修正(v2.1)により起点となる失敗が消滅したため、修正後の一括実行では2件ともXFAILとなり本事象は解消した。
なお本テスト群はDBへの書き込みを行わないSELECT系の参照確認であり、外部送信・本番データの改変には該当しない。
v2.0時点では、test_reports_denied_for_school の失敗時に tests/ 一括実行で非同期DBセッションのイベントループ競合に起因するRuntimeError("got Future attached to a different loop")が併発することを確認していた。 不具合No.1の修正(v2.1)後の一括実行ではこのRuntimeErrorは発生していない。 ただし根本にあるテスト実行基盤側の構成(複数の非同期テストが同一のDBエンジンを共有する構成)は変わっていないため、 テストスイートの安定性向上のため、DBエンジンのフィクスチャスコープ見直しを別途検討することを引き続き推奨する。