mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/jxxghp/MoviePilot.git
synced 2026-09-20 08:03:34 +08:00
172 lines
5.3 KiB
JavaScript
172 lines
5.3 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import test from 'node:test'
|
|
import { reuse } from './reuse.mjs'
|
|
|
|
/** 构造带真实 GitHub 字段形状的离线 API 替身。 */
|
|
function fixture() {
|
|
const head = 'a'.repeat(40),
|
|
base = 'b'.repeat(40),
|
|
sha = 'c'.repeat(40),
|
|
merge = 'd'.repeat(40)
|
|
const state = {
|
|
context: {
|
|
eventName: 'push',
|
|
repo: { owner: 'owner', repo: 'repo' },
|
|
ref: 'refs/heads/v3',
|
|
sha,
|
|
runId: 20,
|
|
payload: { before: base },
|
|
},
|
|
pull: {
|
|
number: 7,
|
|
merged_at: '2026-09-09T00:00:00Z',
|
|
merge_commit_sha: sha,
|
|
head: { sha: head, repo: { id: 2 } },
|
|
base: { ref: 'v3', repo: { full_name: 'owner/repo' } },
|
|
},
|
|
runs: [
|
|
{
|
|
id: 10,
|
|
run_attempt: 1,
|
|
status: 'completed',
|
|
conclusion: 'success',
|
|
head_sha: head,
|
|
head_repository: { id: 2 },
|
|
html_url: 'https://github.com/owner/repo/actions/runs/10',
|
|
},
|
|
],
|
|
jobs: [{ name: `CI proof (${merge})`, status: 'completed', conclusion: 'success' }],
|
|
tested: { parents: [{ sha: base }, { sha: head }], tree: { sha: 'tree' } },
|
|
outputs: {},
|
|
calls: [],
|
|
warnings: [],
|
|
}
|
|
/** 保留 API 参数供测试验证,避免错误地过滤历史失败记录。 */
|
|
const api = name => async args => {
|
|
state.calls.push({ name, args })
|
|
if (state.error) throw new Error('API unavailable')
|
|
if (name === 'getWorkflowRun')
|
|
return { data: args.run_id === 20 ? { workflow_id: 100 } : { ...state.runs[0], ...state.refreshed } }
|
|
if (name === 'getCommit') return { data: args.commit_sha === sha ? { tree: { sha: 'tree' } } : state.tested }
|
|
if (name === 'listPullRequestsAssociatedWithCommit') return [state.pull]
|
|
if (name === 'listWorkflowRuns') return { data: { workflow_runs: state.runs } }
|
|
if (name === 'listJobsForWorkflowRun') return state.jobs
|
|
throw new Error(name)
|
|
}
|
|
state.github = {
|
|
rest: {
|
|
actions: {
|
|
getWorkflowRun: api('getWorkflowRun'),
|
|
listWorkflowRuns: api('listWorkflowRuns'),
|
|
listJobsForWorkflowRun: api('listJobsForWorkflowRun'),
|
|
},
|
|
git: { getCommit: api('getCommit') },
|
|
repos: { listPullRequestsAssociatedWithCommit: api('listPullRequestsAssociatedWithCommit') },
|
|
},
|
|
paginate: (method, args) => method(args),
|
|
}
|
|
state.core = {
|
|
setOutput: (key, value) => {
|
|
state.outputs[key] = value
|
|
},
|
|
notice: () => {},
|
|
info: () => {},
|
|
warning: message => state.warnings.push(message),
|
|
}
|
|
return state
|
|
}
|
|
|
|
test('相同合并代码且完整成功时复用,即使 fork run 没有 pull_requests 字段', async () => {
|
|
const state = fixture()
|
|
await reuse(state)
|
|
assert.equal(state.outputs.reuse, 'true')
|
|
const query = state.calls.find(call => call.name === 'listWorkflowRuns').args
|
|
assert.equal(query.workflow_id, 100)
|
|
assert.equal(query.event, 'pull_request')
|
|
assert.equal(query.status, undefined)
|
|
assert.equal(state.calls.find(call => call.name === 'listJobsForWorkflowRun').args.filter, 'latest')
|
|
})
|
|
|
|
const cases = {
|
|
'PR 必须完整执行': state => {
|
|
state.context.eventName = 'pull_request'
|
|
},
|
|
'手工触发必须完整执行': state => {
|
|
state.context.eventName = 'workflow_dispatch'
|
|
},
|
|
'force push 必须完整执行': state => {
|
|
state.context.payload.forced = true
|
|
},
|
|
'新建分支必须完整执行': state => {
|
|
state.context.payload.before = '0'.repeat(40)
|
|
},
|
|
'直接 push 没有对应合并': state => {
|
|
state.pull.merge_commit_sha = 'other'
|
|
},
|
|
'未合并 PR 不可复用': state => {
|
|
state.pull.merged_at = null
|
|
},
|
|
'目标分支不同': state => {
|
|
state.pull.base.ref = 'other'
|
|
},
|
|
'目标仓库不同': state => {
|
|
state.pull.base.repo.full_name = 'other/repo'
|
|
},
|
|
'PR head 已改变': state => {
|
|
state.pull.head.sha = 'e'.repeat(40)
|
|
},
|
|
'PR 来源仓库不同': state => {
|
|
state.pull.head.repo.id = 9
|
|
},
|
|
'旧工作流没有 proof': state => {
|
|
state.jobs = []
|
|
},
|
|
'proof 被跳过': state => {
|
|
state.jobs[0].conclusion = 'skipped'
|
|
},
|
|
'proof 尚未完成': state => {
|
|
state.jobs[0].status = 'in_progress'
|
|
},
|
|
'proof 重复': state => {
|
|
state.jobs.push({ ...state.jobs[0] })
|
|
},
|
|
'工作流有失败': state => {
|
|
state.runs[0].conclusion = 'failure'
|
|
},
|
|
'工作流尚未完成': state => {
|
|
state.runs[0].status = 'in_progress'
|
|
},
|
|
'最新执行失败不可退回旧成功': state => {
|
|
state.runs.push({ ...state.runs[0], id: 11, conclusion: 'failure' })
|
|
},
|
|
'查询期间开始重跑': state => {
|
|
state.refreshed = { status: 'in_progress', conclusion: null }
|
|
},
|
|
'查询期间运行轮次变化': state => {
|
|
state.refreshed = { run_attempt: 2 }
|
|
},
|
|
'目标分支已经前进': state => {
|
|
state.tested.parents[0].sha = 'e'.repeat(40)
|
|
},
|
|
'证明不是 PR 模拟合并': state => {
|
|
state.tested.parents.pop()
|
|
},
|
|
'模拟合并来自其他 PR head': state => {
|
|
state.tested.parents[1].sha = 'e'.repeat(40)
|
|
},
|
|
'合入代码树有变化': state => {
|
|
state.tested.tree.sha = 'different'
|
|
},
|
|
'API 异常回退全量': state => {
|
|
state.error = true
|
|
},
|
|
}
|
|
for (const [name, mutate] of Object.entries(cases)) {
|
|
test(name, async () => {
|
|
const state = fixture()
|
|
mutate(state)
|
|
await reuse(state)
|
|
assert.equal(state.outputs.reuse, 'false')
|
|
})
|
|
}
|