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
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ wrangler.jsonc Worker 설정과 공개 값(vars)
| `pull_request` (`opened`, `reopened`, `synchronize`) | Week 검사, AI 풀이 분석 check run(`tagPatterns`), 학습 현황 댓글(`postLearningStatus`) |
| `issue_comment`, `pull_request_review_comment` (`created`) | `@dalestudy` 멘션에 응답한다. `approve`·`승인` 이면 PR 을 승인하고, 그 밖에는 AI 리뷰를 단다 |

Week 검사는 제목이 `WEEK NN Solutions` 형태인 풀이 PR 만 대상으로 하고, 경고 댓글은 봇이 단 것만 지운다.
Week 검사는 제목이 `WEEK NN Solutions` 형태인 풀이 PR 만 대상으로 하고, 경고 댓글은 봇이 단 것만 지운다. AI 풀이 분석과 학습 현황 댓글은 제목과 상관없이 돌리고(`[user] Onboarding` 같은 PR 도 포함), 풀이 파일이 없으면 두 핸들러가 AI 를 부르지 않고 건너뛴다.

### AI 작업을 별도 invocation 으로 나누기

Expand Down
19 changes: 9 additions & 10 deletions handlers/webhooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,11 @@ async function handleProjectsV2ItemEvent(payload, env) {

/**
* Pull Request 이벤트 처리
* - opened/reopened: Week 설정 체크 + 알고리즘 패턴 태깅(분석하지 않았거나 변경된 파일만)
* - synchronize: 알고리즘 패턴 태깅만 (분석하지 않았거나 변경된 파일만, Week 체크 스킵)
* - opened/reopened: Week 설정 체크(제목이 WEEK NN 형태인 풀이 PR만) + AI 풀이 분석
* - synchronize: AI 풀이 분석만 (Week 체크 스킵)
*
* AI 풀이 분석과 학습 현황은 제목과 상관없이 돌린다. Onboarding PR 처럼 WEEK 가 없는
* 제목에도 풀이 파일이 있을 수 있고, 풀이 파일이 없으면 두 핸들러가 AI 를 부르지 않고 건너뛴다.
*/
async function handlePullRequestEvent(payload, env, ctx) {
const action = payload.action;
Expand All @@ -230,17 +233,13 @@ async function handlePullRequestEvent(payload, env, ctx) {
const repoName = payload.repository.name;
const prNumber = pr.number;

// 풀이 제출 PR이 아니면 스킵 (early exit - GitHub API 호출 전에)
if (!isSolutionPR(pr.title)) {
console.log(`Skipping PR #${prNumber}: not a solution PR`);
return corsResponse({ message: "Ignored: not a solution PR" });
}

const appToken = await generateGitHubAppToken(env);
let weekValue = null;

// Week 체크는 opened/reopened일 때만 (synchronize는 이미 설정됐을 가능성 높음)
if (action === "opened" || action === "reopened") {
// Week 체크는 풀이 PR 이 opened/reopened 일 때만 (synchronize 는 이미 설정됐을 가능성 높음)
if (!isSolutionPR(pr.title)) {
console.log(`Skipping Week check for PR #${prNumber}: not a WEEK solution PR`);
} else if (action === "opened" || action === "reopened") {
console.log(`New PR ${action}: #${prNumber}`);

// 잠시 대기 후 체크 (프로젝트 추가 시간 고려)
Expand Down
49 changes: 49 additions & 0 deletions handlers/webhooks.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -383,3 +383,52 @@ describe("handlePullRequestEvent — AI 핸들러 디스패치", () => {
});

});

describe("handlePullRequestEvent — WEEK 가 없는 제목", () => {
const onboardingPayload = {
action: "opened",
organization: { login: "DaleStudy" },
repository: {
name: "leetcode-study",
owner: { login: "DaleStudy" },
},
pull_request: {
number: 43,
title: "[testuser] Onboarding",
labels: [],
head: { sha: "head-sha" },
user: { login: "testuser" },
},
};

beforeEach(() => {
vi.clearAllMocks();
globalThis.fetch = vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({}),
});
});

it("Week 체크는 건너뛰지만 AI 핸들러는 디스패치한다", async () => {
const ctx = { waitUntil: vi.fn() };
const env = {
AI: FAKE_AI,
INTERNAL_SECRET: "fake-secret",
WORKER_URL: "https://worker.test",
};

const response = await handleWebhook(
makeRequest("pull_request", onboardingPayload),
env,
ctx
);

expect(response.status).toBe(200);
expect(handleWeekComment).not.toHaveBeenCalled();
expect(ctx.waitUntil).toHaveBeenCalledTimes(2);

const fetchedUrls = globalThis.fetch.mock.calls.map(([url]) => url);
expect(fetchedUrls).toContain("https://worker.test/internal/tag-patterns");
expect(fetchedUrls).toContain("https://worker.test/internal/learning-status");
});
});
Loading