mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
372 lines
11 KiB
JavaScript
372 lines
11 KiB
JavaScript
import assert from 'node:assert/strict'
|
||
import test from 'node:test'
|
||
|
||
import {
|
||
countVisibleUnits,
|
||
nextResolvingIssueStatus,
|
||
parseReferences,
|
||
retainIssueReferences,
|
||
requiresPullRequestPolicy,
|
||
validateBody,
|
||
validateIssue,
|
||
validatePullRequest,
|
||
} from './policy.mjs'
|
||
|
||
const withDetails = (summary) =>
|
||
`${summary}\n\n<details><summary>验收与细节</summary>待补充。</details>`
|
||
|
||
const legalIssue = {
|
||
title: '完成议题管理校验',
|
||
body: withDetails('完成议题管理校验。'),
|
||
assignees: [],
|
||
labels: [],
|
||
type: 'Idea',
|
||
priority: null,
|
||
status: 'In review',
|
||
state: 'open',
|
||
stateReason: null,
|
||
}
|
||
|
||
const canonicalKinds = [
|
||
'kind/feature',
|
||
'kind/bug-fix',
|
||
'kind/doc',
|
||
'kind/testing',
|
||
'kind/cleanup',
|
||
'kind/dependency',
|
||
]
|
||
|
||
// Keep an independent oracle rather than importing the implementation's reserved set.
|
||
const legacyLabels = [
|
||
'kind/bug',
|
||
'kind/documentation',
|
||
'feature',
|
||
'bug-fix',
|
||
'doc',
|
||
'cleanup',
|
||
'testing',
|
||
'dependencies',
|
||
'ci',
|
||
'cli',
|
||
'llm',
|
||
'web-search',
|
||
]
|
||
|
||
const reviewedPull = (labels) => ({
|
||
isDraft: false,
|
||
authorType: 'User',
|
||
reviewRequestCount: 1,
|
||
reviewCount: 0,
|
||
labels,
|
||
references: { all: [2], resolving: [], related: [2] },
|
||
issues: new Map([[2, { priority: null }]]),
|
||
})
|
||
|
||
test('counts only text outside details', () => {
|
||
assert.deepEqual(countVisibleUnits('支持 GitHub Project。<details>隐藏文字</details>'), {
|
||
units: 4,
|
||
balanced: true,
|
||
detailsCount: 1,
|
||
allCollapsed: true,
|
||
})
|
||
})
|
||
|
||
test('requires a balanced default-collapsed details region', () => {
|
||
assert.deepEqual(validateBody({ body: '完成工作。', assignees: [] }), [
|
||
'正文必须包含默认收起的 <details> 区域',
|
||
])
|
||
assert.deepEqual(
|
||
validateBody({
|
||
body: '完成工作。\n\n<details open><summary>细节</summary>待补充。</details>',
|
||
assignees: [],
|
||
}),
|
||
['details 必须默认收起,不得设置 open'],
|
||
)
|
||
assert.deepEqual(
|
||
validateBody({ body: '完成工作。\n\n<details><summary>细节</summary>', assignees: [] }),
|
||
['details 标签必须成对闭合'],
|
||
)
|
||
})
|
||
|
||
test('requires Owner for multiple assignees', () => {
|
||
assert.deepEqual(
|
||
validateBody({
|
||
body: withDetails('完成工作。'),
|
||
assignees: ['tianyicui', 'tianyicui-bot'],
|
||
}),
|
||
['多个 Assignees 时首个非空行必须是 Owner: @login'],
|
||
)
|
||
})
|
||
|
||
test('accepts an intended Owner while assignment permission is pending', () => {
|
||
assert.deepEqual(
|
||
validateBody({
|
||
body: withDetails('Owner: @octocat\n\n完成工作。'),
|
||
assignees: [],
|
||
}),
|
||
[],
|
||
)
|
||
assert.deepEqual(
|
||
validateBody({
|
||
body: withDetails('Owner: @octocat\n\n完成工作。'),
|
||
assignees: ['hubot'],
|
||
}),
|
||
['零或一个 Assignee 时不得写 Owner 行'],
|
||
)
|
||
})
|
||
|
||
test('allows optional metadata in every open Status', () => {
|
||
assert.deepEqual(validateIssue(legalIssue), [])
|
||
for (const status of ['Inbox', 'Backlog', 'Ready', 'In progress', 'In review']) {
|
||
assert.deepEqual(validateIssue({ ...legalIssue, status }), [])
|
||
}
|
||
})
|
||
|
||
test('rejects metadata prefixes in an Issue title', () => {
|
||
const errors = validateIssue({ ...legalIssue, title: '[Bug] 修复恢复错误' })
|
||
assert.ok(errors.includes('Issue 标题不得带 Type、Priority、Status、area 或 Owner 前缀'))
|
||
})
|
||
|
||
test('reserves PR kind and legacy labels for pull requests', () => {
|
||
for (const label of [
|
||
...canonicalKinds,
|
||
'kind/experimental',
|
||
...legacyLabels,
|
||
]) {
|
||
assert.ok(
|
||
validateIssue({ ...legalIssue, labels: [label] }).some((error) =>
|
||
error.startsWith('Issue 不得使用 PR kind 或旧版标签:'),
|
||
),
|
||
label,
|
||
)
|
||
}
|
||
assert.deepEqual(validateIssue({ ...legalIssue, labels: ['area/web', 'source/member'] }), [])
|
||
})
|
||
|
||
test('keeps terminal Status aligned with the native close reason', () => {
|
||
assert.deepEqual(
|
||
validateIssue({ ...legalIssue, status: 'Done', state: 'closed', stateReason: 'completed' }),
|
||
[],
|
||
)
|
||
assert.deepEqual(
|
||
validateIssue({
|
||
...legalIssue,
|
||
status: 'No action',
|
||
state: 'closed',
|
||
stateReason: 'not_planned',
|
||
}),
|
||
[],
|
||
)
|
||
assert.ok(validateIssue({ ...legalIssue, status: 'Done' }).includes('Done 必须对应 Completed 关闭原因'))
|
||
})
|
||
|
||
test('separates resolving and informational references', () => {
|
||
assert.deepEqual(
|
||
parseReferences({
|
||
body: 'Fixes #12\nRelated to #4\nRefs deepseekharness/dsh-test#7',
|
||
repository: 'deepseekharness/dsh-test',
|
||
}),
|
||
{ all: [4, 7, 12], resolving: [12], related: [4, 7] },
|
||
)
|
||
})
|
||
|
||
test('does not treat pull request references as Issue associations', () => {
|
||
const references = {
|
||
all: [123, 1180, 1181],
|
||
resolving: [123, 1180],
|
||
related: [1181],
|
||
}
|
||
const issues = new Map([
|
||
[1180, {}],
|
||
[1181, {}],
|
||
])
|
||
|
||
assert.deepEqual(retainIssueReferences(references, issues), {
|
||
all: [1180, 1181],
|
||
resolving: [1180],
|
||
related: [1181],
|
||
})
|
||
})
|
||
|
||
test('allows informational references without cross-object constraints', () => {
|
||
const errors = validatePullRequest({
|
||
isDraft: false,
|
||
authorType: 'User',
|
||
reviewRequestCount: 1,
|
||
reviewCount: 0,
|
||
labels: ['kind/cleanup', 'area/infra'],
|
||
references: { all: [4], resolving: [], related: [4] },
|
||
issues: new Map([[4, { type: 'Bug', priority: 'P0', labels: ['area/web'] }]]),
|
||
})
|
||
assert.deepEqual(errors, [])
|
||
})
|
||
|
||
test('enforces highest resolving Priority without Type or area synchronization', () => {
|
||
const pull = {
|
||
isDraft: false,
|
||
authorType: 'User',
|
||
reviewRequestCount: 0,
|
||
reviewCount: 1,
|
||
labels: ['kind/cleanup', 'p0', 'area/web'],
|
||
references: { all: [2, 3], resolving: [2, 3], related: [] },
|
||
issues: new Map([
|
||
[2, { type: 'Feature', priority: 'P2', labels: ['area/web'] }],
|
||
[3, { type: 'Bug', priority: 'P0', labels: ['area/session'] }],
|
||
]),
|
||
}
|
||
assert.deepEqual(validatePullRequest(pull), [])
|
||
assert.ok(
|
||
validatePullRequest({ ...pull, labels: ['kind/cleanup', 'p2', 'area/web'] }).includes(
|
||
'PR Priority 应为 p0',
|
||
),
|
||
)
|
||
})
|
||
|
||
test('requires policy only after a human PR enters review', () => {
|
||
assert.equal(
|
||
requiresPullRequestPolicy({
|
||
isDraft: false,
|
||
authorType: 'User',
|
||
reviewRequestCount: 1,
|
||
reviewCount: 0,
|
||
}),
|
||
true,
|
||
)
|
||
assert.equal(
|
||
requiresPullRequestPolicy({
|
||
isDraft: false,
|
||
authorType: 'User',
|
||
reviewRequestCount: 0,
|
||
reviewCount: 0,
|
||
}),
|
||
false,
|
||
)
|
||
})
|
||
|
||
test('advances resolving Issues to the live PR phase', () => {
|
||
const draft = { isDraft: true, reviewRequestCount: 1, reviewCount: 4 }
|
||
const open = { isDraft: false, reviewRequestCount: 0, reviewCount: 0 }
|
||
const requestedReview = { isDraft: false, reviewRequestCount: 1, reviewCount: 0 }
|
||
const submittedReview = { isDraft: false, reviewRequestCount: 0, reviewCount: 1 }
|
||
|
||
for (const status of ['Inbox', 'Backlog', 'Ready']) {
|
||
assert.equal(nextResolvingIssueStatus(status, draft), 'In progress')
|
||
assert.equal(nextResolvingIssueStatus(status, open), 'In progress')
|
||
assert.equal(nextResolvingIssueStatus(status, requestedReview), 'In review')
|
||
assert.equal(nextResolvingIssueStatus(status, submittedReview), 'In review')
|
||
}
|
||
assert.equal(nextResolvingIssueStatus('In progress', requestedReview), 'In review')
|
||
assert.equal(nextResolvingIssueStatus('In progress', submittedReview), 'In review')
|
||
})
|
||
|
||
test('never regresses or reopens a resolving Issue', () => {
|
||
const implementation = { isDraft: false, reviewRequestCount: 0, reviewCount: 0 }
|
||
const review = { isDraft: false, reviewRequestCount: 0, reviewCount: 1 }
|
||
|
||
assert.equal(nextResolvingIssueStatus('In progress', implementation), null)
|
||
assert.equal(nextResolvingIssueStatus('In review', implementation), null)
|
||
assert.equal(nextResolvingIssueStatus('In review', review), null)
|
||
assert.equal(nextResolvingIssueStatus('Done', review), null)
|
||
assert.equal(nextResolvingIssueStatus('No action', review), null)
|
||
assert.equal(nextResolvingIssueStatus(null, review), null)
|
||
})
|
||
|
||
test('keeps lifecycle projection independent of PR metadata enforcement', () => {
|
||
const pull = {
|
||
isDraft: false,
|
||
authorType: 'User',
|
||
reviewRequestCount: 1,
|
||
reviewCount: 0,
|
||
labels: [],
|
||
references: { all: [2], resolving: [2], related: [] },
|
||
issues: new Map([[2, { priority: null }]]),
|
||
}
|
||
|
||
assert.ok(validatePullRequest(pull).length > 0)
|
||
assert.equal(nextResolvingIssueStatus('Inbox', pull), 'In review')
|
||
})
|
||
|
||
test('exempts Draft, Bot, and App PRs', () => {
|
||
const invalid = {
|
||
isDraft: false,
|
||
labels: [],
|
||
references: { all: [], resolving: [], related: [] },
|
||
issues: new Map(),
|
||
reviewRequestCount: 1,
|
||
reviewCount: 0,
|
||
}
|
||
assert.deepEqual(validatePullRequest({ ...invalid, authorType: 'Bot' }), [])
|
||
assert.deepEqual(validatePullRequest({ ...invalid, authorType: 'App' }), [])
|
||
assert.deepEqual(validatePullRequest({ ...invalid, authorType: 'User', isDraft: true }), [])
|
||
assert.ok(validatePullRequest({ ...invalid, authorType: 'User' }).length > 0)
|
||
})
|
||
|
||
test('requires repository PR labels in the enforcement scope', () => {
|
||
const errors = validatePullRequest({
|
||
isDraft: false,
|
||
authorType: 'User',
|
||
reviewRequestCount: 1,
|
||
reviewCount: 0,
|
||
labels: [],
|
||
references: { all: [2], resolving: [], related: [2] },
|
||
issues: new Map([[2, { priority: null }]]),
|
||
})
|
||
assert.ok(errors.includes('PR 必须恰好有一个允许的 kind/*,当前为 0'))
|
||
assert.ok(errors.includes('PR 必须至少有一个 area/*'))
|
||
})
|
||
|
||
test('accepts exactly the canonical kinds with extensible areas', () => {
|
||
for (const kind of canonicalKinds) {
|
||
assert.deepEqual(validatePullRequest(reviewedPull([kind, 'area/future-domain'])), [], kind)
|
||
}
|
||
})
|
||
|
||
test('rejects multiple, unknown, legacy, and Issue-source PR labels', () => {
|
||
assert.ok(
|
||
validatePullRequest(
|
||
reviewedPull(['kind/feature', 'kind/doc', 'area/web']),
|
||
).includes('PR 必须恰好有一个允许的 kind/*,当前为 2'),
|
||
)
|
||
assert.ok(
|
||
validatePullRequest(reviewedPull(['kind/experimental', 'area/web'])).includes(
|
||
'PR 含不支持的 kind/*:kind/experimental',
|
||
),
|
||
)
|
||
for (const label of legacyLabels) {
|
||
assert.ok(
|
||
validatePullRequest(reviewedPull(['kind/feature', 'area/web', label])).some((error) =>
|
||
error.startsWith('PR 含旧版标签:'),
|
||
),
|
||
label,
|
||
)
|
||
}
|
||
assert.ok(
|
||
validatePullRequest(
|
||
reviewedPull(['kind/feature', 'area/web', 'source/internal-pr']),
|
||
).includes('source/* 仅用于 Issue:source/internal-pr'),
|
||
)
|
||
})
|
||
|
||
test('allows missing Priority only when resolving Issues are also unprioritized', () => {
|
||
const pull = {
|
||
isDraft: false,
|
||
authorType: 'User',
|
||
reviewRequestCount: 1,
|
||
reviewCount: 0,
|
||
labels: ['kind/feature', 'area/web'],
|
||
references: { all: [2], resolving: [2], related: [] },
|
||
issues: new Map([[2, { priority: null }]]),
|
||
}
|
||
assert.deepEqual(validatePullRequest(pull), [])
|
||
assert.ok(
|
||
validatePullRequest({ ...pull, issues: new Map([[2, { priority: 'P2' }]]) }).includes(
|
||
'PR Priority 应为 p2',
|
||
),
|
||
)
|
||
assert.ok(
|
||
validatePullRequest({ ...pull, labels: [...pull.labels, 'p2'] }).includes(
|
||
'有 Priority 的解决型 PR 要求每个被解决 Issue 都设置 Priority',
|
||
),
|
||
)
|
||
})
|