Merge remote-tracking branch 'origin/master' into worktree/attachment-alignment-2
This commit is contained in:
@@ -319,6 +319,50 @@ describe('workspace.create', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('workspace.insertBefore', () => {
|
||||
it('commits the complete order, streams one order frame, and maps unknown ids', async () => {
|
||||
const { api, ctx, root } = await harness()
|
||||
const first = expectOk(await api.workspace.create(request({ path: stageDir(root, 'first') }))).workspace
|
||||
const second = expectOk(await api.workspace.create(request({ path: stageDir(root, 'second') }))).workspace
|
||||
const third = expectOk(await api.workspace.create(request({ path: stageDir(root, 'third') }))).workspace
|
||||
|
||||
const abort = new AbortController()
|
||||
const listWorkspaces = vi.spyOn(ctx.workspace, 'list')
|
||||
const stream: AsyncIterator<RpcRequest<HostFrame>> =
|
||||
api.events.host(request({}), abort.signal)[Symbol.asyncIterator]()
|
||||
expect(listWorkspaces).toHaveBeenCalledTimes(1)
|
||||
const changed = nextHostFrame(stream)
|
||||
const reordered = expectOk(await api.workspace.insertBefore(request({
|
||||
workspaceId: first.workspaceId,
|
||||
beforeWorkspaceId: second.workspaceId,
|
||||
})))
|
||||
expect(reordered.workspaceIds).toEqual([third.workspaceId, first.workspaceId, second.workspaceId])
|
||||
expect(await changed).toMatchObject({
|
||||
payload: {
|
||||
type: 'host/workspace-order-changed',
|
||||
workspaceIds: [third.workspaceId, first.workspaceId, second.workspaceId],
|
||||
},
|
||||
})
|
||||
expect(expectOk(await api.workspace.list(request({}))).items.map(item => item.workspaceId))
|
||||
.toEqual(reordered.workspaceIds)
|
||||
|
||||
const missingSource = await api.workspace.insertBefore(request({
|
||||
workspaceId: 'missing' as WorkspaceId,
|
||||
}))
|
||||
expect(missingSource.result).toMatchObject({
|
||||
ok: false, error: { code: 'workspace-not-found', details: { workspaceId: 'missing' } },
|
||||
})
|
||||
const missingAnchor = await api.workspace.insertBefore(request({
|
||||
workspaceId: first.workspaceId,
|
||||
beforeWorkspaceId: 'missing-anchor' as WorkspaceId,
|
||||
}))
|
||||
expect(missingAnchor.result).toMatchObject({
|
||||
ok: false, error: { code: 'workspace-not-found', details: { workspaceId: 'missing-anchor' } },
|
||||
})
|
||||
abort.abort()
|
||||
})
|
||||
})
|
||||
|
||||
describe('session creation and Workspace membership', () => {
|
||||
it('attaches a preallocated idempotent session while cwd-only sessions stay ungrouped', async () => {
|
||||
const { api, ctx, root } = await harness()
|
||||
|
||||
@@ -85,6 +85,7 @@ function scriptedApi(overrides: {
|
||||
create: r => ok(r, { workspace: { workspaceId: 'w1' as never, path: '/t', title: 't', sessionIds: [], createdAt: '0', updatedAt: '0' }, created: true }),
|
||||
rename: r => ok(r, { workspace: { workspaceId: 'w1' as never, path: '/t', title: 't', sessionIds: [], createdAt: '0', updatedAt: '0' } }),
|
||||
delete: r => ok(r, { deleted: true as const }),
|
||||
insertBefore: r => ok(r, { workspaceIds: [r.payload.workspaceId] }),
|
||||
insertSessionBefore: r => ok(r, { workspace: { workspaceId: 'w1' as never, path: '/t', title: 't', sessionIds: [], createdAt: '0', updatedAt: '0' } }),
|
||||
archiveSession: r => ok(r, { archivedSessionIds: [r.payload.sessionId] }),
|
||||
},
|
||||
@@ -218,7 +219,7 @@ describe('unary round trip', () => {
|
||||
expect(response.result).toEqual({ ok: true, value: { sessionId: 's-child' } })
|
||||
})
|
||||
|
||||
it('routes workspace rename, delete, and insertSessionBefore through the wire', async () => {
|
||||
it('routes workspace rename, delete, and ordering through the wire', async () => {
|
||||
const api = scriptedApi()
|
||||
const c = client(api)
|
||||
const renamed = await c.workspace.rename({ workspaceId: 'w1' as never, title: 'next' })
|
||||
@@ -227,6 +228,11 @@ describe('unary round trip', () => {
|
||||
expect(blankTitle.result).toMatchObject({ ok: false, error: { code: 'bad-request' } })
|
||||
const deleted = await c.workspace.delete({ workspaceId: 'w1' as never })
|
||||
expect(deleted.result).toEqual({ ok: true, value: { deleted: true } })
|
||||
const workspaceOrder = await c.workspace.insertBefore({
|
||||
workspaceId: 'w1' as never,
|
||||
beforeWorkspaceId: 'w2' as never,
|
||||
})
|
||||
expect(workspaceOrder.result).toEqual({ ok: true, value: { workspaceIds: ['w1'] } })
|
||||
const anchored = await c.workspace.insertSessionBefore({ workspaceId: 'w1' as never, sessionId: sid('s1'), beforeSessionId: sid('s2') })
|
||||
expect(anchored.result.ok).toBe(true)
|
||||
const appended = await c.workspace.insertSessionBefore({ workspaceId: 'w1' as never, sessionId: sid('s1') })
|
||||
|
||||
@@ -179,6 +179,9 @@ function fakeApi(overrides: Partial<{ muxFrames: MuxFrame[]; hostFrames: HostFra
|
||||
async delete(request) {
|
||||
return { rpcId: request.rpcId, result: { ok: true, value: { deleted: true as const } } }
|
||||
},
|
||||
async insertBefore(request) {
|
||||
return { rpcId: request.rpcId, result: { ok: true, value: { workspaceIds: [request.payload.workspaceId] } } }
|
||||
},
|
||||
async insertSessionBefore(request) {
|
||||
return {
|
||||
rpcId: request.rpcId,
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
workspaceArchiveSessionRequestSchema, workspaceArchiveSessionValueSchema,
|
||||
workspaceCreateRequestSchema, workspaceCreateValueSchema, workspaceIdSchema,
|
||||
workspaceDeleteRequestSchema, workspaceDeleteValueSchema,
|
||||
workspaceInsertBeforeRequestSchema, workspaceInsertBeforeValueSchema,
|
||||
workspaceInsertSessionBeforeRequestSchema, workspaceInsertSessionBeforeValueSchema,
|
||||
workspaceListRequestSchema, workspaceListValueSchema,
|
||||
workspaceRenameRequestSchema, workspaceRenameValueSchema, workspaceViewSchema,
|
||||
@@ -394,6 +395,17 @@ describe('workspace domain schemas', () => {
|
||||
expect(workspaceDeleteValueSchema.parse({ deleted: true })).toEqual({ deleted: true })
|
||||
expect(() => workspaceDeleteValueSchema.parse({ deleted: false })).toThrow()
|
||||
})
|
||||
|
||||
it('insertBefore accepts an anchored or anchorless Workspace move and returns the complete order', () => {
|
||||
expect(workspaceInsertBeforeRequestSchema.parse({
|
||||
workspaceId: 'w1', beforeWorkspaceId: 'w2',
|
||||
}).beforeWorkspaceId).toBe('w2')
|
||||
expect(workspaceInsertBeforeRequestSchema.parse({ workspaceId: 'w1' }).beforeWorkspaceId)
|
||||
.toBeUndefined()
|
||||
expect(() => workspaceInsertBeforeRequestSchema.parse({ beforeWorkspaceId: 'w2' })).toThrow()
|
||||
expect(workspaceInsertBeforeValueSchema.parse({ workspaceIds: ['w2', 'w1'] }).workspaceIds)
|
||||
.toEqual(['w2', 'w1'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('skills domain schemas', () => {
|
||||
|
||||
Reference in New Issue
Block a user