Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 71 additions & 2 deletions spec/github/models_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,30 @@ describe Github::Subject do
end
end

describe "#commented?" do
it "is true when the thread has a comment" do
subject = subject_from(
"PullRequest",
url: "https://api.github.com/repos/o/r/pulls/1",
latest_comment_url: "https://api.github.com/repos/o/r/issues/comments/1",
)
subject.commented?.should be_true
end

it "is false when latest_comment_url mirrors the subject url (no comment yet)" do
subject = subject_from(
"PullRequest",
url: "https://api.github.com/repos/o/r/pulls/1",
latest_comment_url: "https://api.github.com/repos/o/r/pulls/1",
)
subject.commented?.should be_false
end

it "is false when latest_comment_url is blank" do
subject_from("PullRequest", url: "https://api.github.com/repos/o/r/pulls/1").commented?.should be_false
end
end

describe "#number" do
it "extracts a trailing issue/PR number from the url" do
subject_from("Issue", url: "https://api.github.com/repos/o/r/issues/42").number.should eq "42"
Expand Down Expand Up @@ -109,6 +133,51 @@ describe Github::Notification do
it "falls back to a generic message for unknown reasons" do
notification_from("some_future_reason").reason_message.should eq Github::Notification::GENERIC_MESSAGE
end

it "switches to a follow-up message once a review-requested thread has a comment" do
notification = notification_from(
"review_requested",
url: "https://api.github.com/repos/o/r/pulls/1",
latest_comment_url: "https://api.github.com/repos/o/r/issues/comments/1",
)
notification.reason_message.should eq "レビュー依頼中の PR に動きがありました"
end

it "switches to a follow-up message once an assigned thread has a comment" do
notification = notification_from(
"assign",
url: "https://api.github.com/repos/o/r/issues/1",
latest_comment_url: "https://api.github.com/repos/o/r/issues/comments/1",
)
notification.reason_message.should eq "担当している PR/Issue に動きがありました"
end

it "keeps the assign message while the thread has no comment" do
notification = notification_from(
"assign",
url: "https://api.github.com/repos/o/r/issues/1",
latest_comment_url: "https://api.github.com/repos/o/r/issues/1",
)
notification.reason_message.should eq "アサインされました"
end

it "keeps the review-requested message while the thread has no comment" do
notification = notification_from(
"review_requested",
url: "https://api.github.com/repos/o/r/pulls/1",
latest_comment_url: "https://api.github.com/repos/o/r/pulls/1",
)
notification.reason_message.should eq "レビューを依頼されました"
end

it "keeps the reason message for reasons without a follow-up variant" do
notification = notification_from(
"comment",
url: "https://api.github.com/repos/o/r/pulls/1",
latest_comment_url: "https://api.github.com/repos/o/r/issues/comments/1",
)
notification.reason_message.should eq "コメントがつきました"
end
end

describe "#pretext" do
Expand Down Expand Up @@ -155,10 +224,10 @@ describe Github::Notification do
end
end

private def notification_from(reason : String)
private def notification_from(reason : String, url = "", latest_comment_url = "")
Github::Notification.from_json({
reason: reason,
subject: {type: "Issue", title: "title"},
subject: {type: "Issue", title: "title", url: url, latest_comment_url: latest_comment_url},
repository: {owner: {login: "octocat"}},
updated_at: "2026-07-14T00:00:00Z",
}.to_json)
Expand Down
11 changes: 10 additions & 1 deletion spec/github/usecase_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ private def notification(
url = "https://api.github.com/repos/octocat/Hello-World/issues/42",
reason = "review_requested",
repo_html_url : String? = "https://github.com/octocat/Hello-World",
latest_comment_url = "",
)
Github::Notification.from_json({
reason: reason,
subject: {type: "Issue", title: "Spurious failure", url: url},
subject: {type: "Issue", title: "Spurious failure", url: url, latest_comment_url: latest_comment_url},
repository: {full_name: "octocat/Hello-World", html_url: repo_html_url, owner: {login: "octocat"}},
updated_at: "2026-07-14T00:00:00Z",
}.to_json)
Expand All @@ -40,6 +41,14 @@ describe Github::Usecase do
build(notification(reason: "review_requested"), comment).pretext.should eq "[Issue] レビューを依頼されました"
end

it "reflects the follow-up wording for a commented review-requested thread in the pretext" do
notify = notification(
reason: "review_requested",
latest_comment_url: "https://api.github.com/repos/octocat/Hello-World/issues/comments/1",
)
build(notify, comment).pretext.should eq "[Issue] レビュー依頼中の PR に動きがありました"
end

it "formats the title as owner/repo#number title" do
build(notification, comment).title.should eq "octocat/Hello-World#42 Spurious failure"
end
Expand Down
37 changes: 37 additions & 0 deletions src/github/models.cr
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,22 @@ module Github
"invitation" => "招待が届きました",
}

# 「一度きりの出来事」を指す reason 向けの、2 回目以降の文言。
#
# GitHub の reason は「そのスレッドを購読している理由」であってイベント種別
# ではないため、一度レビュー依頼/アサインされた PR・Issue は、以降のコメントや
# 更新もすべて同じ reason で届く。REASON_MESSAGES だけだと常に「レビューを依頼
# されました」「アサインされました」になり通知理由が実態と合わないので、
# 初回ではないと判断できる通知は文言を差し替える(issue #104)。
#
# 何が起きたか(コメントか push か状態変更か)は通知 payload から判別できない
# ため、文言はコメントに限定せず「動きがありました」に留める。
# ここに reason を足せば他の reason にも同じ切り替えを適用できる。
FOLLOWUP_MESSAGES = {
"review_requested" => "レビュー依頼中の PR に動きがありました",
"assign" => "担当している PR/Issue に動きがありました",
}

GENERIC_MESSAGE = "なにかあったみたいです。確認してみましょう!"

getter subject : Subject
Expand All @@ -47,6 +63,11 @@ module Github
end

def reason_message : String
if subject.commented?
followup = FOLLOWUP_MESSAGES[reason]?
return followup if followup
end

REASON_MESSAGES[reason]? || GENERIC_MESSAGE
end

Expand Down Expand Up @@ -139,6 +160,22 @@ module Github
type.in?(BODY_TYPES) ? url : ""
end

# スレッドにコメントが 1 件以上付いているか。latest_comment_url はスレッドの
# 最新コメントの URL だが、コメントがまだ無いスレッドでは subject.url と同じ
# 値が入る。よって url と異なる値のときだけコメントありと判断できる。
#
# あくまで「コメントが存在するか」であって「今回の通知の起点がコメントか」では
# ない点に注意。latest_comment_url は通知を発生させたイベントではなくスレッドの
# 現在の最新コメントを指すため、コメント済みスレッドに push や状態変更が来た
# 通知でも真になる。通知 payload にイベント種別が無く、追加の API 呼び出し
# 無しでは区別できないので、これを「初回ではない=その後の動き」の目安として
# 使い、文言側はコメントに限定しない表現にしている(issue #104 / PR #106
# レビュー指摘)。
def commented? : Bool
return false unless comment = latest_comment_url.presence
comment != url
end

# subject.url 末尾の PR / Issue / Discussion 番号。番号が意味を持つ type に
# 限り、末尾セグメントが数値なら返す。Commit(末尾が SHA)や末尾スラッシュ、
# URL が無い場合などは nil を返す(issue #96)。
Expand Down
Loading