Skip to content

fix: 部屋のステータスが変更されていないときログを残さない #320#353

Open
ynta-3 wants to merge 1 commit into
mainfrom
fix/320-room-status-history-on-change-only
Open

fix: 部屋のステータスが変更されていないときログを残さない #320#353
ynta-3 wants to merge 1 commit into
mainfrom
fix/320-room-status-history-on-change-only

Conversation

@ynta-3

@ynta-3 ynta-3 commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

close #320

Summary by CodeRabbit

リリースノート

  • バグ修正

    • 同じ内容でのステータス更新時に不要なログ作成を防止
  • テスト

    • べき等性更新の動作確認テストを追加

@coderabbitai

coderabbitai Bot commented Apr 16, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

SetRoomStatus関数に、既存のルームステータスと内容が同じ場合に更新をスキップする検出ロジックが追加された。新しいヘルパー関数roomStatusContentEqualsで比較し、不要なupsertと履歴ログの作成を防止する。対応するテストケースも追加された。

Changes

Cohort / File(s) Summary
Room Status Update Logic
repository/gormrepository/room_status.go
SetRoomStatus内でトランザクション開始時に現在のステータスをプリロード。新規ヘルパー関数roomStatusContentEqualsでTopicとTypeの等値性を比較し、内容が同じ場合はupsertと履歴ログ作成をスキップする早期終了を実装。
Room Status Tests
repository/gormrepository/room_status_test.go
TestRepository_SetRoomStatusに新しいサブテストを追加。同一内容のステータスを2回連続で設定した場合、履歴ログが1エントリのままであることを検証(べき等性の確認)。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 PRのタイトルは、部屋のステータスが変更されていないときにログを残さないという変更の主要な目的を明確に説明しており、変更内容と完全に関連しています。
Linked Issues check ✅ Passed リンクされたissue #320は、部屋ステータスの内容が変わっていないのに履歴に記録される問題を説明しており、PRの変更(early returnと重複ログ排除)がこの要件を満たしています。
Out of Scope Changes check ✅ Passed すべての変更(SetRoomStatusの早期終了ロジック、roomStatusContentEquals関数、テストケース)がissue #320の目的範囲内であり、アウトオブスコープな変更は検出されません。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/320-room-status-history-on-change-only

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 and usage tips.

@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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
repository/gormrepository/room_status_test.go (1)

130-155: 回帰テストは良いです。Type=nil 同値ケースも追加するとさらに堅いです

今回の実装は Typenil 分岐を含むため、Type:nil かつ Topic 同一で2回更新して履歴が増えないケースも追加しておくと、将来の回帰を防ぎやすいです。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@repository/gormrepository/room_status_test.go` around lines 130 - 155, Add a
regression subcase covering the Type=nil equality path: in the test
"同じ内容で更新しても履歴は増えない" create a model.RoomStatus with Type set to nil and Topic set
to a random topic, call mustSetRoomStatus(t, r, room.ID, status, operatorID)
then call r.SetRoomStatus(t.Context(), room.ID, status, operatorID) and assert
no error and that r.GetRoomStatusLogs(t.Context(), room.ID) returns exactly one
log entry; this ensures the same-content-with-nil-Type branch in
mustSetRoomStatus / SetRoomStatus is tested.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@repository/gormrepository/room_status.go`:
- Around line 22-31: 現在の実装は currentStatus を先に読んでから roomStatusContentEquals
で判定し別操作で更新しているため TOCTOU が発生します。読取→判定→更新の一連をトランザクション内で部屋行ロックして直列化するように修正してください:
tx 上で gorm.G[model.RoomStatus](tx).Clauses(clause.Locking{Strength:"UPDATE"})
を使って room_id = ? の行を SELECT ... FOR UPDATE で取得し、そのロック下で
roomStatusContentEquals(currentStatus, status) を評価してから履歴追加/更新処理を行う(roomID と
currentStatus を扱う全ての分岐をこのロック内に移動する)。

---

Nitpick comments:
In `@repository/gormrepository/room_status_test.go`:
- Around line 130-155: Add a regression subcase covering the Type=nil equality
path: in the test "同じ内容で更新しても履歴は増えない" create a model.RoomStatus with Type set to
nil and Topic set to a random topic, call mustSetRoomStatus(t, r, room.ID,
status, operatorID) then call r.SetRoomStatus(t.Context(), room.ID, status,
operatorID) and assert no error and that r.GetRoomStatusLogs(t.Context(),
room.ID) returns exactly one log entry; this ensures the
same-content-with-nil-Type branch in mustSetRoomStatus / SetRoomStatus is
tested.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 898e12b1-8f92-43be-aa14-103059e503e1

📥 Commits

Reviewing files that changed from the base of the PR and between 97b3460 and 7effc02.

📒 Files selected for processing (2)
  • repository/gormrepository/room_status.go
  • repository/gormrepository/room_status_test.go

Comment on lines +22 to +31
currentStatus, err := gorm.G[model.RoomStatus](tx).
Where("room_id = ?", roomID).
Take(ctx)

if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}

if err == nil && roomStatusContentEquals(currentStatus, status) {
return nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

同時更新時に履歴が重複しうる TOCTOU があります

Line 22 での読取結果を Line 30 で判定してから別操作で更新しているため、同一 roomID への同時リクエストで両方がログを書けます。
「内容が未変更なら履歴を増やさない」という要件を競合時にも満たすには、部屋単位で直列化してください。

🔧 修正案(部屋行ロックで直列化)
 return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
+    if _, err := gorm.G[model.Room](tx).
+        Clauses(clause.Locking{Strength: "UPDATE"}).
+        Where("id = ?", roomID).
+        Take(ctx); err != nil {
+        if errors.Is(err, gorm.ErrRecordNotFound) {
+            return repository.ErrRoomNotFound
+        }
+        return err
+    }
+
     currentStatus, err := gorm.G[model.RoomStatus](tx).
         Where("room_id = ?", roomID).
         Take(ctx)

     if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
         return err
     }

     if err == nil && roomStatusContentEquals(currentStatus, status) {
         return nil
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@repository/gormrepository/room_status.go` around lines 22 - 31, 現在の実装は
currentStatus を先に読んでから roomStatusContentEquals で判定し別操作で更新しているため TOCTOU
が発生します。読取→判定→更新の一連をトランザクション内で部屋行ロックして直列化するように修正してください: tx 上で
gorm.G[model.RoomStatus](tx).Clauses(clause.Locking{Strength:"UPDATE"}) を使って
room_id = ? の行を SELECT ... FOR UPDATE で取得し、そのロック下で
roomStatusContentEquals(currentStatus, status) を評価してから履歴追加/更新処理を行う(roomID と
currentStatus を扱う全ての分岐をこのロック内に移動する)。

@akimon658 akimon658 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

めちゃくちゃレビュー遅くなりましたが方針は良さそうです!

CodeRabbitの指摘も修正してもらえると 🙏 (修正案のコードが正しいかは分からないのでよしなに)

assert.NoError(t, err)

logs, err := r.GetRoomStatusLogs(t.Context(), room.ID)
assert.NoError(t, err)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

すごく細かいことですが、ここはrequire.NoErrorを使ってほしいかもです

今テストしたい(assertしたい)のはSetRoomStatusの挙動とログの件数であり、GetRoomStatusLogsがエラーを返さず成功するのはテストの前提であるためです

Suggested change
assert.NoError(t, err)
require.NoError(t, err)

Comment on lines +26 to +28
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return repository.ErrRoomNotFound
}
return err
}

こうした上で、次のif文のerr == nilや44行目の条件分岐を消すとより流れが分かりやすくなると思います

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.

部屋ステータスの内容が変わっていなくても履歴に記録されてしまう

2 participants