qwen-code/scripts/tests/pr-force-push-reminder-workflow.test.js
易良 e743f0e2e0
ci(autofix): run agents on dedicated ECS runners (#6207)
* ci(autofix): run agents on dedicated ECS runners

* ci(autofix): retry repository checkout

* ci(autofix): use local qwen bundle fallback

* ci(autofix): harden ECS runner failure handling

* ci(autofix): retry transient qwen runner failures

* fix(ci): proxy autofix model credentials

* fix(ci): limit ECS autofix to ready issues

* fix(ci): address autofix workflow review feedback

* fix(ci): restore autofix known bots

* chore(ci): trim autofix ECS scope

* fix(ci): consolidate autofix OpenAI proxy

* fix(ci): restrict autofix agent execution tools

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-03 07:40:07 +00:00

124 lines
5 KiB
JavaScript

/**
* @license
* Copyright 2025 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
import { readFileSync } from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
const repoRoot = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
'../..',
);
describe('pr force-push reminder workflow', () => {
const workflow = readFileSync(
path.join(repoRoot, '.github/workflows/pr-force-push-reminder.yml'),
'utf8',
);
it('triggers only on pull_request_target synchronize', () => {
expect(workflow).toContain('pull_request_target:');
expect(workflow).toContain("- 'synchronize'");
// Must not check out or run PR code: a github-script-only job needs no
// checkout, which is what keeps pull_request_target safe from pwn-requests.
expect(workflow).not.toContain('actions/checkout');
});
it('only runs on the upstream repo', () => {
expect(workflow).toContain("github.repository == 'QwenLM/qwen-code'");
});
it('grants the permissions the comment endpoints need', () => {
expect(workflow).toContain("contents: 'read'");
expect(workflow).toContain("issues: 'write'");
expect(workflow).toContain("pull-requests: 'write'");
});
it('uses no concurrency group so no push event is ever dropped', () => {
// GitHub keeps at most one pending run per concurrency group, so a group
// could cancel a still-pending force-push run before it posts. Idempotency
// comes from the marker instead, so there must be no concurrency block.
expect(workflow).not.toContain('concurrency:');
expect(workflow).not.toContain('cancel-in-progress');
});
it('bounds the job and pins the github-script action by SHA', () => {
expect(workflow).toContain('timeout-minutes: 5');
expect(workflow).toContain(
'actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3',
);
});
it('guards against missing or zero before/after SHAs', () => {
expect(workflow).toContain('!before || !after || /^0+$/.test(before)');
});
it('skips bot and known-automation pushes', () => {
// GitHub Apps arrive as sender.type Bot; the autofix bot force-pushes via a
// PAT as a User account, so its login must be skipped explicitly.
expect(workflow).toContain(
"sender?.type === 'Bot' || KNOWN_AUTOMATION.has(sender?.login)",
);
for (const login of [
'qwen-code-ci-bot',
'qwen-code-dev-bot',
'github-actions',
'github-actions[bot]',
'gemini-cli-robot',
]) {
expect(workflow).toContain(`'${login}'`);
}
});
it('detects force-pushes with a 3-dot compare on the base repo', () => {
// Verified against the live REST API: 3-dot returns diverged/behind for
// force-pushes and resolves fork-PR commits, while 2-dot 404s. Do not
// "simplify" this to two dots.
expect(workflow).toContain('basehead: `${before}...${after}`');
expect(workflow).not.toContain('basehead: `${before}..${after}`');
// The base repo resolves fork-PR commits via refs/pull/N/head, so the
// compare targets context.repo, not the (possibly deleted) head repo.
expect(workflow).not.toContain('pr.head.repo');
// Only ahead/identical is a normal push; behind/diverged is a force-push.
expect(workflow).toContain("status === 'ahead' || status === 'identical'");
});
it('skips on a 404 compare but surfaces other errors', () => {
// A 404 means the old tip was orphaned by the force-push; anything else
// (403/429/5xx) must fail the run loudly instead of a silent green no-op.
expect(workflow).toContain('if (err.status === 404)');
expect(workflow).toContain('throw err;');
expect(workflow).not.toContain('Could not compare');
});
it('only trusts the dedup marker on its own bot comment', () => {
// Otherwise any user could suppress all future reminders by pasting the
// marker string into a comment.
expect(workflow).toContain('<!-- pr-force-push-reminder -->');
expect(workflow).toContain("c.user?.type === 'Bot'");
expect(workflow).toContain("c.user?.login === 'github-actions[bot]'");
// Assert the skip path itself, so deleting the guard fails a test.
expect(workflow).toContain(
'Reminder already posted by the bot on this PR; skipping.',
);
});
it('wraps every GitHub write/read in error logging that rethrows', () => {
// listComments, compare (via the 404 branch), and createComment must all
// surface failures rather than swallowing them.
expect(workflow).toContain('Failed to list comments on PR #${pr.number}');
expect(workflow).toContain('Failed to comment on PR #${pr.number}');
expect(workflow).toContain('core.error(');
});
it('posts a bilingual reminder', () => {
expect(workflow).toContain('Please do not rebase or force-push');
expect(workflow).toContain('squash all changes into a single commit');
expect(workflow).toContain('<summary>中文</summary>');
expect(workflow).toContain('请勿对活跃的 PR 执行 rebase 或 force-push');
});
});