import assert from 'node:assert/strict' import { mkdtemp, rm } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { describe, it } from 'node:test' import { DEFAULT_CONFIG } from '../config.ts' import { openCodeIntelligenceDb } from '../db/connection.ts' import { CodeIntelligenceProgressWidget, formatEmbeddingBackendLine, formatEmbeddingDeviceLine, formatEmbeddingDownloadLine, formatEmbeddingProviderLabel, formatEmbeddingStatusLine, formatEmbeddingThroughputLine, formatFileProgress, isActiveEmbeddingStatus, isCodeIntelligenceBusy, isEmbeddingWorkVisible, isFileWorkVisible } from '../pi/progressWidget.ts' import { isIndexProgressStale } from '../db/repositories/indexingStateRepo.ts' describe('code intelligence progress widget', () => { it('does not show incremental changed-file progress as a fraction of the whole repo', () => { assert.equal(formatFileProgress('changedFilesIndex', undefined, 2, 2000), 'Changed files 2') }) it('shows full repo indexing as processed over total files', () => { assert.equal(formatFileProgress('fullRepoIndex', 'chunking', 2, 2000), 'Files 2/2000') }) it('shows scanning progress before total file count is known', () => { assert.equal(formatFileProgress('fullRepoIndex', 'scanning', 0, 0), 'Scanning repository...') assert.equal(formatFileProgress('fullRepoIndex', 'scanning', 12, 0), 'Scanning • 12 files found') }) it('does not show active repo total when job kind is unavailable', () => { assert.equal(formatFileProgress(undefined, undefined, 2, 159), 'Files 2') }) it('treats not_started embedding status as idle when indexing is inactive', () => { assert.equal(isActiveEmbeddingStatus('not_started'), false) assert.equal(isCodeIntelligenceBusy({ indexRunning: false, queuedJobs: 0, workerActive: false, embeddingStatus: 'not_started', missingEmbeddings: 0, }), false) assert.equal(isCodeIntelligenceBusy({ indexRunning: false, queuedJobs: 0, workerActive: true, embeddingStatus: 'not_started', missingEmbeddings: 0, }), true) }) it('detects stale in-progress indexing state', () => { const now = Date.now() assert.equal(isIndexProgressStale({ progress_phase: 'scanning', progress_updated_at: new Date(now - 130_000).toISOString(), } as never, 120_000, now), true) assert.equal(isIndexProgressStale({ progress_phase: 'complete', progress_updated_at: new Date(now - 130_000).toISOString(), } as never, 120_000, now), false) }) it('shows file and embedding progress as separate conditional lines', () => { assert.equal(isFileWorkVisible('fullRepoIndex', true, 0), true) assert.equal(isFileWorkVisible('embeddingBackfill', true, 0), false) assert.equal(isFileWorkVisible(undefined, false, 0), false) assert.equal(isEmbeddingWorkVisible('ready', 3), true) assert.equal(isEmbeddingWorkVisible('warming', 0), true) assert.equal(isEmbeddingWorkVisible('ready', 0), false) }) it('does not show a ready status line while embeddings are still missing', () => { assert.equal(formatEmbeddingStatusLine('ready', 4), undefined) assert.equal(formatEmbeddingStatusLine('ready', 0), undefined) assert.equal(formatEmbeddingStatusLine('warming', 0), 'Status warming') }) it('shows active or requested embedding device and model', () => { assert.equal(formatEmbeddingDeviceLine('coreml', 'cpu', 'jinaai/jina-embeddings-v2-base-code'), 'Device coreml • jina-embeddings-v2-base-code') assert.equal(formatEmbeddingDeviceLine(undefined, 'coreml', 'Xenova/all-MiniLM-L6-v2'), 'Device coreml requested • all-MiniLM-L6-v2') assert.equal(formatEmbeddingDeviceLine(undefined, undefined), undefined) }) it('shows embedding provider in backend line', () => { assert.equal(formatEmbeddingBackendLine({ kind: 'openai-compatible', modelId: 'http://localhost:11434/v1::nomic-embed-text' }), 'Provider remote • nomic-embed-text') assert.equal(formatEmbeddingBackendLine({ kind: 'disabled' }), 'Provider disabled (FTS only)') assert.equal(formatEmbeddingBackendLine({ kind: 'local', activeDevice: 'cpu', modelId: 'onnx-community/bge-small-en-v1.5-ONNX' }), 'Device cpu • bge-small-en-v1.5-ONNX') assert.equal(formatEmbeddingProviderLabel('openai-compatible'), 'remote (openai-compatible)') }) it('shows embedding download progress with size', () => { assert.equal(formatEmbeddingDownloadLine({ status: 'download', file: 'onnx/model.onnx', loadedBytes: 512 * 1024, totalBytes: 1024 * 1024, progress: 50 }), 'Downloading model.onnx 50% • 512.0 KB/1.0 MB') assert.equal(formatEmbeddingDownloadLine({ status: 'download', loadedBytes: 0, totalBytes: 1024, progress: 0 }), 'Downloading 0% • 0 B/1.0 KB') assert.equal(formatEmbeddingDownloadLine({}), undefined) }) it('does not reuse hidden idle output when work becomes visible', async () => { const storage = await mkdtemp(join(tmpdir(), 'pi-code-intelligence-progress-widget-')) const db = await openCodeIntelligenceDb(storage) let queuedJobs = 0 const layouts: Array<{ visible: boolean; height: number }> = [] try { const widget = new CodeIntelligenceProgressWidget(() => ({ db, identity: { repoKey: 'progress-widget-repo', gitRoot: storage, identitySource: 'path' }, config: DEFAULT_CONFIG, indexScheduler: { getStatus: () => ({ running: false, queuedJobs, workerProgressStale: false, stats: { activeFiles: 0, totalFiles: 0 }, }), }, services: { get: () => ({ status: 'ready' }) }, }) as never, undefined, (layout) => layouts.push(layout)) assert.deepEqual(widget.render(80), []) queuedJobs = 1 assert(widget.render(80).length > 0) assert.deepEqual(layouts.at(-1)?.visible, true) } finally { db.close() await rm(storage, { recursive: true, force: true }) } }) it('shows embedding throughput and ETA', () => { assert.equal(formatEmbeddingThroughputLine(12.4, 125), 'Rate 12/s • ETA 2m 5s') assert.equal(formatEmbeddingThroughputLine(3.25, 42), 'Rate 3.3/s • ETA 42s') assert.equal(formatEmbeddingThroughputLine(0, 42), undefined) }) })