✨ feat(vscode): 工单状态评论改写成隐藏格式,Gitea 和 YouTrack 共用一套读写
This commit is contained in:
1 parent
fa7b32d205
commit
38ca193434
17 files changed
+588
-660
No files matched your search
@@ -2,6 +2,7 @@ import { Buffer } from 'node:buffer'
|
||||
import { EventEmitter } from 'node:events'
|
||||
import { delimiter } from 'node:path'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { parseStateComment } from '../../src/issues/stateComment'
|
||||
|
||||
const execFile = vi.fn()
|
||||
const spawn = vi.fn()
|
||||
@@ -89,7 +90,7 @@ describe('mergeStateJsonCommentGuarded', () => {
|
||||
})
|
||||
|
||||
expect(postIssueComment).toHaveBeenCalledOnce()
|
||||
expect(JSON.parse(postIssueComment.mock.calls[0][0].body)).toEqual({
|
||||
expect(parseStateComment(postIssueComment.mock.calls[0][0].body)).toEqual({
|
||||
column: 'done',
|
||||
implementStatus: 'done',
|
||||
prMerged: true,
|
||||
@@ -156,7 +157,7 @@ describe('state JSON 尾部普通评论不丢状态', () => {
|
||||
})
|
||||
|
||||
expect(postIssueComment).toHaveBeenCalledOnce()
|
||||
expect(JSON.parse(postIssueComment.mock.calls[0][0].body)).toEqual({
|
||||
expect(parseStateComment(postIssueComment.mock.calls[0][0].body)).toEqual({
|
||||
...fullState,
|
||||
pr: '182',
|
||||
implementStatus: 'done',
|
||||
@@ -191,7 +192,7 @@ describe('state JSON 尾部普通评论不丢状态', () => {
|
||||
|
||||
expect(postIssueComment).toHaveBeenCalledTimes(2)
|
||||
// 重放基于并发写者的最新状态,两边的字段都保留。
|
||||
expect(JSON.parse(postIssueComment.mock.calls[1][0].body)).toEqual({
|
||||
expect(parseStateComment(postIssueComment.mock.calls[1][0].body)).toEqual({
|
||||
column: 'review',
|
||||
pr: '99',
|
||||
})
|
||||
@@ -231,7 +232,7 @@ describe('state defaults', () => {
|
||||
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(JSON.parse(postIssueComment.mock.calls[0][0].body)).toEqual(expected)
|
||||
expect(parseStateComment(postIssueComment.mock.calls[0][0].body)).toEqual(expected)
|
||||
expect(postIssueComment).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
@@ -241,7 +242,7 @@ describe('state defaults', () => {
|
||||
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(JSON.parse(postIssueComment.mock.calls[0][0].body)).toEqual({ column: 'done', color: 'blue' })
|
||||
expect(parseStateComment(postIssueComment.mock.calls[0][0].body)).toEqual({ column: 'done', color: 'blue' })
|
||||
})
|
||||
|
||||
it('read-back does not force defaults over a concurrent update', async () => {
|
||||
@@ -249,7 +250,7 @@ describe('state defaults', () => {
|
||||
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: {} })
|
||||
expect(postIssueComment).toHaveBeenCalledOnce()
|
||||
expect(JSON.parse(postIssueComment.mock.calls[0][0].body)).toEqual({ column: 'todo', color: 'blue' })
|
||||
expect(parseStateComment(postIssueComment.mock.calls[0][0].body)).toEqual({ column: 'todo', color: 'blue' })
|
||||
})
|
||||
})
|
||||
|
||||
@@ -434,18 +435,18 @@ describe('deleteLocalBranch', () => {
|
||||
|
||||
describe('hasStateColumn', () => {
|
||||
it('带 column 的 state JSON → true', async () => {
|
||||
const { hasStateColumn } = await import('../../src/gitea/stateJson.js')
|
||||
const { hasStateColumn } = await import('../../src/issues/stateComment.js')
|
||||
expect(hasStateColumn('{"column":"done","pr":"318"}')).toBe(true)
|
||||
expect(hasStateColumn(' {"column":"in-progress"} ')).toBe(true)
|
||||
})
|
||||
|
||||
it('没有 column 的 state JSON → false', async () => {
|
||||
const { hasStateColumn } = await import('../../src/gitea/stateJson.js')
|
||||
const { hasStateColumn } = await import('../../src/issues/stateComment.js')
|
||||
expect(hasStateColumn('{"pr":"318","branch":"feature/x"}')).toBe(false)
|
||||
})
|
||||
|
||||
it('普通文本、审查评论、空值 → false', async () => {
|
||||
const { hasStateColumn } = await import('../../src/gitea/stateJson.js')
|
||||
const { hasStateColumn } = await import('../../src/issues/stateComment.js')
|
||||
expect(hasStateColumn('<!-- spx:review=1 -->\n看着没问题')).toBe(false)
|
||||
expect(hasStateColumn('拖到完成列了')).toBe(false)
|
||||
expect(hasStateColumn('')).toBe(false)
|
||||
@@ -453,7 +454,7 @@ describe('hasStateColumn', () => {
|
||||
})
|
||||
|
||||
it('json 数组不算 state 评论', async () => {
|
||||
const { hasStateColumn } = await import('../../src/gitea/stateJson.js')
|
||||
const { hasStateColumn } = await import('../../src/issues/stateComment.js')
|
||||
expect(hasStateColumn('[{"column":"done"}]')).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -1,32 +1,40 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { findLatestState, parseStateComment, STATE_MARKER } from '../../src/youtrack/stateComment'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
describe('youtrack stateComment parsing', () => {
|
||||
it('parses a marker-prefixed state blob', () => {
|
||||
const body = `${STATE_MARKER}\n${JSON.stringify({ column: 'in-progress', sessionId: 'abc' })}`
|
||||
expect(parseStateComment(body)).toEqual({ column: 'in-progress', sessionId: 'abc' })
|
||||
const listComments = vi.fn()
|
||||
const addComment = vi.fn()
|
||||
|
||||
vi.mock('../../src/youtrack/api', () => ({ listComments, addComment }))
|
||||
|
||||
const auth = { baseUrl: 'https://yt.example', token: 't' }
|
||||
|
||||
describe('youtrack 状态评论', () => {
|
||||
beforeEach(() => {
|
||||
listComments.mockReset()
|
||||
addComment.mockReset()
|
||||
})
|
||||
|
||||
it('tolerates a bare JSON blob written without the marker', () => {
|
||||
expect(parseStateComment(JSON.stringify({ column: 'done' }))).toEqual({ column: 'done' })
|
||||
})
|
||||
|
||||
it('rejects JSON objects without any known state field (customer replies)', () => {
|
||||
expect(parseStateComment(JSON.stringify({ hello: 'world' }))).toBeNull()
|
||||
expect(parseStateComment('just a plain comment')).toBeNull()
|
||||
expect(parseStateComment('')).toBeNull()
|
||||
})
|
||||
|
||||
it('returns the latest state comment, skipping trailing non-state comments', () => {
|
||||
const comments = [
|
||||
{ id: '1', text: `${STATE_MARKER}\n${JSON.stringify({ column: 'todo' })}` },
|
||||
{ id: '2', text: `${STATE_MARKER}\n${JSON.stringify({ column: 'in-progress', branch: 'feat/x' })}` },
|
||||
it('读到旧标记格式写的状态,跳过客户后来的回复', async () => {
|
||||
const { readStateComment } = await import('../../src/youtrack/stateComment.js')
|
||||
listComments.mockResolvedValue([
|
||||
{ id: '1', text: '<!--spx-state-->\n{"column":"todo"}' },
|
||||
{ id: '2', text: '<!--spx-state-->\n{"column":"in-progress","branch":"feat/x"}' },
|
||||
{ id: '3', text: '客户后续补充:还是不行' },
|
||||
]
|
||||
expect(findLatestState(comments)).toEqual({ column: 'in-progress', branch: 'feat/x' })
|
||||
])
|
||||
expect(await readStateComment(auth, 'LXF-1')).toEqual({ column: 'in-progress', branch: 'feat/x' })
|
||||
})
|
||||
|
||||
it('returns null when no state comment exists', () => {
|
||||
expect(findLatestState([{ id: '1', text: 'hi' }, { id: '2', text: 'thanks' }])).toBeNull()
|
||||
it('合并后用隐藏格式写回', async () => {
|
||||
const { mergeStateComment } = await import('../../src/youtrack/stateComment.js')
|
||||
const { serializeStateComment } = await import('../../src/issues/stateComment.js')
|
||||
const comments = [{ id: '1', text: '{"column":"todo"}' }]
|
||||
listComments.mockImplementation(async () => [...comments])
|
||||
addComment.mockImplementation(async (_auth: unknown, _id: string, text: string) => {
|
||||
comments.push({ id: String(comments.length + 1), text })
|
||||
})
|
||||
|
||||
await mergeStateComment(auth, 'LXF-1', { branch: 'feat/y' })
|
||||
|
||||
expect(addComment).toHaveBeenCalledOnce()
|
||||
expect(addComment).toHaveBeenCalledWith(auth, 'LXF-1', serializeStateComment({ column: 'todo', branch: 'feat/y' }))
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user