Skip to content

Opus変換マイルストーンのユニットテスト抜け穴埋め(設計)

何のためのものか

Opus変換・永続ストレージアップロード(マイルストーン2)は、TDD(implementer/testサブエージェント分離)の手順を踏まずに直接実装された。このページは、その後追いでユニットテストの抜けを洗い出した/digセッションの成果物。

前提: 既存の設計境界

internal/*パッケージは一貫して次の境界でテストを分けている。

  • service.goのドメインロジック — DBアクセスは狭いinterface(repo)越し。fakeで差し替え可能なのでユニットテスト対象。
  • handler.gotus_handler.goworker.goのトランスポート/グルー層 — Echo/HTTPハンドラ、pgxpool.Pool直結のトランザクション管理、外部プロセス(ffmpeg)・外部サービス(S3)呼び出し。scripts/e2e-smoke.shで検証し、ユニットテスト対象外。

internal/transcode/audio.gotranscodeAudio(ffmpeg実行・S3ダウンロード/アップロード)はこの境界の典型例で、コード内コメントに設計判断が明記されている。今回の抜け穴探しもこの境界を前提に、境界の内側(service.goのロジック)に絞って行った。境界そのものを変える(例: ffmpeg実行をinterfaceで切り出してfake化する)かどうかは別の意思決定として見送った。

ユニットテストを書く基準

「分岐の複雑さ」×「fakeで表現できる依存かどうか」の2軸で判断する。

  • 両方高い(分岐が多く、依存をfake化できる) → ユニットテスト向き。
  • どちらかが低い(分岐がほぼないグルーコード、または外部システムの実際の挙動が仕様の本体) → E2E任せが妥当。過去の複合IDバグ(known-findings)のように、fakeベースのテストではそもそも検出できない種類のバグもある。

洗い出した抜けと追加したテストケース

internal/transcode/service_test.gofinishUpload

既存6ケースに加えて追加:

  1. GetWorkErrNoRows以外のエラーを返したときエラーが伝播する(既存はErrNoRowsのケースしかなかった)
  2. PublishPendingWorkがエラーを返したときエラーが伝播する(既存は成功ケースのみ)
  3. workがpublished(既に公開済み、再アップロード等での再変換)のときPublishPendingWorkが呼ばれない
  4. workがdeletedのときPublishPendingWorkが呼ばれない(削除済みworkを誤って復活させないことの回帰テスト)

work_statusdraft/publish_pending/published/deletedの4値。既存テストはdraftpublish_pendingのみカバーしていた。

internal/works/service_test.gopublishOrPend、新規)

publishOrPendPOST /api/works/:id/publishの状態機械: タイトル必須チェック→アップロード完了済みなら即時公開、そうでなければpublish_pending)は追加前は一切テストがなかったfakeWorksRepoPublishWork/SetWorkPublishPending/GetWorkWithUploadStatusreplaceTagsのテストでは使わないためpanicスタブのままだったが、これらに実装を足す。

  1. タイトル空文字 → errTitleRequiredPublishWorkSetWorkPublishPendingも呼ばれない
  2. タイトルあり・upload未存在(UploadStatus.Valid=false) → SetWorkPublishPendingが呼ばれる
  3. タイトルあり・upload存在するがdone以外(transcoding/failed) → SetWorkPublishPendingが呼ばれる
  4. タイトルあり・uploaddonePublishWorkが呼ばれる(即時公開)
  5. GetWorkWithUploadStatusがエラーを返す → エラーが伝播し後続は呼ばれない

6と7はコード上同じ分岐(row.UploadStatus.Valid && ... == Doneがfalse)に落ちるが、sqlcが生成するNullUploadStatusのValid/Invalidの意味を明示的に固定する回帰テストとしてあえて分けている。

internal/uploads/tus_handler_test.gomapUploadError、新規)

  1. Err*センチネルエラー(ErrWorkIDRequiredErrWorkIDInvalidErrWorkNotFoundErrWorkNotUploadableErrWorkAlreadyUploaded)が対応するtusd.Error(コード・HTTPステータス)にマッピングされ、未知のエラーはERR_INTERNAL/500になる

parseFfprobeDurationと同種の「分岐の多い純粋関数」で、既存の境界基準に合致する。

対象外のまま

  • handler.go系(works.Handlerの各Echoハンドラ)、worker.gotranscode.Worker.Work)、プール直結ラッパー(uploads.createPendingUploadcompleteTUSUpload)は既存の境界どおりユニットテスト対象外のまま。
  • transcodeAudio自体(ffmpeg実行・S3 I/O)もユニットテスト対象外のまま。