yaju-is-always-watching-you/internal/bot/bot_test.go
awayatan f47fef177b
Some checks failed
CI / test (push) Failing after 4s
Add yaju-is-always-watching-you: Forgejo goroku-check webhook bot
Watches issue/PR comments and pushes for flagged phrases; warns commenters
via reply and opens a redacted correction PR for flagged file content,
never rewriting history or deleting content directly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 22:47:12 +09:00

157 lines
5 KiB
Go

package bot
import (
"testing"
"git.folja.dev/awayatan/yaju-is-always-watching-you/internal/detector"
"git.folja.dev/awayatan/yaju-is-always-watching-you/internal/forgejo"
)
type fakeClient struct {
comments []string
corrections []string
prs int
fileContent map[string]string
}
func (f *fakeClient) ReplyToIssue(owner, repo string, index int64, body string) error {
f.comments = append(f.comments, body)
return nil
}
func (f *fakeClient) FileContent(owner, repo, ref, path string) (string, string, error) {
return f.fileContent[path], "sha-" + path, nil
}
func (f *fakeClient) CommitCorrection(owner, repo, baseBranch, newBranch, path, sha, newContent, message string) error {
f.corrections = append(f.corrections, path)
return nil
}
func (f *fakeClient) OpenCorrectionPR(owner, repo, head, base, title, body string) error {
f.prs++
return nil
}
func newTestBot(fc *fakeClient) *Bot {
det := detector.New([]string{"しょうもな"})
return New(fc, det, "yaju-keisatsu-bot", "bot/goroku-fix/", nil)
}
func TestHandleIssueComment_Flagged(t *testing.T) {
fc := &fakeClient{}
b := newTestBot(fc)
err := b.HandleIssueComment(forgejo.IssueCommentPayload{
Action: "created",
Issue: forgejo.WebhookIssue{Number: 1},
Comment: forgejo.WebhookComment{Body: "それはしょうもな", User: forgejo.WebhookUser{Login: "alice"}},
Repository: forgejo.WebhookRepository{Name: "repo", Owner: forgejo.WebhookUser{Login: "owner"}},
Sender: forgejo.WebhookUser{Login: "alice"},
})
if err != nil {
t.Fatalf("HandleIssueComment() error = %v", err)
}
if len(fc.comments) != 1 {
t.Fatalf("expected 1 warning comment, got %d", len(fc.comments))
}
}
func TestHandleIssueComment_IgnoresSelf(t *testing.T) {
fc := &fakeClient{}
b := newTestBot(fc)
err := b.HandleIssueComment(forgejo.IssueCommentPayload{
Action: "created",
Comment: forgejo.WebhookComment{Body: "それはしょうもな", User: forgejo.WebhookUser{Login: "yaju-keisatsu-bot"}},
Repository: forgejo.WebhookRepository{Name: "repo", Owner: forgejo.WebhookUser{Login: "owner"}},
})
if err != nil {
t.Fatalf("HandleIssueComment() error = %v", err)
}
if len(fc.comments) != 0 {
t.Fatalf("bot must not reply to its own comments, got %d replies", len(fc.comments))
}
}
func TestHandleIssueComment_Clean(t *testing.T) {
fc := &fakeClient{}
b := newTestBot(fc)
err := b.HandleIssueComment(forgejo.IssueCommentPayload{
Action: "created",
Comment: forgejo.WebhookComment{Body: "普通のコメントです", User: forgejo.WebhookUser{Login: "alice"}},
Repository: forgejo.WebhookRepository{Name: "repo", Owner: forgejo.WebhookUser{Login: "owner"}},
Sender: forgejo.WebhookUser{Login: "alice"},
})
if err != nil {
t.Fatalf("HandleIssueComment() error = %v", err)
}
if len(fc.comments) != 0 {
t.Fatalf("expected no reply for clean comment, got %d", len(fc.comments))
}
}
func TestHandlePush_OpensCorrectionPR(t *testing.T) {
fc := &fakeClient{fileContent: map[string]string{
"README.md": "これはしょうもな話だ",
}}
b := newTestBot(fc)
err := b.HandlePush(forgejo.PushPayload{
Ref: "refs/heads/main",
After: "abcdef1234567890",
Commits: []forgejo.WebhookCommit{
{ID: "abcdef1234567890", Modified: []string{"README.md"}},
},
Repository: forgejo.WebhookRepository{Name: "repo", Owner: forgejo.WebhookUser{Login: "owner"}},
Pusher: forgejo.WebhookUser{Login: "alice"},
})
if err != nil {
t.Fatalf("HandlePush() error = %v", err)
}
if len(fc.corrections) != 1 || fc.corrections[0] != "README.md" {
t.Fatalf("expected a correction commit for README.md, got %v", fc.corrections)
}
if fc.prs != 1 {
t.Fatalf("expected 1 correction PR, got %d", fc.prs)
}
}
func TestHandlePush_IgnoresSelf(t *testing.T) {
fc := &fakeClient{fileContent: map[string]string{"README.md": "しょうもな"}}
b := newTestBot(fc)
err := b.HandlePush(forgejo.PushPayload{
Ref: "refs/heads/main",
After: "abc",
Commits: []forgejo.WebhookCommit{{ID: "abc", Modified: []string{"README.md"}}},
Repository: forgejo.WebhookRepository{Name: "repo", Owner: forgejo.WebhookUser{Login: "owner"}},
Pusher: forgejo.WebhookUser{Login: "yaju-keisatsu-bot"},
})
if err != nil {
t.Fatalf("HandlePush() error = %v", err)
}
if fc.prs != 0 {
t.Fatalf("bot must not react to its own pushes, got %d PRs", fc.prs)
}
}
func TestHandlePush_CleanFileNoOp(t *testing.T) {
fc := &fakeClient{fileContent: map[string]string{"README.md": "普通のREADMEです"}}
b := newTestBot(fc)
err := b.HandlePush(forgejo.PushPayload{
Ref: "refs/heads/main",
After: "abc",
Commits: []forgejo.WebhookCommit{{ID: "abc", Modified: []string{"README.md"}}},
Repository: forgejo.WebhookRepository{Name: "repo", Owner: forgejo.WebhookUser{Login: "owner"}},
Pusher: forgejo.WebhookUser{Login: "alice"},
})
if err != nil {
t.Fatalf("HandlePush() error = %v", err)
}
if fc.prs != 0 || len(fc.corrections) != 0 {
t.Fatalf("expected no correction for clean file, got prs=%d corrections=%v", fc.prs, fc.corrections)
}
}