mirror of
https://github.com/hanzoai/insights.git
synced 2026-10-30 16:15:31 +00:00
The tree was a debranded snapshot of upstream taken around 2026-02-20, and upstream has moved about five months since. This replaces it with upstream master, mechanically re-derived: bin/debrand does the rename, and the pipeline and squashed migrations are restored from the shipped tree. Not a merge. The previous import replaced the tree wholesale in a single commit, so there is no shared content history to merge against. Reproducing the rename on the new tree is what actually converges. What survives: .hanzo/workflows, the Dockerfiles and bin/docker* entrypoints, compose files, LLM.md, and insights/migrations. The squashed initial migration is load-bearing — the live database records it by name, so upstream's ~918 individual migrations cannot come back. Nine products are dropped as unreferenced. Twenty-one more that we do not run are kept anyway because the core app still imports them; unpicking those is a refactor and does not belong in a rebase. This base does not build yet, and the reasons are not in the rename: upstream now wants Python 3.13.13 against our 3.12.12 and Django 5.2 against our 4.2, and the dependency manifests still name upstream packages that we republish under our own names. Those are decisions, not renames.
272 lines
11 KiB
TypeScript
272 lines
11 KiB
TypeScript
import { afterEach, beforeAll, describe, expect, it } from 'vitest'
|
|
|
|
import {
|
|
TEST_ORG_ID,
|
|
TEST_PROJECT_ID,
|
|
createTestClient,
|
|
createTestContext,
|
|
generateUniqueKey,
|
|
parseToolResponse,
|
|
setActiveProjectAndOrg,
|
|
validateEnvironmentVariables,
|
|
} from '@/shared/test-utils'
|
|
import { GENERATED_TOOLS } from '@/tools/generated/annotations'
|
|
import type { Context } from '@/tools/types'
|
|
|
|
describe('Annotations', { concurrent: false }, () => {
|
|
let context: Context
|
|
const createdAnnotationIds: number[] = []
|
|
|
|
const createTool = GENERATED_TOOLS['annotation-create']!()
|
|
const listTool = GENERATED_TOOLS['annotations-list']!()
|
|
const retrieveTool = GENERATED_TOOLS['annotation-retrieve']!()
|
|
const updateTool = GENERATED_TOOLS['annotations-partial-update']!()
|
|
const deleteTool = GENERATED_TOOLS['annotation-delete']!()
|
|
|
|
beforeAll(async () => {
|
|
validateEnvironmentVariables()
|
|
const client = createTestClient()
|
|
context = createTestContext(client)
|
|
await setActiveProjectAndOrg(context, TEST_PROJECT_ID!, TEST_ORG_ID!)
|
|
})
|
|
|
|
afterEach(async () => {
|
|
for (const id of createdAnnotationIds) {
|
|
try {
|
|
await deleteTool.handler(context, { id })
|
|
} catch {
|
|
// best effort — annotation may already be deleted
|
|
}
|
|
}
|
|
createdAnnotationIds.length = 0
|
|
})
|
|
|
|
describe('annotation-create tool', () => {
|
|
it('should create an annotation with all fields', async () => {
|
|
const params = {
|
|
content: `Test annotation ${generateUniqueKey('ann')}`,
|
|
date_marker: '2024-01-15T12:00:00Z',
|
|
scope: 'project' as const,
|
|
emoji: '🤖',
|
|
}
|
|
|
|
const result = await createTool.handler(context, params)
|
|
const annotation = parseToolResponse(result)
|
|
|
|
expect(annotation.id).toBeTruthy()
|
|
expect(annotation.content).toBe(params.content)
|
|
expect(annotation.date_marker).toContain('2024-01-15')
|
|
expect(annotation.scope).toBe('project')
|
|
expect(annotation.emoji).toBe('🤖')
|
|
|
|
createdAnnotationIds.push(annotation.id)
|
|
})
|
|
|
|
it('should create an annotation with minimal fields', async () => {
|
|
const params = {
|
|
content: `Minimal annotation ${generateUniqueKey('min')}`,
|
|
}
|
|
|
|
const result = await createTool.handler(context, params)
|
|
const annotation = parseToolResponse(result)
|
|
|
|
expect(annotation.id).toBeTruthy()
|
|
expect(annotation.content).toBe(params.content)
|
|
|
|
createdAnnotationIds.push(annotation.id)
|
|
})
|
|
})
|
|
|
|
describe('annotations-list tool', () => {
|
|
it('should list annotations including a newly created one', async () => {
|
|
const createResult = await createTool.handler(context, {
|
|
content: `List test annotation ${generateUniqueKey('list')}`,
|
|
date_marker: '2024-02-01T00:00:00Z',
|
|
scope: 'project' as const,
|
|
})
|
|
const created = parseToolResponse(createResult)
|
|
createdAnnotationIds.push(created.id)
|
|
|
|
const result = await listTool.handler(context, {})
|
|
const response = parseToolResponse(result)
|
|
|
|
expect(Array.isArray(response.results)).toBe(true)
|
|
expect(response.results.some((a: { id: number }) => a.id === created.id)).toBe(true)
|
|
})
|
|
|
|
it('should support pagination', async () => {
|
|
const result = await listTool.handler(context, { limit: 5, offset: 0 })
|
|
const response = parseToolResponse(result)
|
|
|
|
expect(Array.isArray(response.results)).toBe(true)
|
|
expect(response.results.length).toBeLessThanOrEqual(5)
|
|
})
|
|
})
|
|
|
|
describe('annotation-retrieve tool', () => {
|
|
it('should retrieve an annotation by ID', async () => {
|
|
const content = `Retrieve test ${generateUniqueKey('retrieve')}`
|
|
const createResult = await createTool.handler(context, {
|
|
content,
|
|
date_marker: '2024-03-10T08:30:00Z',
|
|
scope: 'project' as const,
|
|
})
|
|
const created = parseToolResponse(createResult)
|
|
createdAnnotationIds.push(created.id)
|
|
|
|
const result = await retrieveTool.handler(context, { id: created.id })
|
|
const annotation = parseToolResponse(result)
|
|
|
|
expect(annotation.id).toBe(created.id)
|
|
expect(annotation.content).toBe(content)
|
|
expect(annotation.date_marker).toContain('2024-03-10')
|
|
expect(annotation.scope).toBe('project')
|
|
})
|
|
})
|
|
|
|
describe('annotations-partial-update tool', () => {
|
|
it('should update annotation content', async () => {
|
|
const createResult = await createTool.handler(context, {
|
|
content: `Original ${generateUniqueKey('orig')}`,
|
|
date_marker: '2024-04-01T00:00:00Z',
|
|
scope: 'project' as const,
|
|
})
|
|
const created = parseToolResponse(createResult)
|
|
createdAnnotationIds.push(created.id)
|
|
|
|
const updatedContent = `Updated ${generateUniqueKey('upd')}`
|
|
const result = await updateTool.handler(context, {
|
|
id: created.id,
|
|
content: updatedContent,
|
|
})
|
|
const updated = parseToolResponse(result)
|
|
|
|
expect(updated.id).toBe(created.id)
|
|
expect(updated.content).toBe(updatedContent)
|
|
})
|
|
|
|
it('should update annotation date_marker', async () => {
|
|
const createResult = await createTool.handler(context, {
|
|
content: `Date update ${generateUniqueKey('date')}`,
|
|
date_marker: '2024-05-01T00:00:00Z',
|
|
scope: 'project' as const,
|
|
})
|
|
const created = parseToolResponse(createResult)
|
|
createdAnnotationIds.push(created.id)
|
|
|
|
const result = await updateTool.handler(context, {
|
|
id: created.id,
|
|
date_marker: '2024-06-15T12:00:00Z',
|
|
})
|
|
const updated = parseToolResponse(result)
|
|
|
|
expect(updated.id).toBe(created.id)
|
|
expect(updated.date_marker).toContain('2024-06-15')
|
|
})
|
|
|
|
it('should update annotation emoji', async () => {
|
|
const createResult = await createTool.handler(context, {
|
|
content: `Emoji update ${generateUniqueKey('emoji')}`,
|
|
date_marker: '2024-07-15T00:00:00Z',
|
|
scope: 'project' as const,
|
|
})
|
|
const created = parseToolResponse(createResult)
|
|
createdAnnotationIds.push(created.id)
|
|
|
|
const result = await updateTool.handler(context, {
|
|
id: created.id,
|
|
emoji: '🚀',
|
|
})
|
|
const updated = parseToolResponse(result)
|
|
|
|
expect(updated.id).toBe(created.id)
|
|
expect(updated.emoji).toBe('🚀')
|
|
})
|
|
|
|
it('should update annotation scope', async () => {
|
|
const createResult = await createTool.handler(context, {
|
|
content: `Scope update ${generateUniqueKey('scope')}`,
|
|
date_marker: '2024-07-01T00:00:00Z',
|
|
scope: 'project' as const,
|
|
})
|
|
const created = parseToolResponse(createResult)
|
|
createdAnnotationIds.push(created.id)
|
|
|
|
const result = await updateTool.handler(context, {
|
|
id: created.id,
|
|
scope: 'organization' as const,
|
|
})
|
|
const updated = parseToolResponse(result)
|
|
|
|
expect(updated.id).toBe(created.id)
|
|
expect(updated.scope).toBe('organization')
|
|
})
|
|
})
|
|
|
|
describe('annotation-delete tool', () => {
|
|
it('should delete an annotation', async () => {
|
|
const createResult = await createTool.handler(context, {
|
|
content: `Delete test ${generateUniqueKey('del')}`,
|
|
date_marker: '2024-08-01T00:00:00Z',
|
|
scope: 'project' as const,
|
|
})
|
|
const created = parseToolResponse(createResult)
|
|
// Don't track for cleanup since we're deleting it here
|
|
|
|
await deleteTool.handler(context, { id: created.id })
|
|
|
|
const listResult = await listTool.handler(context, {})
|
|
const response = parseToolResponse(listResult)
|
|
expect(response.results.some((a: { id: number }) => a.id === created.id)).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('full CRUD workflow', () => {
|
|
it('should support create → retrieve → update → verify → delete', async () => {
|
|
// Create
|
|
const content = `CRUD workflow ${generateUniqueKey('crud')}`
|
|
const createResult = await createTool.handler(context, {
|
|
content,
|
|
date_marker: '2024-09-01T00:00:00Z',
|
|
scope: 'project' as const,
|
|
})
|
|
const created = parseToolResponse(createResult)
|
|
expect(created.id).toBeTruthy()
|
|
expect(created.content).toBe(content)
|
|
|
|
// Retrieve
|
|
const retrieveResult = await retrieveTool.handler(context, { id: created.id })
|
|
const retrieved = parseToolResponse(retrieveResult)
|
|
expect(retrieved.id).toBe(created.id)
|
|
expect(retrieved.content).toBe(content)
|
|
|
|
// Update
|
|
const updatedContent = `Updated CRUD ${generateUniqueKey('crud-upd')}`
|
|
const updateResult = await updateTool.handler(context, {
|
|
id: created.id,
|
|
content: updatedContent,
|
|
date_marker: '2024-10-15T18:00:00Z',
|
|
scope: 'organization' as const,
|
|
})
|
|
const updated = parseToolResponse(updateResult)
|
|
expect(updated.content).toBe(updatedContent)
|
|
expect(updated.date_marker).toContain('2024-10-15')
|
|
expect(updated.scope).toBe('organization')
|
|
|
|
// Verify update via retrieve
|
|
const verifyResult = await retrieveTool.handler(context, { id: created.id })
|
|
const verified = parseToolResponse(verifyResult)
|
|
expect(verified.content).toBe(updatedContent)
|
|
expect(verified.scope).toBe('organization')
|
|
|
|
// Delete
|
|
await deleteTool.handler(context, { id: created.id })
|
|
|
|
// Verify deletion
|
|
const listResult = await listTool.handler(context, {})
|
|
const response = parseToolResponse(listResult)
|
|
expect(response.results.some((a: { id: number }) => a.id === created.id)).toBe(false)
|
|
})
|
|
})
|
|
})
|