@skillsmith/core 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/analysis/types.d.ts +2 -0
- package/dist/src/analysis/types.d.ts.map +1 -1
- package/dist/src/analysis/types.js +13 -1
- package/dist/src/analysis/types.js.map +1 -1
- package/dist/src/analytics/AnalyticsRepository.d.ts +4 -0
- package/dist/src/analytics/AnalyticsRepository.d.ts.map +1 -1
- package/dist/src/analytics/AnalyticsRepository.js +26 -44
- package/dist/src/analytics/AnalyticsRepository.js.map +1 -1
- package/dist/src/analytics/schema.d.ts +1 -1
- package/dist/src/analytics/schema.d.ts.map +1 -1
- package/dist/src/analytics/schema.js +68 -0
- package/dist/src/analytics/schema.js.map +1 -1
- package/dist/src/api/client.d.ts +33 -29
- package/dist/src/api/client.d.ts.map +1 -1
- package/dist/src/api/client.js +15 -10
- package/dist/src/api/client.js.map +1 -1
- package/dist/src/billing/BillingService.d.ts +139 -0
- package/dist/src/billing/BillingService.d.ts.map +1 -0
- package/dist/src/billing/BillingService.js +393 -0
- package/dist/src/billing/BillingService.js.map +1 -0
- package/dist/src/billing/GDPRComplianceService.d.ts +176 -0
- package/dist/src/billing/GDPRComplianceService.d.ts.map +1 -0
- package/dist/src/billing/GDPRComplianceService.js +361 -0
- package/dist/src/billing/GDPRComplianceService.js.map +1 -0
- package/dist/src/billing/StripeClient.d.ts +177 -0
- package/dist/src/billing/StripeClient.d.ts.map +1 -0
- package/dist/src/billing/StripeClient.js +462 -0
- package/dist/src/billing/StripeClient.js.map +1 -0
- package/dist/src/billing/StripeReconciliationJob.d.ts +95 -0
- package/dist/src/billing/StripeReconciliationJob.d.ts.map +1 -0
- package/dist/src/billing/StripeReconciliationJob.js +405 -0
- package/dist/src/billing/StripeReconciliationJob.js.map +1 -0
- package/dist/src/billing/StripeWebhookHandler.d.ts +92 -0
- package/dist/src/billing/StripeWebhookHandler.d.ts.map +1 -0
- package/dist/src/billing/StripeWebhookHandler.js +409 -0
- package/dist/src/billing/StripeWebhookHandler.js.map +1 -0
- package/dist/src/billing/index.d.ts +18 -0
- package/dist/src/billing/index.d.ts.map +1 -0
- package/dist/src/billing/index.js +19 -0
- package/dist/src/billing/index.js.map +1 -0
- package/dist/src/billing/types.d.ts +266 -0
- package/dist/src/billing/types.d.ts.map +1 -0
- package/dist/src/billing/types.js +23 -0
- package/dist/src/billing/types.js.map +1 -0
- package/dist/src/embeddings/hnsw-store.d.ts +568 -0
- package/dist/src/embeddings/hnsw-store.d.ts.map +1 -0
- package/dist/src/embeddings/hnsw-store.js +805 -0
- package/dist/src/embeddings/hnsw-store.js.map +1 -0
- package/dist/src/embeddings/index.d.ts +2 -0
- package/dist/src/embeddings/index.d.ts.map +1 -1
- package/dist/src/embeddings/index.js +2 -0
- package/dist/src/embeddings/index.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/learning/PatternStore.d.ts +457 -0
- package/dist/src/learning/PatternStore.d.ts.map +1 -0
- package/dist/src/learning/PatternStore.js +893 -0
- package/dist/src/learning/PatternStore.js.map +1 -0
- package/dist/src/learning/ReasoningBankIntegration.d.ts +403 -0
- package/dist/src/learning/ReasoningBankIntegration.d.ts.map +1 -0
- package/dist/src/learning/ReasoningBankIntegration.js +627 -0
- package/dist/src/learning/ReasoningBankIntegration.js.map +1 -0
- package/dist/src/learning/index.d.ts +15 -0
- package/dist/src/learning/index.d.ts.map +1 -0
- package/dist/src/learning/index.js +15 -0
- package/dist/src/learning/index.js.map +1 -0
- package/dist/src/routing/SONARouter.d.ts +154 -0
- package/dist/src/routing/SONARouter.d.ts.map +1 -0
- package/dist/src/routing/SONARouter.js +679 -0
- package/dist/src/routing/SONARouter.js.map +1 -0
- package/dist/src/routing/index.d.ts +9 -0
- package/dist/src/routing/index.d.ts.map +1 -0
- package/dist/src/routing/index.js +10 -0
- package/dist/src/routing/index.js.map +1 -0
- package/dist/src/routing/types.d.ts +331 -0
- package/dist/src/routing/types.d.ts.map +1 -0
- package/dist/src/routing/types.js +203 -0
- package/dist/src/routing/types.js.map +1 -0
- package/dist/src/scripts/__tests__/scan-imported-skills.test.js +5 -0
- package/dist/src/scripts/__tests__/scan-imported-skills.test.js.map +1 -1
- package/dist/src/security/SkillSandbox.d.ts +156 -0
- package/dist/src/security/SkillSandbox.d.ts.map +1 -0
- package/dist/src/security/SkillSandbox.js +303 -0
- package/dist/src/security/SkillSandbox.js.map +1 -0
- package/dist/src/security/index.d.ts +3 -1
- package/dist/src/security/index.d.ts.map +1 -1
- package/dist/src/security/index.js +5 -1
- package/dist/src/security/index.js.map +1 -1
- package/dist/src/security/rate-limiter/presets.d.ts +12 -0
- package/dist/src/security/rate-limiter/presets.d.ts.map +1 -1
- package/dist/src/security/rate-limiter/presets.js +12 -0
- package/dist/src/security/rate-limiter/presets.js.map +1 -1
- package/dist/src/security/sanitization.d.ts +85 -0
- package/dist/src/security/sanitization.d.ts.map +1 -1
- package/dist/src/security/sanitization.js +133 -0
- package/dist/src/security/sanitization.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.d.ts +23 -0
- package/dist/src/security/scanner/SecurityScanner.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.js +232 -28
- package/dist/src/security/scanner/SecurityScanner.js.map +1 -1
- package/dist/src/security/scanner/patterns.d.ts +13 -0
- package/dist/src/security/scanner/patterns.d.ts.map +1 -1
- package/dist/src/security/scanner/patterns.js +51 -0
- package/dist/src/security/scanner/patterns.js.map +1 -1
- package/dist/src/security/scanner/types.d.ts +13 -1
- package/dist/src/security/scanner/types.d.ts.map +1 -1
- package/dist/src/security/scanner/weights.d.ts.map +1 -1
- package/dist/src/security/scanner/weights.js +1 -0
- package/dist/src/security/scanner/weights.js.map +1 -1
- package/dist/src/session/SessionManager.d.ts +7 -0
- package/dist/src/session/SessionManager.d.ts.map +1 -1
- package/dist/src/session/SessionManager.js +117 -10
- package/dist/src/session/SessionManager.js.map +1 -1
- package/dist/src/sync/SyncEngine.d.ts.map +1 -1
- package/dist/src/sync/SyncEngine.js +52 -32
- package/dist/src/sync/SyncEngine.js.map +1 -1
- package/dist/src/testing/MultiLLMProvider.d.ts +374 -0
- package/dist/src/testing/MultiLLMProvider.d.ts.map +1 -0
- package/dist/src/testing/MultiLLMProvider.js +720 -0
- package/dist/src/testing/MultiLLMProvider.js.map +1 -0
- package/dist/src/testing/index.d.ts +8 -0
- package/dist/src/testing/index.d.ts.map +1 -0
- package/dist/src/testing/index.js +9 -0
- package/dist/src/testing/index.js.map +1 -0
- package/dist/src/types.d.ts +3 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/tests/SecurityScanner.test.js +337 -1
- package/dist/tests/SecurityScanner.test.js.map +1 -1
- package/dist/tests/billing/BillingService.test.d.ts +7 -0
- package/dist/tests/billing/BillingService.test.d.ts.map +1 -0
- package/dist/tests/billing/BillingService.test.js +168 -0
- package/dist/tests/billing/BillingService.test.js.map +1 -0
- package/dist/tests/billing/GDPRCompliance.test.d.ts +7 -0
- package/dist/tests/billing/GDPRCompliance.test.d.ts.map +1 -0
- package/dist/tests/billing/GDPRCompliance.test.js +195 -0
- package/dist/tests/billing/GDPRCompliance.test.js.map +1 -0
- package/dist/tests/billing/StripeReconciliation.test.d.ts +7 -0
- package/dist/tests/billing/StripeReconciliation.test.d.ts.map +1 -0
- package/dist/tests/billing/StripeReconciliation.test.js +266 -0
- package/dist/tests/billing/StripeReconciliation.test.js.map +1 -0
- package/dist/tests/billing/stripe-validators.test.d.ts +7 -0
- package/dist/tests/billing/stripe-validators.test.d.ts.map +1 -0
- package/dist/tests/billing/stripe-validators.test.js +107 -0
- package/dist/tests/billing/stripe-validators.test.js.map +1 -0
- package/dist/tests/embeddings/hnsw-store.test.d.ts +7 -0
- package/dist/tests/embeddings/hnsw-store.test.d.ts.map +1 -0
- package/dist/tests/embeddings/hnsw-store.test.js +295 -0
- package/dist/tests/embeddings/hnsw-store.test.js.map +1 -0
- package/dist/tests/integration/neural/e2e-learning.test.d.ts +17 -0
- package/dist/tests/integration/neural/e2e-learning.test.d.ts.map +1 -0
- package/dist/tests/integration/neural/e2e-learning.test.js +238 -0
- package/dist/tests/integration/neural/e2e-learning.test.js.map +1 -0
- package/dist/tests/integration/neural/helpers.d.ts +132 -0
- package/dist/tests/integration/neural/helpers.d.ts.map +1 -0
- package/dist/tests/integration/neural/helpers.js +287 -0
- package/dist/tests/integration/neural/helpers.js.map +1 -0
- package/dist/tests/integration/neural/personalization.test.d.ts +21 -0
- package/dist/tests/integration/neural/personalization.test.d.ts.map +1 -0
- package/dist/tests/integration/neural/personalization.test.js +304 -0
- package/dist/tests/integration/neural/personalization.test.js.map +1 -0
- package/dist/tests/integration/neural/preference-learner.test.d.ts +23 -0
- package/dist/tests/integration/neural/preference-learner.test.d.ts.map +1 -0
- package/dist/tests/integration/neural/preference-learner.test.js +289 -0
- package/dist/tests/integration/neural/preference-learner.test.js.map +1 -0
- package/dist/tests/integration/neural/privacy.test.d.ts +19 -0
- package/dist/tests/integration/neural/privacy.test.d.ts.map +1 -0
- package/dist/tests/integration/neural/privacy.test.js +249 -0
- package/dist/tests/integration/neural/privacy.test.js.map +1 -0
- package/dist/tests/integration/neural/setup.d.ts +175 -0
- package/dist/tests/integration/neural/setup.d.ts.map +1 -0
- package/dist/tests/integration/neural/setup.js +487 -0
- package/dist/tests/integration/neural/setup.js.map +1 -0
- package/dist/tests/integration/neural/signal-collection.test.d.ts +21 -0
- package/dist/tests/integration/neural/signal-collection.test.d.ts.map +1 -0
- package/dist/tests/integration/neural/signal-collection.test.js +232 -0
- package/dist/tests/integration/neural/signal-collection.test.js.map +1 -0
- package/dist/tests/learning/PatternStore.test.d.ts +8 -0
- package/dist/tests/learning/PatternStore.test.d.ts.map +1 -0
- package/dist/tests/learning/PatternStore.test.js +589 -0
- package/dist/tests/learning/PatternStore.test.js.map +1 -0
- package/dist/tests/learning/ReasoningBankIntegration.test.d.ts +8 -0
- package/dist/tests/learning/ReasoningBankIntegration.test.d.ts.map +1 -0
- package/dist/tests/learning/ReasoningBankIntegration.test.js +269 -0
- package/dist/tests/learning/ReasoningBankIntegration.test.js.map +1 -0
- package/dist/tests/routing/SONARouter.test.d.ts +8 -0
- package/dist/tests/routing/SONARouter.test.d.ts.map +1 -0
- package/dist/tests/routing/SONARouter.test.js +400 -0
- package/dist/tests/routing/SONARouter.test.js.map +1 -0
- package/dist/tests/security/ContinuousSecurity.test.js +10 -12
- package/dist/tests/security/ContinuousSecurity.test.js.map +1 -1
- package/dist/tests/security/SkillSandbox.test.d.ts +8 -0
- package/dist/tests/security/SkillSandbox.test.d.ts.map +1 -0
- package/dist/tests/security/SkillSandbox.test.js +321 -0
- package/dist/tests/security/SkillSandbox.test.js.map +1 -0
- package/dist/tests/sync/SyncEngine.test.js +4 -2
- package/dist/tests/sync/SyncEngine.test.js.map +1 -1
- package/dist/tests/testing/MultiLLMProvider.test.d.ts +14 -0
- package/dist/tests/testing/MultiLLMProvider.test.d.ts.map +1 -0
- package/dist/tests/testing/MultiLLMProvider.test.js +438 -0
- package/dist/tests/testing/MultiLLMProvider.test.js.map +1 -0
- package/package.json +16 -3
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-1519: HNSW Embedding Store Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for the hybrid HNSW + SQLite embedding storage.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
7
|
+
import { existsSync, unlinkSync, mkdirSync } from 'node:fs';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { tmpdir } from 'node:os';
|
|
10
|
+
import { HNSWEmbeddingStore, DEFAULT_HNSW_CONFIG, HNSW_PRESETS, createHNSWStore, isHNSWAvailable, } from '../../src/embeddings/hnsw-store.js';
|
|
11
|
+
describe('HNSWEmbeddingStore', () => {
|
|
12
|
+
let store;
|
|
13
|
+
let testDbPath;
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
// Create unique test database path
|
|
16
|
+
const testDir = join(tmpdir(), 'skillsmith-hnsw-tests');
|
|
17
|
+
if (!existsSync(testDir)) {
|
|
18
|
+
mkdirSync(testDir, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
testDbPath = join(testDir, `test-${Date.now()}.db`);
|
|
21
|
+
});
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
if (store) {
|
|
24
|
+
store.close();
|
|
25
|
+
}
|
|
26
|
+
// Clean up test database
|
|
27
|
+
if (existsSync(testDbPath)) {
|
|
28
|
+
try {
|
|
29
|
+
unlinkSync(testDbPath);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// Ignore cleanup errors
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
describe('constructor', () => {
|
|
37
|
+
it('should create store with default config', () => {
|
|
38
|
+
store = new HNSWEmbeddingStore({ dbPath: testDbPath });
|
|
39
|
+
expect(store).toBeInstanceOf(HNSWEmbeddingStore);
|
|
40
|
+
});
|
|
41
|
+
it('should create store with custom config', () => {
|
|
42
|
+
store = new HNSWEmbeddingStore({
|
|
43
|
+
dbPath: testDbPath,
|
|
44
|
+
hnswConfig: {
|
|
45
|
+
m: 32,
|
|
46
|
+
efConstruction: 400,
|
|
47
|
+
efSearch: 150,
|
|
48
|
+
dimensions: 384,
|
|
49
|
+
},
|
|
50
|
+
maxElements: 50000,
|
|
51
|
+
});
|
|
52
|
+
expect(store).toBeInstanceOf(HNSWEmbeddingStore);
|
|
53
|
+
});
|
|
54
|
+
it('should use brute-force fallback when useHNSW is false', () => {
|
|
55
|
+
store = new HNSWEmbeddingStore({
|
|
56
|
+
dbPath: testDbPath,
|
|
57
|
+
useHNSW: false,
|
|
58
|
+
});
|
|
59
|
+
expect(store.isUsingFallback()).toBe(true);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe('storeEmbedding', () => {
|
|
63
|
+
beforeEach(() => {
|
|
64
|
+
store = new HNSWEmbeddingStore({ dbPath: testDbPath, useHNSW: false });
|
|
65
|
+
});
|
|
66
|
+
it('should store embedding successfully', () => {
|
|
67
|
+
const embedding = new Float32Array(384).fill(0.1);
|
|
68
|
+
store.storeEmbedding('skill-1', embedding, 'Test skill description');
|
|
69
|
+
const retrieved = store.getEmbedding('skill-1');
|
|
70
|
+
expect(retrieved).not.toBeNull();
|
|
71
|
+
expect(retrieved.length).toBe(384);
|
|
72
|
+
});
|
|
73
|
+
it('should reject embedding with wrong dimensions', () => {
|
|
74
|
+
const wrongDimEmbedding = new Float32Array(768).fill(0.1);
|
|
75
|
+
expect(() => {
|
|
76
|
+
store.storeEmbedding('skill-1', wrongDimEmbedding, 'Test');
|
|
77
|
+
}).toThrow('Embedding dimension mismatch');
|
|
78
|
+
});
|
|
79
|
+
it('should update existing embedding on re-store', () => {
|
|
80
|
+
const embedding1 = new Float32Array(384).fill(0.1);
|
|
81
|
+
const embedding2 = new Float32Array(384).fill(0.2);
|
|
82
|
+
store.storeEmbedding('skill-1', embedding1, 'Original text');
|
|
83
|
+
store.storeEmbedding('skill-1', embedding2, 'Updated text');
|
|
84
|
+
const retrieved = store.getEmbedding('skill-1');
|
|
85
|
+
expect(retrieved).not.toBeNull();
|
|
86
|
+
expect(retrieved[0]).toBeCloseTo(0.2);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe('getEmbedding', () => {
|
|
90
|
+
beforeEach(() => {
|
|
91
|
+
store = new HNSWEmbeddingStore({ dbPath: testDbPath, useHNSW: false });
|
|
92
|
+
});
|
|
93
|
+
it('should return null for non-existent skill', () => {
|
|
94
|
+
const result = store.getEmbedding('non-existent');
|
|
95
|
+
expect(result).toBeNull();
|
|
96
|
+
});
|
|
97
|
+
it('should return stored embedding', () => {
|
|
98
|
+
const embedding = new Float32Array(384);
|
|
99
|
+
for (let i = 0; i < 384; i++) {
|
|
100
|
+
embedding[i] = i / 384;
|
|
101
|
+
}
|
|
102
|
+
store.storeEmbedding('skill-1', embedding, 'Test');
|
|
103
|
+
const retrieved = store.getEmbedding('skill-1');
|
|
104
|
+
expect(retrieved).not.toBeNull();
|
|
105
|
+
expect(retrieved[0]).toBeCloseTo(0 / 384);
|
|
106
|
+
expect(retrieved[100]).toBeCloseTo(100 / 384);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
describe('getAllEmbeddings', () => {
|
|
110
|
+
beforeEach(() => {
|
|
111
|
+
store = new HNSWEmbeddingStore({ dbPath: testDbPath, useHNSW: false });
|
|
112
|
+
});
|
|
113
|
+
it('should return empty map for empty store', () => {
|
|
114
|
+
const all = store.getAllEmbeddings();
|
|
115
|
+
expect(all.size).toBe(0);
|
|
116
|
+
});
|
|
117
|
+
it('should return all stored embeddings', () => {
|
|
118
|
+
store.storeEmbedding('skill-1', new Float32Array(384).fill(0.1), 'Skill 1');
|
|
119
|
+
store.storeEmbedding('skill-2', new Float32Array(384).fill(0.2), 'Skill 2');
|
|
120
|
+
store.storeEmbedding('skill-3', new Float32Array(384).fill(0.3), 'Skill 3');
|
|
121
|
+
const all = store.getAllEmbeddings();
|
|
122
|
+
expect(all.size).toBe(3);
|
|
123
|
+
expect(all.has('skill-1')).toBe(true);
|
|
124
|
+
expect(all.has('skill-2')).toBe(true);
|
|
125
|
+
expect(all.has('skill-3')).toBe(true);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
describe('findSimilar', () => {
|
|
129
|
+
beforeEach(() => {
|
|
130
|
+
store = new HNSWEmbeddingStore({ dbPath: testDbPath, useHNSW: false });
|
|
131
|
+
});
|
|
132
|
+
it('should return empty array for empty store', () => {
|
|
133
|
+
const query = new Float32Array(384).fill(0.1);
|
|
134
|
+
const results = store.findSimilar(query, 10);
|
|
135
|
+
expect(results).toEqual([]);
|
|
136
|
+
});
|
|
137
|
+
it('should find similar embeddings (brute-force fallback)', () => {
|
|
138
|
+
// Create embeddings with known similarities
|
|
139
|
+
const embedding1 = new Float32Array(384).fill(0.1);
|
|
140
|
+
const embedding2 = new Float32Array(384).fill(0.2);
|
|
141
|
+
const embedding3 = new Float32Array(384).fill(0.9);
|
|
142
|
+
store.storeEmbedding('similar-high', embedding3, 'High similarity');
|
|
143
|
+
store.storeEmbedding('similar-low', embedding1, 'Low similarity');
|
|
144
|
+
store.storeEmbedding('similar-mid', embedding2, 'Mid similarity');
|
|
145
|
+
// Query with vector close to embedding3
|
|
146
|
+
const query = new Float32Array(384).fill(0.85);
|
|
147
|
+
const results = store.findSimilar(query, 3);
|
|
148
|
+
expect(results.length).toBe(3);
|
|
149
|
+
// Highest similarity should be first
|
|
150
|
+
expect(results[0].skillId).toBe('similar-high');
|
|
151
|
+
expect(results[0].score).toBeGreaterThan(results[1].score);
|
|
152
|
+
});
|
|
153
|
+
it('should respect topK limit', () => {
|
|
154
|
+
for (let i = 0; i < 20; i++) {
|
|
155
|
+
store.storeEmbedding(`skill-${i}`, new Float32Array(384).fill(i / 20), `Skill ${i}`);
|
|
156
|
+
}
|
|
157
|
+
const query = new Float32Array(384).fill(0.5);
|
|
158
|
+
const results = store.findSimilar(query, 5);
|
|
159
|
+
expect(results.length).toBe(5);
|
|
160
|
+
});
|
|
161
|
+
it('should reject query with wrong dimensions', () => {
|
|
162
|
+
const wrongDimQuery = new Float32Array(768).fill(0.1);
|
|
163
|
+
expect(() => {
|
|
164
|
+
store.findSimilar(wrongDimQuery, 10);
|
|
165
|
+
}).toThrow('Query dimension mismatch');
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
describe('cosineSimilarity', () => {
|
|
169
|
+
beforeEach(() => {
|
|
170
|
+
store = new HNSWEmbeddingStore({ dbPath: testDbPath, useHNSW: false });
|
|
171
|
+
});
|
|
172
|
+
it('should return 1 for identical normalized vectors', () => {
|
|
173
|
+
const v = new Float32Array(384);
|
|
174
|
+
for (let i = 0; i < 384; i++) {
|
|
175
|
+
v[i] = 1 / Math.sqrt(384);
|
|
176
|
+
}
|
|
177
|
+
const similarity = store.cosineSimilarity(v, v);
|
|
178
|
+
expect(similarity).toBeCloseTo(1.0);
|
|
179
|
+
});
|
|
180
|
+
it('should return 0 for orthogonal vectors', () => {
|
|
181
|
+
const v1 = new Float32Array(4).fill(0);
|
|
182
|
+
const v2 = new Float32Array(4).fill(0);
|
|
183
|
+
v1[0] = 1;
|
|
184
|
+
v2[1] = 1;
|
|
185
|
+
const similarity = store.cosineSimilarity(v1, v2);
|
|
186
|
+
expect(similarity).toBeCloseTo(0);
|
|
187
|
+
});
|
|
188
|
+
it('should throw for dimension mismatch', () => {
|
|
189
|
+
const v1 = new Float32Array(384).fill(0.1);
|
|
190
|
+
const v2 = new Float32Array(768).fill(0.1);
|
|
191
|
+
expect(() => store.cosineSimilarity(v1, v2)).toThrow('Embedding dimension mismatch');
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
describe('batchInsert', () => {
|
|
195
|
+
beforeEach(() => {
|
|
196
|
+
store = new HNSWEmbeddingStore({ dbPath: testDbPath, useHNSW: false });
|
|
197
|
+
});
|
|
198
|
+
it('should batch insert multiple embeddings', () => {
|
|
199
|
+
const embeddings = [
|
|
200
|
+
{ skillId: 'batch-1', embedding: new Float32Array(384).fill(0.1), text: 'Batch 1' },
|
|
201
|
+
{ skillId: 'batch-2', embedding: new Float32Array(384).fill(0.2), text: 'Batch 2' },
|
|
202
|
+
{ skillId: 'batch-3', embedding: new Float32Array(384).fill(0.3), text: 'Batch 3' },
|
|
203
|
+
];
|
|
204
|
+
const result = store.batchInsert(embeddings);
|
|
205
|
+
expect(result.inserted).toBe(3);
|
|
206
|
+
expect(result.updated).toBe(0);
|
|
207
|
+
expect(result.failed).toBe(0);
|
|
208
|
+
expect(result.durationMs).toBeGreaterThanOrEqual(0);
|
|
209
|
+
// Verify all were stored
|
|
210
|
+
const all = store.getAllEmbeddings();
|
|
211
|
+
expect(all.size).toBe(3);
|
|
212
|
+
});
|
|
213
|
+
it('should report failed insertions', () => {
|
|
214
|
+
const embeddings = [
|
|
215
|
+
{ skillId: 'valid', embedding: new Float32Array(384).fill(0.1), text: 'Valid' },
|
|
216
|
+
{ skillId: 'invalid', embedding: new Float32Array(768).fill(0.1), text: 'Invalid dim' },
|
|
217
|
+
];
|
|
218
|
+
const result = store.batchInsert(embeddings);
|
|
219
|
+
expect(result.inserted).toBe(1);
|
|
220
|
+
expect(result.failed).toBe(1);
|
|
221
|
+
expect(result.errors.length).toBe(1);
|
|
222
|
+
expect(result.errors[0].skillId).toBe('invalid');
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
describe('removeEmbedding', () => {
|
|
226
|
+
beforeEach(() => {
|
|
227
|
+
store = new HNSWEmbeddingStore({ dbPath: testDbPath, useHNSW: false });
|
|
228
|
+
});
|
|
229
|
+
it('should remove existing embedding', () => {
|
|
230
|
+
store.storeEmbedding('to-remove', new Float32Array(384).fill(0.1), 'To remove');
|
|
231
|
+
expect(store.getEmbedding('to-remove')).not.toBeNull();
|
|
232
|
+
const removed = store.removeEmbedding('to-remove');
|
|
233
|
+
expect(removed).toBe(true);
|
|
234
|
+
expect(store.getEmbedding('to-remove')).toBeNull();
|
|
235
|
+
});
|
|
236
|
+
it('should return false for non-existent embedding', () => {
|
|
237
|
+
const removed = store.removeEmbedding('non-existent');
|
|
238
|
+
expect(removed).toBe(false);
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
describe('getStats', () => {
|
|
242
|
+
beforeEach(() => {
|
|
243
|
+
store = new HNSWEmbeddingStore({ dbPath: testDbPath, useHNSW: false });
|
|
244
|
+
});
|
|
245
|
+
it('should return correct stats for empty store', () => {
|
|
246
|
+
const stats = store.getStats();
|
|
247
|
+
expect(stats.vectorCount).toBe(0);
|
|
248
|
+
expect(stats.utilizationPercent).toBe(0);
|
|
249
|
+
expect(stats.dimensions).toBe(384);
|
|
250
|
+
});
|
|
251
|
+
it('should return correct stats after insertions', () => {
|
|
252
|
+
store.storeEmbedding('s1', new Float32Array(384).fill(0.1), 'S1');
|
|
253
|
+
store.storeEmbedding('s2', new Float32Array(384).fill(0.2), 'S2');
|
|
254
|
+
const stats = store.getStats();
|
|
255
|
+
expect(stats.vectorCount).toBe(2);
|
|
256
|
+
expect(stats.isHNSWEnabled).toBe(false); // useHNSW: false
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
describe('factory functions', () => {
|
|
260
|
+
it('createHNSWStore should create with preset config', () => {
|
|
261
|
+
store = createHNSWStore('large', { dbPath: testDbPath });
|
|
262
|
+
const stats = store.getStats();
|
|
263
|
+
expect(stats.m).toBe(HNSW_PRESETS.large.m);
|
|
264
|
+
expect(stats.efConstruction).toBe(HNSW_PRESETS.large.efConstruction);
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
describe('isHNSWAvailable', () => {
|
|
268
|
+
it('should return boolean indicating availability', async () => {
|
|
269
|
+
const available = await isHNSWAvailable();
|
|
270
|
+
expect(typeof available).toBe('boolean');
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
describe('DEFAULT_HNSW_CONFIG', () => {
|
|
274
|
+
it('should have expected default values', () => {
|
|
275
|
+
expect(DEFAULT_HNSW_CONFIG.m).toBe(16);
|
|
276
|
+
expect(DEFAULT_HNSW_CONFIG.efConstruction).toBe(200);
|
|
277
|
+
expect(DEFAULT_HNSW_CONFIG.efSearch).toBe(100);
|
|
278
|
+
expect(DEFAULT_HNSW_CONFIG.dimensions).toBe(384);
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
describe('HNSW_PRESETS', () => {
|
|
282
|
+
it('should have all expected presets', () => {
|
|
283
|
+
expect(HNSW_PRESETS).toHaveProperty('small');
|
|
284
|
+
expect(HNSW_PRESETS).toHaveProperty('medium');
|
|
285
|
+
expect(HNSW_PRESETS).toHaveProperty('large');
|
|
286
|
+
expect(HNSW_PRESETS).toHaveProperty('xlarge');
|
|
287
|
+
});
|
|
288
|
+
it('should have increasing M values across presets', () => {
|
|
289
|
+
expect(HNSW_PRESETS.small.m).toBeLessThan(HNSW_PRESETS.medium.m);
|
|
290
|
+
expect(HNSW_PRESETS.medium.m).toBeLessThan(HNSW_PRESETS.large.m);
|
|
291
|
+
expect(HNSW_PRESETS.large.m).toBeLessThan(HNSW_PRESETS.xlarge.m);
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
//# sourceMappingURL=hnsw-store.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hnsw-store.test.js","sourceRoot":"","sources":["../../../tests/embeddings/hnsw-store.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-1536: End-to-End Learning Loop Integration Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests the complete learning loop from signal collection through
|
|
5
|
+
* personalized recommendations, verifying measurable improvements.
|
|
6
|
+
*
|
|
7
|
+
* Test Cases:
|
|
8
|
+
* 1. Learning improves recommendations over 10 interactions
|
|
9
|
+
* 2. Dismiss patterns reduce category scores measurably
|
|
10
|
+
* 3. Combined signals (accept + usage) boost scores higher
|
|
11
|
+
* 4. Learning persists across session restart
|
|
12
|
+
*
|
|
13
|
+
* @see packages/core/src/learning/interfaces.ts
|
|
14
|
+
* @see docs/execution/phase5-testing-execution.md
|
|
15
|
+
*/
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=e2e-learning.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"e2e-learning.test.d.ts","sourceRoot":"","sources":["../../../../tests/integration/neural/e2e-learning.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-1536: End-to-End Learning Loop Integration Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests the complete learning loop from signal collection through
|
|
5
|
+
* personalized recommendations, verifying measurable improvements.
|
|
6
|
+
*
|
|
7
|
+
* Test Cases:
|
|
8
|
+
* 1. Learning improves recommendations over 10 interactions
|
|
9
|
+
* 2. Dismiss patterns reduce category scores measurably
|
|
10
|
+
* 3. Combined signals (accept + usage) boost scores higher
|
|
11
|
+
* 4. Learning persists across session restart
|
|
12
|
+
*
|
|
13
|
+
* @see packages/core/src/learning/interfaces.ts
|
|
14
|
+
* @see docs/execution/phase5-testing-execution.md
|
|
15
|
+
*/
|
|
16
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
17
|
+
import { createNeuralTestContext, cleanupNeuralTestContext, createDefaultProfile, } from './setup.js';
|
|
18
|
+
import { generateContext, generateUserJourney, generateSkillSet } from './helpers.js';
|
|
19
|
+
import { SignalType, SkillCategory, COLD_START_WEIGHTS } from '../../../src/learning/types.js';
|
|
20
|
+
describe('E2E Learning Loop Integration', () => {
|
|
21
|
+
let ctx;
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
ctx = createNeuralTestContext();
|
|
24
|
+
});
|
|
25
|
+
afterEach(async () => {
|
|
26
|
+
await cleanupNeuralTestContext(ctx);
|
|
27
|
+
});
|
|
28
|
+
describe('Learning Effectiveness', () => {
|
|
29
|
+
it('should improve recommendations over 10 interactions', async () => {
|
|
30
|
+
// Generate initial recommendations (cold start)
|
|
31
|
+
const skills = generateSkillSet(5);
|
|
32
|
+
const initialRecs = await ctx.personalizationEngine.personalizeRecommendations(skills);
|
|
33
|
+
// At cold start, personalization should not be applied
|
|
34
|
+
expect(initialRecs[0].personalization_applied).toBe(false);
|
|
35
|
+
// Simulate 10 interactions with testing skills
|
|
36
|
+
for (let i = 0; i < 10; i++) {
|
|
37
|
+
await ctx.signalCollector.recordAccept(`testing-skill-${i}`, generateContext({ category: SkillCategory.TESTING }));
|
|
38
|
+
await ctx.signalCollector.recordUsage(`testing-skill-${i}`, 'daily');
|
|
39
|
+
}
|
|
40
|
+
// Train the profile
|
|
41
|
+
let profile = createDefaultProfile();
|
|
42
|
+
const signals = await ctx.signalCollector.getSignals({});
|
|
43
|
+
profile = await ctx.preferenceLearner.batchUpdateProfile(profile, signals);
|
|
44
|
+
await ctx.profileRepository.saveProfile(profile);
|
|
45
|
+
// Create a new skill set with same base scores
|
|
46
|
+
const uniformSkills = [
|
|
47
|
+
{
|
|
48
|
+
skill_id: 'testing-new',
|
|
49
|
+
base_score: 0.5,
|
|
50
|
+
skill_data: { category: SkillCategory.TESTING, trustTier: 'community' },
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
skill_id: 'devops-new',
|
|
54
|
+
base_score: 0.5,
|
|
55
|
+
skill_data: { category: SkillCategory.DEVOPS, trustTier: 'community' },
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
skill_id: 'frontend-new',
|
|
59
|
+
base_score: 0.5,
|
|
60
|
+
skill_data: { category: SkillCategory.FRONTEND, trustTier: 'community' },
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
const learnedRecs = await ctx.personalizationEngine.personalizeRecommendations(uniformSkills);
|
|
64
|
+
// Personalization should now be applied
|
|
65
|
+
expect(learnedRecs[0].personalization_applied).toBe(true);
|
|
66
|
+
// Testing skill should rank higher after learning
|
|
67
|
+
const testingRec = learnedRecs.find((r) => r.skill_id === 'testing-new');
|
|
68
|
+
// Testing should have higher personalized score due to positive signals
|
|
69
|
+
expect(testingRec.personalized_score).toBeGreaterThan(testingRec.base_score);
|
|
70
|
+
expect(testingRec.score_breakdown.category_boost).toBeGreaterThan(0);
|
|
71
|
+
// First position should be testing (highest learned preference)
|
|
72
|
+
expect(learnedRecs[0].skill_id).toBe('testing-new');
|
|
73
|
+
});
|
|
74
|
+
it('should reduce category scores measurably with dismiss patterns', async () => {
|
|
75
|
+
// Simulate consistent dismissal of SECURITY category
|
|
76
|
+
for (let i = 0; i < 10; i++) {
|
|
77
|
+
await ctx.signalCollector.recordDismiss(`security-skill-${i}`, generateContext({ category: SkillCategory.SECURITY }));
|
|
78
|
+
}
|
|
79
|
+
// Also add accepts for comparison (need threshold)
|
|
80
|
+
for (let i = 0; i < 5; i++) {
|
|
81
|
+
await ctx.signalCollector.recordAccept(`git-skill-${i}`, generateContext({ category: SkillCategory.GIT }));
|
|
82
|
+
}
|
|
83
|
+
// Train profile
|
|
84
|
+
let profile = createDefaultProfile();
|
|
85
|
+
const signals = await ctx.signalCollector.getSignals({});
|
|
86
|
+
profile = await ctx.preferenceLearner.batchUpdateProfile(profile, signals);
|
|
87
|
+
await ctx.profileRepository.saveProfile(profile);
|
|
88
|
+
// Security category weight should be negative
|
|
89
|
+
const securityWeight = profile.category_weights[SkillCategory.SECURITY] ?? 0;
|
|
90
|
+
const coldStartWeight = COLD_START_WEIGHTS.category_weights[SkillCategory.SECURITY] ?? 0;
|
|
91
|
+
expect(securityWeight).toBeLessThan(coldStartWeight);
|
|
92
|
+
// Git category weight should be positive (or at least higher than security)
|
|
93
|
+
const gitWeight = profile.category_weights[SkillCategory.GIT] ?? 0;
|
|
94
|
+
expect(gitWeight).toBeGreaterThan(securityWeight);
|
|
95
|
+
// Test personalization with uniform base scores
|
|
96
|
+
const skills = [
|
|
97
|
+
{
|
|
98
|
+
skill_id: 'security-new',
|
|
99
|
+
base_score: 0.7,
|
|
100
|
+
skill_data: { category: SkillCategory.SECURITY },
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
skill_id: 'git-new',
|
|
104
|
+
base_score: 0.7,
|
|
105
|
+
skill_data: { category: SkillCategory.GIT },
|
|
106
|
+
},
|
|
107
|
+
];
|
|
108
|
+
const personalized = await ctx.personalizationEngine.personalizeRecommendations(skills);
|
|
109
|
+
const securityRec = personalized.find((r) => r.skill_id === 'security-new');
|
|
110
|
+
const gitRec = personalized.find((r) => r.skill_id === 'git-new');
|
|
111
|
+
// Security should have negative category boost
|
|
112
|
+
expect(securityRec.score_breakdown.category_boost).toBeLessThan(0);
|
|
113
|
+
// Git should rank higher than security despite same base score
|
|
114
|
+
expect(gitRec.personalized_score).toBeGreaterThan(securityRec.personalized_score);
|
|
115
|
+
});
|
|
116
|
+
it('should boost scores higher with combined accept + usage signals', async () => {
|
|
117
|
+
// Add signals for two skills with same accept count but different usage
|
|
118
|
+
const heavilyUsedSkill = 'heavily-used';
|
|
119
|
+
const leastUsedSkill = 'least-used';
|
|
120
|
+
// Both get accepted
|
|
121
|
+
await ctx.signalCollector.recordAccept(heavilyUsedSkill, generateContext({
|
|
122
|
+
category: SkillCategory.DOCUMENTATION,
|
|
123
|
+
}));
|
|
124
|
+
await ctx.signalCollector.recordAccept(leastUsedSkill, generateContext({
|
|
125
|
+
category: SkillCategory.ANALYSIS,
|
|
126
|
+
}));
|
|
127
|
+
// Add more accepts for threshold
|
|
128
|
+
for (let i = 0; i < 5; i++) {
|
|
129
|
+
await ctx.signalCollector.recordAccept(`other-${i}`, generateContext());
|
|
130
|
+
}
|
|
131
|
+
// Heavily used skill gets daily usage signals
|
|
132
|
+
for (let i = 0; i < 10; i++) {
|
|
133
|
+
await ctx.signalCollector.recordUsage(heavilyUsedSkill, 'daily');
|
|
134
|
+
}
|
|
135
|
+
// Train profile
|
|
136
|
+
let profile = createDefaultProfile();
|
|
137
|
+
const signals = await ctx.signalCollector.getSignals({});
|
|
138
|
+
profile = await ctx.preferenceLearner.batchUpdateProfile(profile, signals);
|
|
139
|
+
await ctx.profileRepository.saveProfile(profile);
|
|
140
|
+
// Documentation category (heavily used) should have higher weight
|
|
141
|
+
const docWeight = profile.category_weights[SkillCategory.DOCUMENTATION] ?? 0;
|
|
142
|
+
const analysisWeight = profile.category_weights[SkillCategory.ANALYSIS] ?? 0;
|
|
143
|
+
// Daily usage signals have weight 1.0 (highest), so documentation should be higher
|
|
144
|
+
expect(docWeight).toBeGreaterThan(analysisWeight);
|
|
145
|
+
});
|
|
146
|
+
it('should persist learning across session restart', async () => {
|
|
147
|
+
// Session 1: Build preferences
|
|
148
|
+
for (let i = 0; i < 10; i++) {
|
|
149
|
+
await ctx.signalCollector.recordAccept(`backend-skill-${i}`, generateContext({ category: SkillCategory.BACKEND }));
|
|
150
|
+
}
|
|
151
|
+
let profile = createDefaultProfile();
|
|
152
|
+
const signals1 = await ctx.signalCollector.getSignals({});
|
|
153
|
+
profile = await ctx.preferenceLearner.batchUpdateProfile(profile, signals1);
|
|
154
|
+
await ctx.profileRepository.saveProfile(profile, 'persistent-user');
|
|
155
|
+
const savedWeight = profile.category_weights[SkillCategory.BACKEND];
|
|
156
|
+
const savedSignalCount = profile.signal_count;
|
|
157
|
+
// Simulate session restart by creating new context
|
|
158
|
+
// (but keeping same profile repository state - in real implementation this would be persisted)
|
|
159
|
+
const savedProfile = await ctx.profileRepository.getProfile('persistent-user');
|
|
160
|
+
// Session 2: Load profile and verify learning persisted
|
|
161
|
+
expect(savedProfile).not.toBeNull();
|
|
162
|
+
expect(savedProfile.signal_count).toBe(savedSignalCount);
|
|
163
|
+
expect(savedProfile.category_weights[SkillCategory.BACKEND]).toBe(savedWeight);
|
|
164
|
+
// Continue learning in session 2
|
|
165
|
+
await ctx.signalCollector.recordAccept('backend-skill-new', generateContext({ category: SkillCategory.BACKEND }));
|
|
166
|
+
const signals2 = await ctx.signalCollector.getSignals({
|
|
167
|
+
type: SignalType.ACCEPT,
|
|
168
|
+
});
|
|
169
|
+
const latestSignal = signals2[signals2.length - 1];
|
|
170
|
+
const updatedProfile = await ctx.preferenceLearner.updateProfile(savedProfile, latestSignal);
|
|
171
|
+
// Signal count should increment
|
|
172
|
+
expect(updatedProfile.signal_count).toBe(savedSignalCount + 1);
|
|
173
|
+
// Weight should continue to accumulate
|
|
174
|
+
expect(updatedProfile.category_weights[SkillCategory.BACKEND]).toBeGreaterThanOrEqual(savedWeight);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
describe('User Journey Simulation', () => {
|
|
178
|
+
it('should handle complete successful user journey', async () => {
|
|
179
|
+
const skillId = 'journey-skill-success';
|
|
180
|
+
const signals = generateUserJourney(skillId, 'successful', SkillCategory.TESTING);
|
|
181
|
+
// Simulate the journey by adding signals
|
|
182
|
+
for (const signal of signals) {
|
|
183
|
+
ctx.signalCollector.addSignal(signal);
|
|
184
|
+
}
|
|
185
|
+
// Add more signals to meet threshold
|
|
186
|
+
for (let i = 0; i < 5; i++) {
|
|
187
|
+
await ctx.signalCollector.recordAccept(`filler-${i}`, generateContext());
|
|
188
|
+
}
|
|
189
|
+
// Train profile
|
|
190
|
+
let profile = createDefaultProfile();
|
|
191
|
+
const allSignals = await ctx.signalCollector.getSignals({});
|
|
192
|
+
profile = await ctx.preferenceLearner.batchUpdateProfile(profile, allSignals);
|
|
193
|
+
// Successful journey (accept + multiple usage) should strongly boost category
|
|
194
|
+
expect(profile.category_weights[SkillCategory.TESTING]).toBeGreaterThan(COLD_START_WEIGHTS.category_weights[SkillCategory.TESTING] ?? 0);
|
|
195
|
+
});
|
|
196
|
+
it('should handle abandoned user journey', async () => {
|
|
197
|
+
const skillId = 'journey-skill-abandoned';
|
|
198
|
+
const signals = generateUserJourney(skillId, 'abandoned', SkillCategory.DEVOPS);
|
|
199
|
+
// Simulate the journey
|
|
200
|
+
for (const signal of signals) {
|
|
201
|
+
ctx.signalCollector.addSignal(signal);
|
|
202
|
+
}
|
|
203
|
+
// Add more signals to meet threshold
|
|
204
|
+
for (let i = 0; i < 5; i++) {
|
|
205
|
+
await ctx.signalCollector.recordAccept(`filler-${i}`, generateContext());
|
|
206
|
+
}
|
|
207
|
+
// Train profile
|
|
208
|
+
let profile = createDefaultProfile();
|
|
209
|
+
const allSignals = await ctx.signalCollector.getSignals({});
|
|
210
|
+
profile = await ctx.preferenceLearner.batchUpdateProfile(profile, allSignals);
|
|
211
|
+
// Abandoned journey (accept + abandoned) has net negative effect
|
|
212
|
+
// ACCEPT weight (0.5) + ABANDONED weight (-0.7) = -0.2 net
|
|
213
|
+
// The net effect should be slightly negative due to ABANDONED signal
|
|
214
|
+
// But we also added 5 filler accepts, so the overall effect depends on implementation
|
|
215
|
+
// Verify profile was updated
|
|
216
|
+
expect(profile.signal_count).toBeGreaterThan(0);
|
|
217
|
+
});
|
|
218
|
+
it('should handle uninstalled user journey', async () => {
|
|
219
|
+
const skillId = 'journey-skill-uninstalled';
|
|
220
|
+
const signals = generateUserJourney(skillId, 'uninstalled', SkillCategory.SECURITY);
|
|
221
|
+
// Simulate the journey
|
|
222
|
+
for (const signal of signals) {
|
|
223
|
+
ctx.signalCollector.addSignal(signal);
|
|
224
|
+
}
|
|
225
|
+
// Add minimal fillers for threshold
|
|
226
|
+
for (let i = 0; i < 3; i++) {
|
|
227
|
+
await ctx.signalCollector.recordAccept(`filler-${i}`, generateContext());
|
|
228
|
+
}
|
|
229
|
+
// Train profile
|
|
230
|
+
let profile = createDefaultProfile();
|
|
231
|
+
const allSignals = await ctx.signalCollector.getSignals({});
|
|
232
|
+
profile = await ctx.preferenceLearner.batchUpdateProfile(profile, allSignals);
|
|
233
|
+
// Uninstalled skill should be in negative patterns
|
|
234
|
+
expect(profile.negative_patterns.skill_ids).toContain(skillId);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
//# sourceMappingURL=e2e-learning.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"e2e-learning.test.js","sourceRoot":"","sources":["../../../../tests/integration/neural/e2e-learning.test.ts"],"names":[],"mappings":"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"}
|