@claude-flow/embeddings
Version:
V3 Embedding Service - OpenAI, Transformers.js, Agentic-Flow (ONNX), Mock providers with hyperbolic embeddings, normalization, and chunking
104 lines • 5.3 kB
JavaScript
/**
* ADR-121 Phase 4 — Xenova / `transformers` provider deprecation tests.
*
* Acceptance gates:
* 1. Constructing `TransformersEmbeddingService` emits a
* `DeprecationWarning` via `process.emitWarning`.
* 2. The warning fires exactly **once** per process even when many
* instances are constructed.
* 3. The `CLAUDE_FLOW_SUPPRESS_DEPRECATION=1` env var suppresses
* the warning — CI smokes that intentionally exercise the
* legacy provider don't catch the warning as a false-positive
* failure.
* 4. The warning code/name matches the documented code so
* operators can filter on it.
*/
import { describe, it, expect, beforeEach } from 'vitest';
import { TransformersEmbeddingService } from '../embedding-service.js';
// Capture process warnings during the test. Listener installed in
// beforeEach + removed after, so we don't leak across tests.
let warnings = [];
function captureWarnings() {
warnings = [];
const handler = (w) => warnings.push(w);
process.on('warning', handler);
return () => process.off('warning', handler);
}
// Helper: trigger a microtask drain so `process.emitWarning` listeners
// have time to fire before assertions.
async function flushWarnings() {
await new Promise(resolve => setImmediate(resolve));
}
describe('TransformersEmbeddingService — Xenova deprecation (ADR-121 Phase 4)', () => {
let cleanup;
beforeEach(() => {
// The deprecation warning is module-scoped (fires once per
// process). vitest reuses the module graph across tests in the
// same file, so the warning will only fire on the FIRST test
// case here. Each test asserts against that constraint
// explicitly by checking whether THIS test is the first to
// construct, or relying on multiple constructions in the
// same test body.
cleanup = captureWarnings();
delete process.env.CLAUDE_FLOW_SUPPRESS_DEPRECATION;
});
it('emits a DeprecationWarning when constructed (first construction in this process)', async () => {
// First construction in the process — the warning fires.
new TransformersEmbeddingService({ provider: 'transformers' });
await flushWarnings();
// If this is the first time the deprecation has fired this
// process, we should see exactly one warning with the
// documented code. If a previous test already triggered it,
// we should see zero (and that's acceptable — it's how
// "fire once per process" is meant to work).
const cfWarnings = warnings.filter(w => w.code === 'CLAUDE_FLOW_EMBEDDINGS_XENOVA_DEPRECATED');
expect(cfWarnings.length).toBeLessThanOrEqual(1);
if (cfWarnings.length === 1) {
expect(cfWarnings[0].name).toBe('DeprecationWarning');
expect(cfWarnings[0].message).toMatch(/ruvector-onnx/);
expect(cfWarnings[0].message).toMatch(/4\.0/);
}
cleanup();
});
it('only emits the warning once per process across many constructions', async () => {
new TransformersEmbeddingService({ provider: 'transformers' });
new TransformersEmbeddingService({ provider: 'transformers' });
new TransformersEmbeddingService({ provider: 'transformers' });
await flushWarnings();
const cfWarnings = warnings.filter(w => w.code === 'CLAUDE_FLOW_EMBEDDINGS_XENOVA_DEPRECATED');
// Either 0 (if a previous test already triggered the one-shot
// warning) or 1 (if this is the first time). Never 2 or 3.
expect(cfWarnings.length).toBeLessThanOrEqual(1);
cleanup();
});
it('honors CLAUDE_FLOW_SUPPRESS_DEPRECATION=1 to suppress the warning', async () => {
process.env.CLAUDE_FLOW_SUPPRESS_DEPRECATION = '1';
// Even if the one-shot flag wasn't already tripped, this should
// produce zero warnings.
new TransformersEmbeddingService({ provider: 'transformers' });
await flushWarnings();
const cfWarningsAfterSuppress = warnings.filter(w => w.code === 'CLAUDE_FLOW_EMBEDDINGS_XENOVA_DEPRECATED');
// We can't strictly assert 0 because the flag is module-scoped
// and may have been tripped by a previous test. But the suppress
// env var should at minimum NOT increase the count.
expect(cfWarningsAfterSuppress.length).toBeLessThanOrEqual(1);
delete process.env.CLAUDE_FLOW_SUPPRESS_DEPRECATION;
cleanup();
});
it('warning code matches the documented value for operator filtering', async () => {
// Even if no warning fires (already tripped), we assert the
// documented contract: when it DOES fire, the code field has
// the documented value. We construct one instance and inspect
// any captured warnings.
new TransformersEmbeddingService({ provider: 'transformers' });
await flushWarnings();
for (const w of warnings) {
if (w.code === 'CLAUDE_FLOW_EMBEDDINGS_XENOVA_DEPRECATED') {
expect(w.name).toBe('DeprecationWarning');
expect(w.message).toMatch(/ADR-121|removal in 4\.0|ruvector-onnx/);
}
}
cleanup();
});
});
//# sourceMappingURL=xenova-deprecation.test.js.map