Files
accounted/lib/bookkeeping/__tests__/template-embeddings.test.ts
T
Jakob Wennberg 03b569d708 refactor: consolidate extension system to general-only with manifest-driven architecture
- Remove all sector-specific extensions (construction, ecommerce, export,
  hotel, restaurant, tech) — only general-purpose extensions remain
- Move NE-bilaga and SRU export from extensions to core reports (lib/reports/)
- Move moms-box-mapping from extensions/export/shared to lib/vat/
- Replace per-extension API routes with catch-all dispatcher
  (app/api/extensions/ext/[...path]/route.ts)
- Add manifest.json for each extension with metadata, env vars, and deps
- Add api-routes.ts pattern for extension-defined API endpoints
- Add code generation scripts (generate-extension-registry, create-extension)
- Add extensions.config.json for opt-in extension loading
- Add extensions.schema.json for config validation
- Add email service interface with noop default (lib/email/service.ts)
- Add CI workflow (core-build.yml) to verify core builds with zero extensions
- Add migration 045: expand account_type CHECK for untaxed_reserves
- Update CLAUDE.md with comprehensive extension system documentation
- Update all report engines and bookkeeping services for new imports
- Clean up extensions.schema.json to only list existing extensions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 14:32:56 +01:00

105 lines
3.5 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { makeTransaction } from '@/tests/helpers'
import { extensionRegistry } from '@/lib/extensions/registry'
// Mock the extension registry
vi.mock('@/lib/extensions/registry', () => ({
extensionRegistry: {
get: vi.fn(),
},
}))
describe('template-embeddings facade', () => {
beforeEach(() => {
vi.clearAllMocks()
})
describe('findSimilarTemplates', () => {
it('delegates to ai-categorization extension service when available', async () => {
const mockResults = [{ template: { id: 'test' }, confidence: 0.9 }]
const mockFindSimilar = vi.fn().mockResolvedValue(mockResults)
vi.mocked(extensionRegistry.get).mockReturnValue({
id: 'ai-categorization',
name: 'AI',
version: '1.0.0',
services: { findSimilarTemplates: mockFindSimilar },
})
const { findSimilarTemplates } = await import('../template-embeddings')
const tx = makeTransaction({ description: 'SPOTIFY', amount: -109 })
const results = await findSimilarTemplates(tx, 'enskild_firma')
expect(mockFindSimilar).toHaveBeenCalledWith(tx, 'enskild_firma', undefined, undefined)
expect(results).toEqual(mockResults)
})
it('falls back to keyword matching when extension not loaded', async () => {
vi.mocked(extensionRegistry.get).mockReturnValue(undefined)
const { findSimilarTemplates } = await import('../template-embeddings')
const tx = makeTransaction({ description: 'SPOTIFY', amount: -109 })
const results = await findSimilarTemplates(tx)
// Should return keyword-based matches (may be empty for generic text)
expect(Array.isArray(results)).toBe(true)
})
})
describe('seedAllTemplateEmbeddings', () => {
it('delegates to extension service when available', async () => {
const mockSeed = vi.fn().mockResolvedValue({ seeded: 48, errors: [] })
vi.mocked(extensionRegistry.get).mockReturnValue({
id: 'ai-categorization',
name: 'AI',
version: '1.0.0',
services: { seedAllTemplateEmbeddings: mockSeed },
})
const { seedAllTemplateEmbeddings } = await import('../template-embeddings')
const result = await seedAllTemplateEmbeddings()
expect(mockSeed).toHaveBeenCalled()
expect(result).toEqual({ seeded: 48, errors: [] })
})
it('returns error when extension not loaded', async () => {
vi.mocked(extensionRegistry.get).mockReturnValue(undefined)
const { seedAllTemplateEmbeddings } = await import('../template-embeddings')
const result = await seedAllTemplateEmbeddings()
expect(result.seeded).toBe(0)
expect(result.errors).toHaveLength(1)
})
})
describe('getSchemaVersion', () => {
it('delegates to extension service when available', async () => {
const mockVersion = vi.fn().mockResolvedValue('abc123def456')
vi.mocked(extensionRegistry.get).mockReturnValue({
id: 'ai-categorization',
name: 'AI',
version: '1.0.0',
services: { getSchemaVersion: mockVersion },
})
const { getSchemaVersion } = await import('../template-embeddings')
const result = await getSchemaVersion()
expect(result).toBe('abc123def456')
})
it('returns "none" when extension not loaded', async () => {
vi.mocked(extensionRegistry.get).mockReturnValue(undefined)
const { getSchemaVersion } = await import('../template-embeddings')
const result = await getSchemaVersion()
expect(result).toBe('none')
})
})
})