♻️ refactor(vscode): 代码托管仓库抽成统一接口,Gitea 实现搬到 provider 后面,翻页改看 Link 头
This commit is contained in:
1 parent
c90d84d7f2
commit
14bee9f505
64 files changed
+1881
-2718
No files matched your search
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { cleanCommentSummary, systemCommentText } from '../../src/gitea/issueComment'
|
||||
import { cleanCommentSummary, systemCommentText } from '../../src/forge/issueComment'
|
||||
|
||||
describe('cleanCommentSummary', () => {
|
||||
it('保留普通单行纯文本', () => {
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { ForgeProvider } from '../../src/forge/types'
|
||||
import { Buffer } from 'node:buffer'
|
||||
import { EventEmitter } from 'node:events'
|
||||
import { delimiter } from 'node:path'
|
||||
@@ -61,36 +62,24 @@ vi.mock('../../src/cc/claudeBin', () => ({
|
||||
|
||||
const listIssueComments = vi.fn()
|
||||
const postIssueComment = vi.fn()
|
||||
const provider = { listIssueComments, postIssueComment } as unknown as ForgeProvider
|
||||
|
||||
vi.mock('../../src/gitea/api', () => ({
|
||||
listIssueComments,
|
||||
postIssueComment,
|
||||
}))
|
||||
|
||||
describe('mergeStateJsonCommentGuarded', () => {
|
||||
describe('mergeForgeStateGuarded', () => {
|
||||
beforeEach(() => {
|
||||
listIssueComments.mockReset()
|
||||
postIssueComment.mockReset()
|
||||
})
|
||||
|
||||
it('keeps done column when an automatic flow tries to merge review while preserving other fields', async () => {
|
||||
const { mergeStateJsonCommentGuarded } = await import('../../src/gitea/stateJson.js')
|
||||
const { mergeForgeStateGuarded } = await import('../../src/forge/state.js')
|
||||
listIssueComments.mockResolvedValue([
|
||||
{ body: JSON.stringify({ column: 'done', implementStatus: 'done', prMerged: true }) },
|
||||
])
|
||||
|
||||
await mergeStateJsonCommentGuarded({
|
||||
host: 'https://gitea.example',
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
token: 'token',
|
||||
issueNumber: 123,
|
||||
protectDoneColumn: true,
|
||||
extra: { column: 'review', reviewSessionId: 'review-1' },
|
||||
})
|
||||
await mergeForgeStateGuarded(provider, 123, { column: 'review', reviewSessionId: 'review-1' }, { protectDoneColumn: true })
|
||||
|
||||
expect(postIssueComment).toHaveBeenCalledOnce()
|
||||
expect(parseStateComment(postIssueComment.mock.calls[0][0].body)).toEqual({
|
||||
expect(parseStateComment(postIssueComment.mock.calls[0][1])).toEqual({
|
||||
column: 'done',
|
||||
implementStatus: 'done',
|
||||
prMerged: true,
|
||||
@@ -99,20 +88,12 @@ describe('mergeStateJsonCommentGuarded', () => {
|
||||
})
|
||||
|
||||
it('skips posting when protected done would only merge a stale column', async () => {
|
||||
const { mergeStateJsonCommentGuarded } = await import('../../src/gitea/stateJson.js')
|
||||
const { mergeForgeStateGuarded } = await import('../../src/forge/state.js')
|
||||
listIssueComments.mockResolvedValue([
|
||||
{ body: JSON.stringify({ column: 'done', implementStatus: 'done' }) },
|
||||
])
|
||||
|
||||
await mergeStateJsonCommentGuarded({
|
||||
host: 'https://gitea.example',
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
token: 'token',
|
||||
issueNumber: 123,
|
||||
protectDoneColumn: true,
|
||||
extra: { column: 'review' },
|
||||
})
|
||||
await mergeForgeStateGuarded(provider, 123, { column: 'review' }, { protectDoneColumn: true })
|
||||
|
||||
expect(postIssueComment).not.toHaveBeenCalled()
|
||||
})
|
||||
@@ -125,7 +106,7 @@ describe('state JSON 尾部普通评论不丢状态', () => {
|
||||
})
|
||||
|
||||
it('merge 时跳过尾部普通文本评论,保留完整历史 state', async () => {
|
||||
const { mergeStateJsonComment } = await import('../../src/gitea/stateJson.js')
|
||||
const { mergeForgeState } = await import('../../src/forge/state.js')
|
||||
const fullState = {
|
||||
color: 'terminal.ansiCyan',
|
||||
column: 'in-progress',
|
||||
@@ -143,21 +124,14 @@ describe('state JSON 尾部普通评论不丢状态', () => {
|
||||
{ body: JSON.stringify({ note: '像 JSON 但没有任何已知 state 字段' }) },
|
||||
]
|
||||
listIssueComments.mockImplementation(async () => [...comments])
|
||||
postIssueComment.mockImplementation(async (opts: { body: string }) => {
|
||||
comments.push({ body: opts.body })
|
||||
postIssueComment.mockImplementation(async (_issueNumber: number, body: string) => {
|
||||
comments.push({ body })
|
||||
})
|
||||
|
||||
await mergeStateJsonComment({
|
||||
host: 'https://gitea.example',
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
token: 'token',
|
||||
issueNumber: 182,
|
||||
extra: { pr: '182', implementStatus: 'done' },
|
||||
})
|
||||
await mergeForgeState(provider, 182, { pr: '182', implementStatus: 'done' })
|
||||
|
||||
expect(postIssueComment).toHaveBeenCalledOnce()
|
||||
expect(parseStateComment(postIssueComment.mock.calls[0][0].body)).toEqual({
|
||||
expect(parseStateComment(postIssueComment.mock.calls[0][1])).toEqual({
|
||||
...fullState,
|
||||
pr: '182',
|
||||
implementStatus: 'done',
|
||||
@@ -165,53 +139,40 @@ describe('state JSON 尾部普通评论不丢状态', () => {
|
||||
})
|
||||
|
||||
it('post 后读回发现字段被并发覆盖时,在最新状态上重放一次', async () => {
|
||||
const { mergeStateJsonComment } = await import('../../src/gitea/stateJson.js')
|
||||
const { mergeForgeState } = await import('../../src/forge/state.js')
|
||||
const comments: Array<{ body: string }> = [
|
||||
{ body: JSON.stringify({ column: 'in-progress' }) },
|
||||
]
|
||||
listIssueComments.mockImplementation(async () => [...comments])
|
||||
let postCount = 0
|
||||
postIssueComment.mockImplementation(async (opts: { body: string }) => {
|
||||
postIssueComment.mockImplementation(async (_issueNumber: number, body: string) => {
|
||||
postCount++
|
||||
if (postCount === 1) {
|
||||
// 模拟并发写者在我们 post 之后又盖了一条:我们的字段丢失。
|
||||
comments.push({ body: JSON.stringify({ column: 'review' }) })
|
||||
return
|
||||
}
|
||||
comments.push({ body: opts.body })
|
||||
comments.push({ body })
|
||||
})
|
||||
|
||||
await mergeStateJsonComment({
|
||||
host: 'https://gitea.example',
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
token: 'token',
|
||||
issueNumber: 7,
|
||||
extra: { pr: '99' },
|
||||
})
|
||||
await mergeForgeState(provider, 7, { pr: '99' })
|
||||
|
||||
expect(postIssueComment).toHaveBeenCalledTimes(2)
|
||||
// 重放基于并发写者的最新状态,两边的字段都保留。
|
||||
expect(parseStateComment(postIssueComment.mock.calls[1][0].body)).toEqual({
|
||||
expect(parseStateComment(postIssueComment.mock.calls[1][1])).toEqual({
|
||||
column: 'review',
|
||||
pr: '99',
|
||||
})
|
||||
})
|
||||
|
||||
it('read 时跳过尾部普通评论返回最近一条 state JSON', async () => {
|
||||
const { readStateJsonComment } = await import('../../src/gitea/stateJson.js')
|
||||
const { readForgeState } = await import('../../src/forge/state.js')
|
||||
listIssueComments.mockResolvedValue([
|
||||
{ body: JSON.stringify({ column: 'review', pr: '9' }) },
|
||||
{ body: 'PR 已关联(Gitea 自动评论)' },
|
||||
])
|
||||
|
||||
const state = await readStateJsonComment({
|
||||
host: 'https://gitea.example',
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
token: 'token',
|
||||
issueNumber: 9,
|
||||
})
|
||||
const state = await readForgeState(provider, 9)
|
||||
|
||||
expect(state).toEqual({ column: 'review', pr: '9' })
|
||||
})
|
||||
@@ -227,30 +188,30 @@ describe('state defaults', () => {
|
||||
[{}, { column: 'todo', color: 'blue', pr: '5' }],
|
||||
[{ column: 'review', color: 'red' }, { column: 'review', color: 'red', pr: '5' }],
|
||||
])('fills missing defaults without replacing existing values: %j', async (existing, expected) => {
|
||||
const { mergeStateJsonComment } = await import('../../src/gitea/stateJson.js')
|
||||
const { mergeForgeState } = await import('../../src/forge/state.js')
|
||||
const comments = [{ body: JSON.stringify(existing) }]
|
||||
listIssueComments.mockImplementation(async () => [...comments])
|
||||
postIssueComment.mockImplementation(async ({ body }: { body: string }) => comments.push({ body }))
|
||||
await mergeStateJsonComment({ host: 'host', owner: 'owner', repo: 'repo', token: 'token', issueNumber: 5, defaults: { column: 'todo', color: 'blue' }, extra: { pr: '5' } })
|
||||
expect(parseStateComment(postIssueComment.mock.calls[0][0].body)).toEqual(expected)
|
||||
postIssueComment.mockImplementation(async (_issueNumber: number, body: string) => comments.push({ body }))
|
||||
await mergeForgeState(provider, 5, { pr: '5' }, { column: 'todo', color: 'blue' })
|
||||
expect(parseStateComment(postIssueComment.mock.calls[0][1])).toEqual(expected)
|
||||
expect(postIssueComment).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('explicit changes override defaults and existing state', async () => {
|
||||
const { mergeStateJsonComment } = await import('../../src/gitea/stateJson.js')
|
||||
const { mergeForgeState } = await import('../../src/forge/state.js')
|
||||
const comments = [{ body: JSON.stringify({ column: 'review' }) }]
|
||||
listIssueComments.mockImplementation(async () => [...comments])
|
||||
postIssueComment.mockImplementation(async ({ body }: { body: string }) => comments.push({ body }))
|
||||
await mergeStateJsonComment({ host: 'host', owner: 'owner', repo: 'repo', token: 'token', issueNumber: 5, defaults: { column: 'todo', color: 'blue' }, extra: { column: 'done' } })
|
||||
expect(parseStateComment(postIssueComment.mock.calls[0][0].body)).toEqual({ column: 'done', color: 'blue' })
|
||||
postIssueComment.mockImplementation(async (_issueNumber: number, body: string) => comments.push({ body }))
|
||||
await mergeForgeState(provider, 5, { column: 'done' }, { column: 'todo', color: 'blue' })
|
||||
expect(parseStateComment(postIssueComment.mock.calls[0][1])).toEqual({ column: 'done', color: 'blue' })
|
||||
})
|
||||
|
||||
it('read-back does not force defaults over a concurrent update', async () => {
|
||||
const { mergeStateJsonComment } = await import('../../src/gitea/stateJson.js')
|
||||
const { mergeForgeState } = await import('../../src/forge/state.js')
|
||||
listIssueComments.mockResolvedValueOnce([]).mockResolvedValue([{ body: JSON.stringify({ column: 'review', color: 'red' }) }])
|
||||
await mergeStateJsonComment({ host: 'host', owner: 'owner', repo: 'repo', token: 'token', issueNumber: 5, defaults: { column: 'todo', color: 'blue' }, extra: {} })
|
||||
await mergeForgeState(provider, 5, {}, { column: 'todo', color: 'blue' })
|
||||
expect(postIssueComment).toHaveBeenCalledOnce()
|
||||
expect(parseStateComment(postIssueComment.mock.calls[0][0].body)).toEqual({ column: 'todo', color: 'blue' })
|
||||
expect(parseStateComment(postIssueComment.mock.calls[0][1])).toEqual({ column: 'todo', color: 'blue' })
|
||||
})
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user