Skip to content

test: incomplete/skip で放置されたテストを棚卸しし、隠れていた実装の不具合を修正#6945

Open
nanasess wants to merge 16 commits into
EC-CUBE:4.4from
nanasess:fix/incomplete-tests
Open

test: incomplete/skip で放置されたテストを棚卸しし、隠れていた実装の不具合を修正#6945
nanasess wants to merge 16 commits into
EC-CUBE:4.4from
nanasess:fix/incomplete-tests

Conversation

@nanasess

@nanasess nanasess commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

概要(Overview・Refs Issue)

#6926 のレビューで EditControllerWithMultipleTestsetUp()markTestIncomplete() によりまったく実行されていないことが判明したのを受け、同種のテストを全件棚卸ししました。

調査の結果、マーカーの理由の多くが誤りか陳腐化しており、「未実装だからスキップ」と書かれたものの大半は機能は 4.4 に実装済みで、テスト側が旧仕様・旧 API のまま取り残されているだけでした。さらに、死んだテストの陰に実装側の不具合が 2 件隠れていました

markTestIncomplete51 件 → 13 件に削減しています(残 13 件のうち Agent Commerce の 12 件は仕様トレーサビリティ用の意図的なマーカーです)。

Before After
incomplete (意図的な分を除く) 51 13
ProductControllerTest 48 tests / Incomplete 5 / Assertions 123 48 tests / Incomplete 0 / Assertions 147
tests/Web/Admin/Order/ 78 tests / Incomplete 11 77 tests / Incomplete 0
CartValidationTest Incomplete 15 / Assertions 96 Incomplete 8 / Assertions 146
TransactionListener 無テスト 14 tests / 27 assertions

方針(Policy)

マーカーを外して実際に実行し、失敗内容から根因を特定したうえで、根因ごとにコミットを分けました。

  • 復活できるものは復活させる
  • テスト側が陳腐化しているものは現行仕様に合わせて書き換える
  • 対象機能が存在しないもの(Silex 時代の化石、廃止された概念)は削除する
  • 削除したもののうち機能が現存してカバレッジが失われるものは新規にテストを書く
  • 通せないものは無理に通さず、マーカーを正しい理由に書き換えて残す

実装に関する補足(Appendix)

src/ の変更は 2 ファイル 12 行のみです

Order::getTotalByTaxRate() に null ガードを追加 (src/Eccube/Entity/Order.php)

$roundingTypes[$rate]->getId() を null ガードなしで呼んでおり、RoundingType 未設定の課税明細が 1 件でもあると Call to a member function getId() on null になります(OrderItem::getRoundingType()?RoundingType)。

対になる getTaxByTaxRate() には 2024-03-11 の f653af6 "Fix phpstan" で同じガードが入っており、getTotalByTaxRate() だけが漏れていました

単なる防御ではなく実害の修正でもあります。OrderPdfService

foreach ($Order->getTotalByTaxRate() as $rate => $total) {
    ... $Order->getTaxByTaxRate()[$rate] ...
}

と両者を突き合わせるため、集計対象の税率がずれると未定義キーを引きます。本修正で両者の税率キーが常に一致します。

通常は PurchaseFlow の TaxProcessor が必ず設定するため顕在化しにくいですが、明細を直接組み立てるカスタマイズ・プラグインからは到達し得ます。

お問い合わせ確認ページの <title> を修正 (src/Eccube/Controller/ContactController.php)

contactcontact_confirm が同一パス /contact に割り当てられているため、ルータは常に contact にマッチします。TwigInitializeListener_route から引いた Page 名を twig グローバル title に設定するので、確認ページでも <title> が「お問い合わせ(入力ページ)」のままでした。コントローラが render に渡す Page(contact_confirm)meta_tags には効いており(noindex は正しく出力される)、title だけが不整合な状態です。

default_frame.twig<title>subtitletitle より優先するため、subtitle に Page 名を渡して上書きします。ProductController'subtitle' => $Product->getName() と同じ手法です。

ルーティングは変更していません。 パスを分けると公開 URL が変わり後方互換性を壊すためです。

なお、この 2 件は確認ページの <title> 表示が変わる従来 fatal だったケースが集計スキップになるという点で描画結果が変わります。いずれも不具合の修正ですが、レビューの観点として明記しておきます。

棚卸しで判明した主な事実

  • CartService::lock() / unlock() は 4 系に存在しません。「カートのアンロック対応」系のマーカーは廃止された概念を前提にしていたため、該当テストを整理しました。
  • 販売種別が異なる商品は、4 系では購入を拒否せず「カートを分割」します。「この商品は同時に購入することはできません。」を期待する EC-CUBE 3 時代のテストが残っていたため、カート分割仕様へ書き換えました。マーカー理由は「複数配送が実装されるまでスキップ」でしたが、複数配送は実装済みで、理由が二重に誤っていました。
  • 管理画面 商品検索の 公開/非公開・在庫 フィルタは SearchProductType に実装済みです。「検索項目(公開・非公開・在庫内)の実装完了後に実施」は陳腐化しており、テストが「在庫なし」をリンクで探していた(現行は select)だけでした。
  • admin_shipping_update_order_status は現存し、受注一覧の一括ステータス更新から実際に使われています。「使用していないルーティングのためスキップ」は事実誤認で、失敗原因は NEW 受注が無いフィクスチャ不足でした。
  • EccubeConfig::offsetSet() は設計上 LogicException を投げる読み取り専用です。設定を書き換える CSV 雛形テストの手法は原理的に成立しないため、期待値側を出力エンコーディングに合わせる方式へ変えました。
  • expectOutputString / expectOutputRegex原理的にマッチしませんHttpKernelBrowser::filterResponse()ob_start()sendContent()ob_end_clean() で StreamedResponse の出力を自前バッファに吸い上げて捨てるためです。$this->client->getInternalResponse()->getContent() へ移行しました。
  • EditControllerWithMultipleTest の 12 テストのうち 8 つは EditControllerTest の重複でした。固有価値のある複数配送のカバレッジのみ現行の作法で書き直しています。

テスト側の品質問題も修正しました

  • OrderControllerTest::testSimpleUpdateOrderStatusWithSendMail が空振りで green でした。findBy(['OrderStatus' => NEW]) が 0 件を返し foreach が回らず、アサーションが 1 つも実行されていませんでした。
  • CsvImportControllerTest は実行するだけでリポジトリを汚していました。fixture のコピー先が __DIR__(git 管理下)だったためです。sys_get_temp_dir() に変更し、併せて 8740f32 で誤ってコミットされていた成果物 tests/Eccube/Tests/Web/Admin/Product/categories.csv を削除しました(コピー元 tests/Fixtures/categories.csv とは改行コードだけが異なり、参照コードはありません)。

テスト(Test)

相談(Discussion)

  • 残した 13 件のマーカーのうち、CartValidationTest 8 件・ShoppingControllerWithNonmemberTest 2 件・ShoppingControllerTest 1 件は、購入フロー変更(scenarioConfirm 後のリダイレクト消失)とマイページ履歴 UI の追従が必要で、購入フロー全体の調査が先行します。本 PR では理由コメントのみ実態に合わせて訂正しました。別 issue での対応が妥当と考えています。
  • Step4TypeTest::testInvalidData の 1 件は、Web インストーラが MySQL/PostgreSQL で 500 になる 4.4 の回帰を検出します(2025-10-22 の fbad0fd が誤った PHPDoc を根拠に $dataarray $data へ変換したもの)。修正とセットで別 PRを予定しています。
  • UpdateSchemaDoctrineCommandTest は PostgreSQL 限定のため、削除した PluginServiceWithEntityExtensionTest が持っていた uninstall 系と「無効プラグインの Trait は proxy に含まれない」の検証は、SQLite/MySQL では未カバーのままです。DB 非依存な EntityProxyServiceTest 側に寄せる余地があります。

マイナーバージョン互換性保持のための制限事項チェックリスト

  • 既存機能の仕様変更はありません(上記 2 件は不具合修正です)
  • フックポイントの呼び出しタイミングの変更はありません
  • フックポイントのパラメータの削除・データ型の変更はありません
  • twigファイルに渡しているパラメータの削除・データ型の変更はありません(subtitle の追加のみ)
  • Serviceクラスの公開関数の、引数の削除・データ型の変更はありません
  • 入出力ファイル(CSVなど)のフォーマット変更はありません

レビュワー確認項目

  • 動作確認
  • コードレビュー
  • E2E/Unit テスト確認(テストの追加・変更が必要かどうか)
  • 互換性が保持されているか
  • セキュリティ上の問題がないか
    • 権限を超えた操作が可能にならないか
    • 不要なファイルアップロードがないか
    • 外部へ公開されるファイルや機能の追加ではないか
    • テンプレートでのエスケープ漏れがないか

🤖 Generated with Claude Code

Summary by CodeRabbit

  • 新機能

    • 販売種別が異なる商品を購入する際、カートを分割してそれぞれ独立した購入手続きへ進めるようになりました。
    • お問い合わせ確認画面のページ名が、画面タイトル・サブタイトルに正しく表示されるようになりました。
  • 不具合修正

    • 丸め規則が未設定の税率を、税率別合計から適切に除外するよう改善しました。
    • 空のカートで複数配送手続きを開始した場合、エラー画面へ正しく遷移します。
  • 品質改善

    • 商品・注文・配送・CSV出力などの検証を強化しました。

nanasess and others added 16 commits July 17, 2026 16:10
以下の4箇所の markTestIncomplete を削除し、テストを有効化する。
いずれもマーカーを外すだけでアサーションが通ることを実測済み。

- Form/Type/Install/Step1TypeTest: setUp() のマーカー削除 (4 tests)
- Service/EntityProxyServiceTest::testGenerateFromOriginalFile (4 assertions)
- Service/Payment/PaymentMethodTest::testConstructorInjection (3 assertions)
- Form/Type/Admin/OrderItemTypeTest::testInvalidQuantityNotNumeric

Rector が markTestIncomplete を含むメソッドに付与していた戻り値型
`: never` も併せて除去している (残すと fatal error になるため)。

なお OrderItemTypeTest::testInvalidQuantityHasMinus は本コミットの
対象外とし、別途扱う。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
商品明細(PRODUCT)は OrderItemType の POST_SUBMIT リスナで
「金額 -> 正, 個数 -> 正負」が仕様であり、個数のマイナス値は valid となる。
このため markTestIncomplete で放置されていた既存テストの期待値は誤りだった。

個数の符号検証が実際に効く明細種別 (DISCOUNT / DELIVERY_FEE / CHARGE) を
DataProvider で回し、個数がマイナスのとき invalid になることを検証する形に
書き換えた。DISCOUNT は金額 <= 0 が要求されるため、明細種別ごとに妥当な金額を
与えて個数のみを検証対象としている。

これまでどのテストからも通っていなかった DISCOUNT / DELIVERY_FEE / CHARGE の
符号検証分岐のカバレッジを埋める。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
参照先クラスが存在せず実行不能な、2017-2018 年の Silex 時代のテストを削除する。

- tests/Eccube/Tests/Command/CsvLoaderCommandTest.php
  テスト対象の Eccube\Command\CsvLoaderCommand が存在しない (コマンド自体が廃止済み)。
  CSV 読み込みエンジン Eccube\Doctrine\Common\CsvDataFixtures は現存するが、
  tests/Eccube/Tests/Doctrine/Common/CsvDataFixtures/ の
  CsvFixtureTest / LoaderTest / DbalExecutorTest が既にカバー済みのため代替テストは不要。
- tests/Eccube/Tests/Command/CommandTestCase.php
  Knp\Command\Command / Knp\Provider\ConsoleServiceProvider /
  Dbtlr\MigrationProvider\Provider\MigrationServiceProvider / Eccube\Application /
  Eccube\Command\GeneratorCommand\AbstractPluginGenerator がすべて存在しない。
  継承者は CsvLoaderCommandTest のみのため同時に削除。
- tests/Eccube/Tests/Transaction/TransactionListenerTest.php (ディレクトリごと)
  Eccube\Application / Eccube\Tests\ServiceProvider\CsrfMockServiceProvider /
  Eccube\Tests\ServiceProvider\FixtureServiceProvider が存在せず、$this->app[...] を 13 箇所使用。
  テスト対象の src/Eccube/EventListener/TransactionListener.php は現存するため、
  新規テストは後続コミットで作成する。
- tests/Eccube/Tests/Service/PluginServiceWithEntityExtensionTest.php
  PHPUnit 5 時代の \PHPUnit_Framework_MockObject_MockObject を型宣言に使用しており実行不能。
- tests/Eccube/Tests/Web/Admin/Order/EditControllerWithMultipleTest.php
  $this->app[...] を 13 箇所使用し、存在しない createShipping() ヘルパーを呼ぶ。
  12 テスト中 8 つは EditControllerTest に生きた同名テストがあり重複。
  固有の複数配送テストは後続コミットで新規作成する。

削除前に他ファイルからの参照が無いことを確認済み。
tests/Eccube/Tests/Web/Admin/Order/AbstractEditControllerTestCase.php は
EditControllerTest / ShippingControllerTest が使用するため残置。

検証:
- vendor/bin/phpunit tests/Eccube/Tests/Command/ tests/Eccube/Tests/Doctrine/ \
    tests/Eccube/Tests/Service/PluginServiceTest.php
  → Tests: 80, Assertions: 169 (Failure/Error 0)
- vendor/bin/phpunit tests/Eccube/Tests/Web/Admin/Order/
  → Tests: 74, Assertions: 292 (Failure/Error 0)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2017 年から markTestIncomplete で死んでいた TransactionListenerTest を
実質的に復活させ, 無カバレッジだった TransactionListener を回帰検知下に置く。

DBAL 4 移行 (EC-CUBE#6747 / PR EC-CUBE#6855) で onKernelRequest の処理順が変わったため,
主目的として次の順序をモックの期待で固定する:

  接続確立 (getNativeConnection) -> setAutoCommit(false)
  -> setTransactionIsolation -> beginTransaction

先に autoCommit=false にすると connect() 内で暗黙のトランザクションが開始され,
後続の beginTransaction がネストして onKernelTerminate の commit が効かず,
書き込みが永続化されない。この罠を順序アサーションで検知する。

その他, サブリクエストのスキップ, isEnabled=false / disable() 時の無処理,
onKernelException / onKernelTerminate の rollback・commit 分岐,
getSubscribedEvents の購読内容を検証する。

services_test.yaml で本 Listener は無効化されているためコンテナからは取得せず,
モックを注入して手動でインスタンス化する。log_debug() が LoggerFacade に
依存するため EccubeTestCase を継承してカーネルを起動する。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
削除した Silex 由来の EditControllerWithMultipleTest のうち, 固有価値のあった
複数配送のカバレッジを現行の作法で書き直す。EditControllerTest には shipping /
Multiple への言及が無く, 管理画面 受注編集の複数配送は空白だった。

追加したシナリオ:

- testRoutingAdminOrderEditWithMultiple: 複数配送の受注で編集画面が表示できること
- testShippingAssociationIsPreservedOnEdit: Shipping 2 件の受注を明細を変更せず
  mode=register で保存し, 商品明細の shipping_id 分布が保存前後で不変であること
  (OrderType::associateOrderAndShipping が既存明細の Shipping を上書きすると
  全商品が第1配送先へ集約される退行を検出する)
- testOrderEditWithShippingItemDelete: 複数配送で明細を削除しても他の配送に
  紐づく明細が保持されること (旧 testOrderEditWithShippingItemDelete 相当)

フィクスチャの注意点:

- Generator::createOrder() は ProductClass を複数渡しても Shipping を 1 つしか
  作らないため, 2 件目の Shipping と明細は手組みする
  (ShippingRepositoryTest の前例に倣う)
- 手組みした明細には PurchaseFlow の TaxProcessor 相当の税率・税調整・丸め規則を
  TaxRule から設定する。未設定だと Order::getTotalByTaxRate() が
  RoundingType null で落ちる
- 複数配送時は OrderType::addShippingForm が Shipping フォームを追加しないため,
  フォームデータから Shipping を除外する。OrderItems は
  フォームへセットされる Order::getItems() (ソート済み) の順で組み立てる

感度確認: associateOrderAndShipping の未紐付けガードを外すと
testShippingAssociationIsPreservedOnEdit が
{1:1, 5:2} -> {1:1, 5:1} で落ちることを実測済み。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CartService の lock()/unlock() は 4 系に存在せず (現存するのは clear() のみ)、
カートのロック概念自体が廃止されている。これらに依存したまま
markTestIncomplete でマークされていたテストを整理した。

削除:
- ShoppingControllerWithMultipleNonmemberTest::testAddMultiShippingCartUnlock
- ShoppingControllerWithMultipleNonmemberTest::testAddMultiShippingShippingUnlock
  いずれも unlock() 前提で、現行仕様に対応する振る舞いが存在しないため削除。
  EC-CUBE 3 系の $this->app['eccube.service.cart'] 参照が残っていた。

書き直し:
- ShoppingControllerWithMultiple{,Nonmember}Test::testAddMultiShippingWithoutCart
  「カートが空の状態で複数配送設定画面へアクセスする」振る舞い自体は現行仕様でも
  有効なため削除せず書き直した。ShippingMultipleController::index は
  CartService::getPreOrderId() が null の場合 shopping_error へリダイレクトするため、
  期待値を cart から shopping_error へ修正した。
  CartService をテストプロセスから直接操作すると SessionNotFoundException となるため、
  クライアントのセッションを介して cart_handle_item(remove) で明細を削除する方式にした。
  併せて、4 系に存在しない /cart/add へのリクエスト (常に 404) を削除した。

- Admin/Order/EditControllerTest::testOrderProcessingToFrontConfirm
  → testOrderProcessingRegister へリネーム。
  markTestIncomplete 以降のフロント購入確認フローが lock() 依存だったため削除し、
  マーカー以前で実行済みだった「管理画面から PROCESSING で受注登録できる」検証を残した
  (本ファイル唯一の PROCESSING カバレッジのため)。未使用となった CartService
  プロパティと Constant の import も削除。

各ファイルの Failure/Error は 0、Incomplete も 0 になり、
EditControllerTest は 19 tests / 59 assertions とベースラインから
アサーション数が変わらないことを確認済み。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
markTestIncomplete で無効化されたテストが、Silex 時代の API や廃止済みルート、
旧ヘルパーシグネチャを参照したまま放置されていたため、機械的に現行の作法へ揃える。

- CartValidationTest::scenarioComplete() の潜在デッドブランチを解消
  ($confirmUrl 省略時に存在しない shopping_order を生成し RouteNotFoundException に
  なっていたため shopping_confirm へ変更)
- 旧シグネチャ scenarioCartIn($client, $productClassId) / scenarioCartUp / scenarioCartDown /
  scenarioConfirm / scenarioComplete を、生きたテストと同じ
  (Customer $Customer, ProductClass $ProductClass, int $num) 形式へ修正
- Silex 残骸を除去
  - $this->app['eccube.repository.base_info']->get() → $this->BaseInfo
  - $this->app['eccube.service.cart'] → static::getContainer()->get(CartService::class)
  - 現行 CartService に存在しない unlock() 呼び出しを削除
  - $this->app->path() / $this->app->url() → $this->generateUrl()
  - MailCatcher 系ヘルパー → assertEmailCount() / getMailerMessage()
- ShoppingControllerTest::testOrtderConfirmLayout の存在しない shopping_payment を
  shopping_redirect_to へ修正
- 未 import だった Eccube\Entity\Delivery を追加

マーカー解除は本コミットの範囲外。マーカーを外して実測した結果、残る失敗要因は
「販売種別違いはエラーではなくカート分割」という仕様変更と、購入フロー・マイページ履歴の
UI 変更であり、いずれも次段階の対応となるため markTestIncomplete は維持し、
理由コメントのみ実態に合わせて更新した。

検証: 変更した 3 ファイルを個別に phpunit 実行し、いずれもベースライン
(Failure/Error 0) を維持していることを確認。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
EC-CUBE 3 時代の「販売種別が異なる商品は同時購入を拒否する」仕様を期待していた
未完了テストを、4系の「販売種別ごとにカートを分割する」仕様へ書き換えて復活させた。

現行実装 (SaleTypeCartAllocator) は販売種別 ID をカートの識別子とするため、
販売種別違いの商品は購入を拒否されず決済単位ごとにカートが分割され、
「同時購入できない商品のカートを分けました。」が表示される。

復活させたテスト:
- testProductInCartSaleType
- testProductInCartSaleTypeWithMultiShipping
- testProductInCartChangeSaleTypeBeforePlus
- testProductInCartChangeSaleTypeBeforePlusWithMultiShipping
- testProductInCartChangeSaleTypeBeforeMinus
- testProductInCartChangeSaleTypeBeforeMinusWithMultiShipping

文言の差し替えに留めず、assertCartDivided で「カートが販売種別ごとに分割され、
各商品がちょうど 1 つのカートにのみ含まれる」ことを構造的に検証する。
分割が起きない変更を入れると 6 件すべてが落ちることを実測して確認済み。

マーカー理由「複数配送が実装されるまでスキップ」は陳腐化していた。複数配送は
実装済みであり、4系に複数配送の ON/OFF 設定 (BaseInfo) は存在しないため、
WithMultiShipping 系は「分割された各カートが独立したレジ導線 (cart_buystep) を
持つこと」の検証に置き換えた。

atestProductInCartSaleTypeFromShopping は名前に反して配送方法削除のテストであり、
atest プレフィクスで無効化されたままのため、誤ったマーカー理由のみ実態に修正した。

Tests: 50, Assertions: 146 (90 から増加), Incomplete: 14 → 8, Failure/Error 0

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
管理画面の商品検索は、旧テストが前提としていた「在庫なし」「公開」「非公開」の
リンクを持たない。現行UIは検索フォームの stock / status セレクトで絞り込むため、
`selectLink()` はノードが空で InvalidArgumentException になっていた。

マーカー理由「検索項目(公開・非公開・在庫内)の実装完了後に実施」は陳腐化している。
SearchProductType には status (ProductStatusType) と stock (ChoiceType) が実装済みのため、
検索フォームを POST する形へ書き換えて markTestIncomplete を除去した。

- testExportWithFilterNoStock: stock=[OUT_OF_STOCK] で絞り込む形へ変更
- testExportWithFilterPrivate/Public: status=[DISPLAY_HIDE|DISPLAY_SHOW] で絞り込む形へ変更
- testExportWithAll: 到達しない2個目の markTestIncomplete (デッドコード) を除去し、
  絞り込みなしで公開・非公開の両方が出力されることを検証する内容へ整理
- testProductExport: 期待どおり動作しない expectOutputRegex を除去

expectOutputRegex が機能しなかったのは、HttpKernelBrowser::filterResponse が
StreamedResponse の sendContent() を自前の ob_start()/ob_end_clean() で包んでおり、
CSV が PHPUnit の出力バッファへ到達しないため (実測で content が '' になることを確認)。
出力の取得は getInternalResponse()->getContent() に統一し、検索と CSV 出力を
searchProduct() / exportProductCsv() ヘルパへ集約した。

Rector が付けていた `: never` 戻り値型もマーカー除去に伴い外している。

Tests: 48, Assertions: 147, Deprecations: 2 (Failure/Error/Risky なし)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
マーカー理由「Impossible to call set("eccube.constants") on a frozen ParameterBag」は
今も正しい。EccubeConfig::offsetSet() は設計上 LogicException を投げる読み取り専用であり、
static::getContainer()->setParameter() も frozen ParameterBag には効かない。
つまり「設定を UTF-8 に書き換えて比較する」という手法自体が成立しない。

設定を書き換えるのではなく、期待値側を実際の出力エンコーディングへ合わせる形に改めた。

- testCsvTemplateWithProduct / testCsvTemplateWithCategory: 設定書き換えを廃止し、
  出力を eccube_csv_export_encoding (既定 SJIS-win) から UTF-8 へ復号してから
  列名の配列として比較する assertCsvTemplate() ヘルパへ集約
- 期待値をバイト列でなく列名配列にしたのは、SJIS は 2 バイト目に 0x5C
  (fputcsv のエスケープ文字) を含む文字があり (「表」= 0x95 0x5C)、
  その列だけ引用符で囲まれてバイト比較が壊れるため
- 商品雛形の期待値に、旧期待値が取りこぼしていた現行カラム
  「商品規格表示フラグ」を追加
- Content-Type / Content-Disposition / StreamedResponse であることも併せて検証
- 出力の取得は前コミット (ProductControllerTest) と同じく
  getInternalResponse()->getContent() に統一。HttpKernelBrowser::filterResponse が
  StreamedResponse の sendContent() を自前の ob_start()/ob_end_clean() で包むため
  expectOutputString では捕捉できない
- Rector が付けていた `: never` 戻り値型もマーカー除去に伴い外している

Tests: 44, Assertions: 223, Deprecations: 1 (Failure/Error/Risky/Incomplete なし)
Deprecation 1 件は vendor の mobiledetect 由来でベースラインから存在する。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
markTestIncomplete で無効化されていた 7 件のテストを、実装を裏取りしたうえで
現行仕様に合わせて復活させた (Rector が付与していた `: never` 戻り値型も併せて除去)。

- Admin/Order/OrderControllerTest::testBulkOrderStatus
  「使用していないルーティングのためスキップ」は事実誤認のため訂正。
  admin_shipping_update_order_status は現存し、受注一覧の一括ステータス更新
  (Order/index.twig の data-update-status-url) から実際に使われている。
  真因は Generator が生成する Order が既定で PROCESSING であり NEW の受注が
  0 件だったフィクスチャ不足。NEW の受注を 2 件用意するようにした。
  併せて、更新後は対象ステータスへ遷移するため findBy の条件を遷移先に修正。

- ForgotControllerTest::testIndexWithPostAndVerify
  Symfony Mailer 移行の取り残し。本文取得を getBody() から getTextBody() に変更。
  再設定画面の見出しを現行の訳語 (front.forgot.reset_title) に、
  forgot_reset は reset_key 必須のためメール記載の URL へ POST し
  mypage_login へのリダイレクトを検証するよう修正。

- ContactControllerTest::testConfirm
  確認ページへは遷移できており (Contact/confirm.twig が描画される)、
  title のみ「入力ページ」になるのが実態。contact と contact_confirm は
  同一パス /contact でルータは常に contact にマッチするため、
  TwigInitializeListener が設定する title グローバルに
  コントローラの Page (contact_confirm) が反映されないことによる。
  確認ページの描画と Page(noindex) の受け渡しを検証する内容に変更し、
  title の制約は実装側の課題として NOTE を残した。

- Admin/Product/CategoryControllerTest::testMoveSortNoAndShow
  カテゴリツリーは各階層とも sort_no 降順で描画される
  (CategoryRepository::getList)。末尾は 子2-2 ではなく、最後の親 (親2) 配下の
  最も深い末尾ノードである 孫2 が正。描画順そのものを検証するよう修正。

- Admin/Store/TemplateControllerTest::testDownload
  実装の不具合ではなく BrowserKit 由来の問題。HttpKernelBrowser::doRequest() は
  handle() 直後に terminate() を呼び、その後 filterResponse() で sendContent()
  するため、kernel.terminate で一時ファイルを削除する download() では
  BinaryFileResponse の読み出し前にファイルが消える (実リクエストでは送信後に
  terminate されるため問題ない)。カーネルを直接 handle して検証し、
  最後に terminate する形に変更した。

- ShoppingControllerTest::testPaymentEmpty
  旧メッセージは現存しない。選択肢が空の場合は OrderType が Payment の
  NotBlank に front.shopping.payment_method_not_fount を設定する仕様のため、
  確認画面では支払い方法が描画されないこと、確定時に当該エラーとなることを検証。
  併せて未 flush だった支払い方法の利用条件変更を反映するよう修正。

- ShoppingControllerTest::testShippingShippingPost / testShippingEditTitle
  旧 UI セレクタと既に存在しない MailCatcher ヘルパー前提だったため、
  現行の shopping_shipping_edit フローに追従。電話番号は
  TruncateHyphenListener によりハイフンが除去される点も反映した。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
追加した未完了テスト復活分に対して Rector と PHP-CS-Fixer を適用した。

- データプロバイダの配列を Iterator (yield) へ変換
- assertSame(n, ->count()) を assertCount へ変換
- 不要な (string) キャストを除去
- find() の戻り値に assertInstanceOf を追加
- isset($phar['app']) を assertArrayHasKey へ変換

TransactionListenerTest では ContainerGetNameToTypeInTestsRector が
get('kernel') を get(Kernel::class) へ変換するが, 'kernel' はクラス名の
エイリアスを持たないため ServiceNotFoundException となり 13 件 error に
なった。rector.php に記載の方針どおり, ルールは無効化せずサービスIDを
変数へ代入して変換対象から外した。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2017-2018 年から markTestIncomplete で放置されていた 2 件を解消する。

ValidatableItemProcessorTest::testValidateCartFail
  アサーションが 1 つも無く、マーカーを外しても risky になるだけだった。
  ItemValidator::execute() には Cart/Order の分岐が無いため、対になる
  testValidateOrderFail と同じく handle() の呼び出しと ProcessResult が
  warning になることを検証する形に補完した。これで
  Success/Fail × Cart/Order の 4 通りが揃う。

PurchaseFlowTest::testAddProcesser
  削除する。呼んでいた addItemHolderProcessor() / addItemProcessor() は
  addItemHolderPreprocessor() / addItemPreprocessor() に改名されており、
  そのままでは fatal になる。ただし本体はこの 2 つを呼ぶだけで
  アサーションが無く、メソッド名を直しても「アサーション無しの重複
  テスト」にしかならない。登録した Preprocessor が実際に効くことは
  testProcessItemProcessors / testProcessItemHolderProcessor が既に
  検証済みのため、復活させる価値がない。

Rector が付与していた戻り値型 `: never` も併せて除去している。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Order::getTotalByTaxRate() は $roundingTypes[$rate]->getId() を null ガード
なしで呼んでおり, RoundingType が未設定の課税明細が 1 件でもあると
"Call to a member function getId() on null" になる。
OrderItem::getRoundingType() は ?RoundingType で null を返し得る。

対になる getTaxByTaxRate() には 2024-03-11 の f653af6 "Fix phpstan" で
同じガードが入っており, getTotalByTaxRate() だけが漏れていた。同じガードを
足して対称にする。

これは単なる防御ではなく実害の修正でもある。OrderPdfService は

    foreach ($Order->getTotalByTaxRate() as $rate => $total) {
        ... $Order->getTaxByTaxRate()[$rate] ...
    }

と両者を突き合わせるため, 集計対象の税率がずれると未定義キーを引く。
本修正で両者の税率キーが常に一致する。

通常は PurchaseFlow の TaxProcessor が必ず RoundingType を設定するため
顕在化しにくいが, 明細を直接組み立てるカスタマイズ・プラグインからは到達し得る。

回帰テストは税率キーの集合が getTaxByTaxRate() と一致することを検証する
(明細を足すと按分が変わるため金額の固定値では検証できない)。ガードを外すと
"Call to a member function getId() on null" で落ちることを確認済み。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ContactController は contact と contact_confirm を同一パス '/contact' に
割り当てているため, ルータは常に先に定義された contact にマッチする。
TwigInitializeListener は _route から引いた Page 名を twig グローバル title に
設定するので, 確認ページでも title が「お問い合わせ(入力ページ)」になっていた。
コントローラが render に渡す Page(contact_confirm) は meta_tags には効いており
(noindex は正しく出力される), title だけが不整合な状態だった。

default_frame.twig の <title> は subtitle を title より優先するため, 確認ページの
render に subtitle として Page 名を渡して上書きする。ProductController が
'subtitle' => $Product->getName() と同じ手法を使っている前例に倣った。

ルーティングは変更しない。パスを分けると公開 URL が変わり後方互換性を壊すため。

ContactControllerTest::testConfirm に title の検証を追加した (これまで
markTestIncomplete('FIXME title') で放置されていたテスト)。subtitle を外すと
「EC-CUBE SHOP / お問い合わせ(入力ページ)」で落ちることを確認済み。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
OrderControllerTest::testSimpleUpdateOrderStatusWithSendMail
  Generator が生成する Order は既定で PROCESSING のため
  findBy(['OrderStatus' => NEW]) が 0 件を返し, foreach が回らず
  アサーションが 1 つも実行されないまま green になっていた。
  先行コミットで testBulkOrderStatus に入れたのと同じ形で NEW の受注を
  用意し, findBy の結果件数も検証する。

CsvImportControllerTest
  インポート処理がアップロードファイルを削除するため, fixture を
  __DIR__ にコピーしてから使っていた。このコピー先が git 管理下のため,
  テストを実行するだけで作業ツリーが汚れていた。コピー先を
  sys_get_temp_dir() に変更する (createCsvFromArray は元から一時
  ディレクトリを使っており, それに揃える形)。
  scenario() は $this->filepath を参照し, 'categories.csv' は
  UploadedFile の表示名にすぎないため, コピー先の変更による影響はない。

  併せて tests/Eccube/Tests/Web/Admin/Product/categories.csv を削除する。
  これは上記のコピー先に生成されたテスト成果物が 8740f32 で誤って
  コミットされたもので, コピー元 tests/Fixtures/categories.csv とは
  改行コードだけが異なる。参照しているコードは無い。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

受注税計算と問い合わせ確認ページを修正し、トランザクション、管理画面、商品、カート、購入関連の未完了テストを実行可能な検証へ更新しました。複数配送、CSV、配送編集、販売種別変更などのシナリオも現行フローへ合わせています。

Changes

EC機能とテスト更新

Layer / File(s) Summary
受注計算と確認ページ
src/Eccube/Entity/Order.php, src/Eccube/Controller/ContactController.php, tests/Eccube/Tests/Entity/OrderTest.php, tests/Eccube/Tests/Web/ContactControllerTest.php
未設定の丸め規則を税率別合計から除外し、問い合わせ確認ページの subtitle と DOM 検証を追加。
トランザクションイベント検証
tests/Eccube/Tests/EventListener/TransactionListenerTest.php
接続、トランザクション開始、コミット、ロールバック、無効化、購読イベントをモックで検証。
単体テストの実行化
tests/Eccube/Tests/Form/..., tests/Eccube/Tests/Service/...
未完了だったフォーム、サービス、購入フローのテストを実装し、数量検証をデータプロバイダ化。
管理受注編集
tests/Eccube/Tests/Web/Admin/Order/*
複数配送時の shipping_id 維持・明細削除・受注ステータス遷移を検証。
管理商品・テンプレート
tests/Eccube/Tests/Web/Admin/Product/*, tests/Eccube/Tests/Web/Admin/Store/TemplateControllerTest.php
カテゴリ順序、CSV出力・テンプレート、テンプレートダウンロードのレスポンス検証を実装。
カート・購入フロー
tests/Eccube/Tests/Web/CartValidationTest.php, tests/Eccube/Tests/Web/ShoppingController*
販売種別によるカート分割、再注文、支払い、配送編集、空カート時の遷移を現行シナリオで検証。

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Customer
  participant CartValidationTest
  participant CartService
  participant CheckoutFlow
  Customer->>CartValidationTest: 異なる販売種別の商品を投入
  CartValidationTest->>CartService: カート分割状態を確認
  CartService-->>CartValidationTest: 分割されたカートを返却
  CartValidationTest->>CheckoutFlow: 各カートの購入導線を確認
  CheckoutFlow-->>Customer: 独立したチェックアウト導線
Loading

Possibly related issues

Possibly related PRs

  • EC-CUBE/ec-cube#6855: TransactionListener の接続・トランザクション処理変更と、今回追加されたイベント動作テストが直接関連します。

Suggested labels: bug

Suggested reviewers: dotani1111, ttokoro20240902

Poem

ぴょんと跳ねれば、テストが走る
丸めも配送も、道を整える
カートは分かれて、月へ向かう
うさぎの耳にも成功が響く
ふわふわコードに、春が来た crescent

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 74.39% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 変更内容の主軸である未実装テストの棚卸しと、それに伴う実装修正を概ね正確に表しています。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/Eccube/Tests/Web/ForgotControllerTest.php (1)

55-56: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

メソッドの戻り値に型宣言を追加してください。

コーディング規約(「PHP の引数と戻り値には型宣言を付け...」)に従い、テストメソッドには戻り値の型として : void を指定することをお勧めします。不適切な : never を削除した際に、型宣言自体が失われています。

♻️ 修正案
-    public function testIndexWithPostAndVerify()
+    public function testIndexWithPostAndVerify(): void
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Eccube/Tests/Web/ForgotControllerTest.php` around lines 55 - 56,
テストメソッド testIndexWithPostAndVerify に戻り値の型宣言 : void を追加してください。誤った : never
は使用せず、既存のテスト処理は変更しないでください。

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/Eccube/Tests/Web/ForgotControllerTest.php`:
- Around line 55-56: テストメソッド testIndexWithPostAndVerify に戻り値の型宣言 : void
を追加してください。誤った : never は使用せず、既存のテスト処理は変更しないでください。

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5215dbc3-772f-4d5e-bf97-fb08b7c0e9cf

📥 Commits

Reviewing files that changed from the base of the PR and between b978d3a and 1f991c9.

⛔ Files ignored due to path filters (1)
  • tests/Eccube/Tests/Web/Admin/Product/categories.csv is excluded by !**/*.csv
📒 Files selected for processing (28)
  • src/Eccube/Controller/ContactController.php
  • src/Eccube/Entity/Order.php
  • tests/Eccube/Tests/Command/CommandTestCase.php
  • tests/Eccube/Tests/Command/CsvLoaderCommandTest.php
  • tests/Eccube/Tests/Entity/OrderTest.php
  • tests/Eccube/Tests/EventListener/TransactionListenerTest.php
  • tests/Eccube/Tests/Form/Type/Admin/OrderItemTypeTest.php
  • tests/Eccube/Tests/Form/Type/Install/Step1TypeTest.php
  • tests/Eccube/Tests/Service/EntityProxyServiceTest.php
  • tests/Eccube/Tests/Service/Payment/PaymentMethodTest.php
  • tests/Eccube/Tests/Service/PluginServiceWithEntityExtensionTest.php
  • tests/Eccube/Tests/Service/PurchaseFlow/PurchaseFlowTest.php
  • tests/Eccube/Tests/Service/PurchaseFlow/ValidatableItemProcessorTest.php
  • tests/Eccube/Tests/Transaction/TransactionListenerTest.php
  • tests/Eccube/Tests/Web/Admin/Order/EditControllerTest.php
  • tests/Eccube/Tests/Web/Admin/Order/EditControllerWithMultipleTest.php
  • tests/Eccube/Tests/Web/Admin/Order/OrderControllerTest.php
  • tests/Eccube/Tests/Web/Admin/Product/CategoryControllerTest.php
  • tests/Eccube/Tests/Web/Admin/Product/CsvImportControllerTest.php
  • tests/Eccube/Tests/Web/Admin/Product/ProductControllerTest.php
  • tests/Eccube/Tests/Web/Admin/Store/TemplateControllerTest.php
  • tests/Eccube/Tests/Web/CartValidationTest.php
  • tests/Eccube/Tests/Web/ContactControllerTest.php
  • tests/Eccube/Tests/Web/ForgotControllerTest.php
  • tests/Eccube/Tests/Web/ShoppingControllerTest.php
  • tests/Eccube/Tests/Web/ShoppingControllerWithMultipleNonmemberTest.php
  • tests/Eccube/Tests/Web/ShoppingControllerWithMultipleTest.php
  • tests/Eccube/Tests/Web/ShoppingControllerWithNonmemberTest.php
💤 Files with no reviewable changes (6)
  • tests/Eccube/Tests/Form/Type/Install/Step1TypeTest.php
  • tests/Eccube/Tests/Command/CsvLoaderCommandTest.php
  • tests/Eccube/Tests/Service/PurchaseFlow/PurchaseFlowTest.php
  • tests/Eccube/Tests/Command/CommandTestCase.php
  • tests/Eccube/Tests/Transaction/TransactionListenerTest.php
  • tests/Eccube/Tests/Service/PluginServiceWithEntityExtensionTest.php

@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 77.12%. Comparing base (c29e601) to head (1f991c9).
⚠️ Report is 1 commits behind head on 4.4.

Additional details and impacted files
@@            Coverage Diff             @@
##              4.4    #6945      +/-   ##
==========================================
+ Coverage   75.79%   77.12%   +1.33%     
==========================================
  Files         546      546              
  Lines       27136    27140       +4     
==========================================
+ Hits        20569    20933     +364     
+ Misses       6567     6207     -360     
Flag Coverage Δ
Unit 77.12% <100.00%> (+1.33%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant