@utaba/deep-memory 0.17.0 → 0.19.0
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/{StorageProvider-DDTLUcuj.d.ts → StorageProvider-CgWRfvIZ.d.ts} +1 -1
- package/dist/{StorageProvider-DGH7bVuK.d.cts → StorageProvider-RPhT7vtH.d.cts} +1 -1
- package/dist/index.cjs +131 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +131 -45
- package/dist/index.js.map +1 -1
- package/dist/{portability-Dbm6vCdD.d.cts → portability-BHyXmv5-.d.cts} +21 -5
- package/dist/{portability-Dbm6vCdD.d.ts → portability-BHyXmv5-.d.ts} +21 -5
- package/dist/providers/index.d.cts +4 -4
- package/dist/providers/index.d.ts +4 -4
- package/dist/testing/conformance.cjs +30 -2
- package/dist/testing/conformance.cjs.map +1 -1
- package/dist/testing/conformance.d.cts +2 -2
- package/dist/testing/conformance.d.ts +2 -2
- package/dist/testing/conformance.js +30 -2
- package/dist/testing/conformance.js.map +1 -1
- package/dist/{traversal-OkXViSTI.d.ts → traversal-BK_3Cqj3.d.cts} +12 -7
- package/dist/{traversal-1f35drOx.d.cts → traversal-CIGJcdCb.d.ts} +12 -7
- package/dist/types/index.d.cts +3 -3
- package/dist/types/index.d.ts +3 -3
- package/package.json +2 -2
|
@@ -473,8 +473,13 @@ interface SearchOptions extends PaginationOptions {
|
|
|
473
473
|
entityTypes?: string[];
|
|
474
474
|
}
|
|
475
475
|
|
|
476
|
-
/**
|
|
477
|
-
|
|
476
|
+
/**
|
|
477
|
+
* Direction filter for relationship queries, relative to the queried entity.
|
|
478
|
+
* - `'out'` — edges where the entity is the source
|
|
479
|
+
* - `'in'` — edges where the entity is the target
|
|
480
|
+
* - `'both'` — edges in either direction
|
|
481
|
+
*/
|
|
482
|
+
type RelationshipDirection = 'out' | 'in' | 'both';
|
|
478
483
|
/** Public relationship representation */
|
|
479
484
|
interface Relationship {
|
|
480
485
|
id: string;
|
|
@@ -522,10 +527,15 @@ interface EnrichedRelationship {
|
|
|
522
527
|
bidirectional: boolean;
|
|
523
528
|
provenance: Provenance;
|
|
524
529
|
}
|
|
525
|
-
/**
|
|
530
|
+
/**
|
|
531
|
+
* Aggregated relationship counts by type, keyed by direction relative to the
|
|
532
|
+
* entity the summary belongs to.
|
|
533
|
+
* - `out` — edges where the entity is the source
|
|
534
|
+
* - `in` — edges where the entity is the target
|
|
535
|
+
*/
|
|
526
536
|
interface RelationshipSummary {
|
|
527
|
-
|
|
528
|
-
|
|
537
|
+
out: Record<string, number>;
|
|
538
|
+
in: Record<string, number>;
|
|
529
539
|
}
|
|
530
540
|
/** Result of a bulk relationship removal */
|
|
531
541
|
interface RemoveRelationshipsResult {
|
|
@@ -922,6 +932,12 @@ interface ExportManifest {
|
|
|
922
932
|
type?: string;
|
|
923
933
|
label: string;
|
|
924
934
|
description?: string;
|
|
935
|
+
/** Free-form legal terms, licence, or compliance notes carried with the repository */
|
|
936
|
+
legal?: string;
|
|
937
|
+
/** Repository owner identifier */
|
|
938
|
+
owner?: string;
|
|
939
|
+
/** Extensible metadata bag (embeddingModelId, embeddingDimensions, custom fields) */
|
|
940
|
+
metadata?: RepositoryMetadata;
|
|
925
941
|
vocabularyVersion: string;
|
|
926
942
|
governanceMode: GovernanceMode;
|
|
927
943
|
};
|
|
@@ -473,8 +473,13 @@ interface SearchOptions extends PaginationOptions {
|
|
|
473
473
|
entityTypes?: string[];
|
|
474
474
|
}
|
|
475
475
|
|
|
476
|
-
/**
|
|
477
|
-
|
|
476
|
+
/**
|
|
477
|
+
* Direction filter for relationship queries, relative to the queried entity.
|
|
478
|
+
* - `'out'` — edges where the entity is the source
|
|
479
|
+
* - `'in'` — edges where the entity is the target
|
|
480
|
+
* - `'both'` — edges in either direction
|
|
481
|
+
*/
|
|
482
|
+
type RelationshipDirection = 'out' | 'in' | 'both';
|
|
478
483
|
/** Public relationship representation */
|
|
479
484
|
interface Relationship {
|
|
480
485
|
id: string;
|
|
@@ -522,10 +527,15 @@ interface EnrichedRelationship {
|
|
|
522
527
|
bidirectional: boolean;
|
|
523
528
|
provenance: Provenance;
|
|
524
529
|
}
|
|
525
|
-
/**
|
|
530
|
+
/**
|
|
531
|
+
* Aggregated relationship counts by type, keyed by direction relative to the
|
|
532
|
+
* entity the summary belongs to.
|
|
533
|
+
* - `out` — edges where the entity is the source
|
|
534
|
+
* - `in` — edges where the entity is the target
|
|
535
|
+
*/
|
|
526
536
|
interface RelationshipSummary {
|
|
527
|
-
|
|
528
|
-
|
|
537
|
+
out: Record<string, number>;
|
|
538
|
+
in: Record<string, number>;
|
|
529
539
|
}
|
|
530
540
|
/** Result of a bulk relationship removal */
|
|
531
541
|
interface RemoveRelationshipsResult {
|
|
@@ -922,6 +932,12 @@ interface ExportManifest {
|
|
|
922
932
|
type?: string;
|
|
923
933
|
label: string;
|
|
924
934
|
description?: string;
|
|
935
|
+
/** Free-form legal terms, licence, or compliance notes carried with the repository */
|
|
936
|
+
legal?: string;
|
|
937
|
+
/** Repository owner identifier */
|
|
938
|
+
owner?: string;
|
|
939
|
+
/** Extensible metadata bag (embeddingModelId, embeddingDimensions, custom fields) */
|
|
940
|
+
metadata?: RepositoryMetadata;
|
|
925
941
|
vocabularyVersion: string;
|
|
926
942
|
governanceMode: GovernanceMode;
|
|
927
943
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { E as EnsureSchemaResult, a as EntityReadOptions, S as StorageProvider } from '../StorageProvider-
|
|
2
|
-
import { al as SearchOptions, T as PaginatedResult, ak as SearchHit } from '../portability-
|
|
3
|
-
import { f as TraversalSpec, d as TraversalResult } from '../traversal-
|
|
1
|
+
export { E as EnsureSchemaResult, a as EntityReadOptions, S as StorageProvider } from '../StorageProvider-RPhT7vtH.cjs';
|
|
2
|
+
import { al as SearchOptions, T as PaginatedResult, ak as SearchHit } from '../portability-BHyXmv5-.cjs';
|
|
3
|
+
import { f as TraversalSpec, d as TraversalResult } from '../traversal-BK_3Cqj3.cjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* EmbeddingProvider — enables semantic search and vocabulary deduplication.
|
|
@@ -145,7 +145,7 @@ interface GraphTraversalCapabilities {
|
|
|
145
145
|
/** Whether the provider supports dedup across traversal results. */
|
|
146
146
|
supportsDedup: boolean;
|
|
147
147
|
/**
|
|
148
|
-
* Whether the provider can return relationship summaries (
|
|
148
|
+
* Whether the provider can return relationship summaries (out/in
|
|
149
149
|
* counts by type) natively in a single traversal query.
|
|
150
150
|
* When false, the library falls back to a batch fetch + local computation.
|
|
151
151
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { E as EnsureSchemaResult, a as EntityReadOptions, S as StorageProvider } from '../StorageProvider-
|
|
2
|
-
import { al as SearchOptions, T as PaginatedResult, ak as SearchHit } from '../portability-
|
|
3
|
-
import { f as TraversalSpec, d as TraversalResult } from '../traversal-
|
|
1
|
+
export { E as EnsureSchemaResult, a as EntityReadOptions, S as StorageProvider } from '../StorageProvider-CgWRfvIZ.js';
|
|
2
|
+
import { al as SearchOptions, T as PaginatedResult, ak as SearchHit } from '../portability-BHyXmv5-.js';
|
|
3
|
+
import { f as TraversalSpec, d as TraversalResult } from '../traversal-CIGJcdCb.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* EmbeddingProvider — enables semantic search and vocabulary deduplication.
|
|
@@ -145,7 +145,7 @@ interface GraphTraversalCapabilities {
|
|
|
145
145
|
/** Whether the provider supports dedup across traversal results. */
|
|
146
146
|
supportsDedup: boolean;
|
|
147
147
|
/**
|
|
148
|
-
* Whether the provider can return relationship summaries (
|
|
148
|
+
* Whether the provider can return relationship summaries (out/in
|
|
149
149
|
* counts by type) natively in a single traversal query.
|
|
150
150
|
* When false, the library falls back to a batch fetch + local computation.
|
|
151
151
|
*/
|
|
@@ -285,10 +285,10 @@ function runStorageProviderConformanceTests(factory) {
|
|
|
285
285
|
(0, import_vitest.it)("filters relationships by direction", async () => {
|
|
286
286
|
await provider.createRelationship(repoId, makeRelationship("r1", "connects", "a", "b"));
|
|
287
287
|
await provider.createRelationship(repoId, makeRelationship("r2", "connects", "c", "a"));
|
|
288
|
-
const outbound = await provider.getEntityRelationships(repoId, "a", { direction: "
|
|
288
|
+
const outbound = await provider.getEntityRelationships(repoId, "a", { direction: "out" });
|
|
289
289
|
(0, import_vitest.expect)(outbound.items).toHaveLength(1);
|
|
290
290
|
(0, import_vitest.expect)(outbound.items[0].targetEntityId).toBe("b");
|
|
291
|
-
const inbound = await provider.getEntityRelationships(repoId, "a", { direction: "
|
|
291
|
+
const inbound = await provider.getEntityRelationships(repoId, "a", { direction: "in" });
|
|
292
292
|
(0, import_vitest.expect)(inbound.items).toHaveLength(1);
|
|
293
293
|
(0, import_vitest.expect)(inbound.items[0].sourceEntityId).toBe("c");
|
|
294
294
|
});
|
|
@@ -316,6 +316,19 @@ function runStorageProviderConformanceTests(factory) {
|
|
|
316
316
|
(0, import_vitest.expect)(result.centerId).toBe("a");
|
|
317
317
|
(0, import_vitest.expect)(result.layers).toHaveLength(1);
|
|
318
318
|
});
|
|
319
|
+
(0, import_vitest.it)("dedupes connected entities per (layer, relationship-type) bucket", async () => {
|
|
320
|
+
await provider.createRelationship(repoId, makeRelationship("r3", "links", "c", "b"));
|
|
321
|
+
const result = await provider.exploreNeighborhood(repoId, "a", {
|
|
322
|
+
depth: 2,
|
|
323
|
+
direction: "both",
|
|
324
|
+
limitPerType: 10,
|
|
325
|
+
offsetPerType: 0
|
|
326
|
+
});
|
|
327
|
+
const linksLayer2 = result.layers[1]?.["links"];
|
|
328
|
+
(0, import_vitest.expect)(linksLayer2).toBeDefined();
|
|
329
|
+
(0, import_vitest.expect)(linksLayer2.total).toBe(1);
|
|
330
|
+
(0, import_vitest.expect)(linksLayer2.entities.map((e) => e.id)).toEqual(["c"]);
|
|
331
|
+
});
|
|
319
332
|
(0, import_vitest.it)("finds paths between connected entities", async () => {
|
|
320
333
|
const result = await provider.findPaths(repoId, "a", "c", {
|
|
321
334
|
maxDepth: 3,
|
|
@@ -336,6 +349,21 @@ function runStorageProviderConformanceTests(factory) {
|
|
|
336
349
|
});
|
|
337
350
|
(0, import_vitest.expect)(result.paths).toHaveLength(0);
|
|
338
351
|
});
|
|
352
|
+
(0, import_vitest.it)("returns only simple paths (no repeated vertices)", async () => {
|
|
353
|
+
await provider.createEntity(repoId, makeEntity("d", "node", "D"));
|
|
354
|
+
await provider.createRelationship(repoId, makeRelationship("r3", "links", "c", "d"));
|
|
355
|
+
await provider.createRelationship(repoId, makeRelationship("r4", "links", "c", "d"));
|
|
356
|
+
const result = await provider.findPaths(repoId, "a", "c", {
|
|
357
|
+
maxDepth: 4,
|
|
358
|
+
limit: 50,
|
|
359
|
+
offset: 0
|
|
360
|
+
});
|
|
361
|
+
for (const path of result.paths) {
|
|
362
|
+
(0, import_vitest.expect)(new Set(path.entityIds).size).toBe(path.entityIds.length);
|
|
363
|
+
(0, import_vitest.expect)(path.entityIds[path.entityIds.length - 1]).toBe("c");
|
|
364
|
+
}
|
|
365
|
+
(0, import_vitest.expect)(result.totalPaths).toBe(1);
|
|
366
|
+
});
|
|
339
367
|
(0, import_vitest.it)("finds paths through non-bidirectional inbound edges", async () => {
|
|
340
368
|
await provider.createEntity(repoId, makeEntity("d", "node", "D"));
|
|
341
369
|
await provider.createRelationship(repoId, makeRelationship("r3", "links", "d", "b"));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/providers-builtin/conformance.ts"],"sourcesContent":["// Provider Conformance Test Suite\n// Any StorageProvider implementer can import and run these tests to verify conformance.\n//\n// Usage:\n// import { runStorageProviderConformanceTests } from '@utaba/deep-memory';\n// runStorageProviderConformanceTests(() => new MyStorageProvider());\n\nimport { describe, it, expect, beforeEach } from 'vitest';\nimport type { StorageProvider } from '../providers/StorageProvider.js';\nimport type { StoredEntity } from '../types/entities.js';\nimport type { StoredRelationship } from '../types/relationships.js';\nimport type { Provenance } from '../types/provenance.js';\n\nfunction makeProvenance(): Provenance {\n const now = new Date().toISOString();\n return {\n createdBy: 'conformance-test',\n createdByType: 'agent',\n createdAt: now,\n modifiedBy: 'conformance-test',\n modifiedByType: 'agent',\n modifiedAt: now,\n };\n}\n\nfunction makeEntity(id: string, type = 'test-type', label?: string): StoredEntity {\n return {\n id,\n slug: `${type}:${(label ?? id).toLowerCase().replace(/[^a-z0-9]+/g, '-')}`,\n entityType: type,\n label: label ?? id,\n summary: `Summary for ${id}`,\n properties: { key: 'value' },\n provenance: makeProvenance(),\n };\n}\n\nfunction makeRelationship(\n id: string,\n type: string,\n sourceId: string,\n targetId: string,\n bidirectional = false,\n): StoredRelationship {\n return {\n id,\n relationshipType: type,\n sourceEntityId: sourceId,\n targetEntityId: targetId,\n properties: {},\n bidirectional,\n provenance: makeProvenance(),\n };\n}\n\n/**\n * Run the full StorageProvider conformance test suite.\n *\n * @param factory - A function that creates a fresh, empty StorageProvider instance.\n * Called before each test to ensure isolation.\n */\nexport function runStorageProviderConformanceTests(\n factory: () => StorageProvider | Promise<StorageProvider>,\n): void {\n // Use a stable GUID so external cleanup scripts can target it\n const repoId = '40000000-0000-4000-a000-000000000001';\n\n let provider: StorageProvider;\n\n async function setup(): Promise<void> {\n provider = await factory();\n if (provider.initialize) await provider.initialize();\n\n await provider.createRepository({\n repositoryId: repoId,\n label: 'Conformance Test',\n governanceConfig: { mode: 'open' },\n createdAt: new Date().toISOString(),\n createdBy: 'conformance-test',\n });\n }\n\n describe('StorageProvider Conformance Tests', () => {\n beforeEach(async () => {\n await setup();\n });\n\n // ─── Repository ─────────────────────────────────────────\n\n describe('repository operations', () => {\n it('creates a repository', async () => {\n const repo = await provider.getRepository(repoId);\n expect(repo).not.toBeNull();\n expect(repo!.repositoryId).toBe(repoId);\n expect(repo!.label).toBe('Conformance Test');\n });\n\n it('returns null for non-existent repository', async () => {\n const repo = await provider.getRepository('ffffffff-ffff-4fff-afff-ffffffffffff');\n expect(repo).toBeNull();\n });\n\n it('lists repositories', async () => {\n const list = await provider.listRepositories();\n expect(list.items.length).toBeGreaterThanOrEqual(1);\n expect(list.items.some((r) => r.repositoryId === repoId)).toBe(true);\n });\n\n it('updates a repository', async () => {\n const updated = await provider.updateRepository(repoId, {\n label: 'Updated Label',\n description: 'Updated description',\n governanceConfig: { mode: 'open', defaultSimilarityThreshold: 0.4 },\n });\n expect(updated.label).toBe('Updated Label');\n expect(updated.description).toBe('Updated description');\n expect(updated.governanceConfig.defaultSimilarityThreshold).toBe(0.4);\n\n // Verify persistence\n const fetched = await provider.getRepository(repoId);\n expect(fetched!.label).toBe('Updated Label');\n expect(fetched!.governanceConfig.defaultSimilarityThreshold).toBe(0.4);\n });\n\n it('deletes a repository', async () => {\n await provider.deleteRepository(repoId);\n expect(await provider.getRepository(repoId)).toBeNull();\n });\n\n it('returns repository stats', async () => {\n const stats = await provider.getRepositoryStats(repoId);\n expect(stats.entityCount).toBe(0);\n expect(stats.relationshipCount).toBe(0);\n expect(typeof stats.vocabularyVersion).toBe('string');\n });\n });\n\n // ─── Vocabulary ─────────────────────────────────────────\n\n describe('vocabulary operations', () => {\n it('gets and saves vocabulary', async () => {\n const vocab = await provider.getVocabulary(repoId);\n expect(vocab).toBeDefined();\n expect(typeof vocab.version).toBe('string');\n\n const updated = { ...vocab, version: '1.0.0' };\n await provider.saveVocabulary(repoId, updated);\n\n const fetched = await provider.getVocabulary(repoId);\n expect(fetched.version).toBe('1.0.0');\n });\n\n it('returns vocabulary change log', async () => {\n const log = await provider.getVocabularyChangeLog(repoId);\n expect(Array.isArray(log.items)).toBe(true);\n });\n });\n\n // ─── Entities ───────────────────────────────────────────\n\n describe('entity operations', () => {\n it('creates and retrieves an entity', async () => {\n const entity = makeEntity('e1');\n await provider.createEntity(repoId, entity);\n\n const retrieved = await provider.getEntity(repoId, 'e1');\n expect(retrieved).not.toBeNull();\n expect(retrieved!.id).toBe('e1');\n expect(retrieved!.label).toBe('e1');\n });\n\n it('retrieves an entity by slug', async () => {\n const entity = makeEntity('e1', 'test-type', 'Alpha');\n await provider.createEntity(repoId, entity);\n\n const retrieved = await provider.getEntityBySlug(repoId, entity.slug);\n expect(retrieved).not.toBeNull();\n expect(retrieved!.id).toBe('e1');\n expect(retrieved!.slug).toBe(entity.slug);\n });\n\n it('returns null for non-existent entity', async () => {\n const result = await provider.getEntity(repoId, 'nonexistent');\n expect(result).toBeNull();\n });\n\n it('returns null for non-existent slug', async () => {\n const result = await provider.getEntityBySlug(repoId, 'nonexistent:slug');\n expect(result).toBeNull();\n });\n\n it('batch retrieves entities', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n await provider.createEntity(repoId, makeEntity('e2'));\n\n const map = await provider.getEntities(repoId, ['e1', 'e2', 'missing']);\n expect(map.size).toBe(2);\n expect(map.has('e1')).toBe(true);\n expect(map.has('e2')).toBe(true);\n expect(map.has('missing')).toBe(false);\n });\n\n it('updates an entity', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n const updated = await provider.updateEntity(repoId, 'e1', {\n label: 'Updated Label',\n provenance: makeProvenance(),\n });\n expect(updated.label).toBe('Updated Label');\n\n const fetched = await provider.getEntity(repoId, 'e1');\n expect(fetched!.label).toBe('Updated Label');\n });\n\n it('clears summary/data/dataFormat when null is passed', async () => {\n const entity: StoredEntity = {\n ...makeEntity('e1'),\n summary: 'starting summary',\n data: 'raw content',\n dataFormat: 'text/plain',\n };\n await provider.createEntity(repoId, entity);\n\n await provider.updateEntity(repoId, 'e1', {\n summary: null,\n data: null,\n dataFormat: null,\n provenance: makeProvenance(),\n });\n\n const fetched = await provider.getEntity(repoId, 'e1');\n expect(fetched!.summary).toBeUndefined();\n expect(fetched!.data).toBeUndefined();\n expect(fetched!.dataFormat).toBeUndefined();\n });\n\n it('preserves summary/data/dataFormat when undefined is passed', async () => {\n const entity: StoredEntity = {\n ...makeEntity('e1'),\n summary: 'keep me',\n data: 'keep me too',\n dataFormat: 'text/plain',\n };\n await provider.createEntity(repoId, entity);\n\n await provider.updateEntity(repoId, 'e1', {\n label: 'Renamed',\n provenance: makeProvenance(),\n });\n\n const fetched = await provider.getEntity(repoId, 'e1');\n expect(fetched!.summary).toBe('keep me');\n expect(fetched!.data).toBe('keep me too');\n expect(fetched!.dataFormat).toBe('text/plain');\n });\n\n it('deletes an entity', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n await provider.deleteEntity(repoId, 'e1');\n expect(await provider.getEntity(repoId, 'e1')).toBeNull();\n });\n\n it('finds entities by search term', async () => {\n await provider.createEntity(repoId, makeEntity('e1', 'test-type', 'Alpha'));\n await provider.createEntity(repoId, makeEntity('e2', 'test-type', 'Beta'));\n\n const result = await provider.findEntities(repoId, {\n searchTerm: 'alpha',\n limit: 10,\n offset: 0,\n });\n expect(result.items).toHaveLength(1);\n expect(result.items[0]!.label).toBe('Alpha');\n });\n\n it('finds entities by search term case-insensitively', async () => {\n // Locks the invariant that searchTerm matching is case-insensitive on\n // every provider. In-memory lowercases both sides; SQL Server's LIKE\n // is case-insensitive under the default *_CI_AS collation; Cosmos\n // routes through CONTAINS(..., @term, true) on the Document endpoint.\n await provider.createEntity(repoId, makeEntity('e1', 'test-type', 'Alpha'));\n\n const result = await provider.findEntities(repoId, {\n searchTerm: 'ALPHA',\n limit: 10,\n offset: 0,\n });\n expect(result.items).toHaveLength(1);\n expect(result.items[0]!.label).toBe('Alpha');\n });\n\n it('finds entities by type filter', async () => {\n await provider.createEntity(repoId, makeEntity('e1', 'type-a', 'A'));\n await provider.createEntity(repoId, makeEntity('e2', 'type-b', 'B'));\n\n const result = await provider.findEntities(repoId, {\n entityTypes: ['type-a'],\n limit: 10,\n offset: 0,\n });\n expect(result.items).toHaveLength(1);\n expect(result.items[0]!.entityType).toBe('type-a');\n });\n\n it('paginates find results', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n await provider.createEntity(repoId, makeEntity('e2'));\n await provider.createEntity(repoId, makeEntity('e3'));\n\n const page1 = await provider.findEntities(repoId, { limit: 2, offset: 0 });\n expect(page1.items).toHaveLength(2);\n expect(page1.hasMore).toBe(true);\n\n const page2 = await provider.findEntities(repoId, { limit: 2, offset: 2 });\n expect(page2.items).toHaveLength(1);\n expect(page2.hasMore).toBe(false);\n });\n });\n\n // ─── Relationships ──────────────────────────────────────\n\n describe('relationship operations', () => {\n beforeEach(async () => {\n await provider.createEntity(repoId, makeEntity('a'));\n await provider.createEntity(repoId, makeEntity('b'));\n await provider.createEntity(repoId, makeEntity('c'));\n });\n\n it('creates and retrieves a relationship', async () => {\n const rel = makeRelationship('r1', 'connects', 'a', 'b');\n await provider.createRelationship(repoId, rel);\n\n const retrieved = await provider.getRelationship(repoId, 'r1');\n expect(retrieved).not.toBeNull();\n expect(retrieved!.sourceEntityId).toBe('a');\n expect(retrieved!.targetEntityId).toBe('b');\n });\n\n it('returns null for non-existent relationship', async () => {\n expect(await provider.getRelationship(repoId, 'nonexistent')).toBeNull();\n });\n\n it('gets entity relationships', async () => {\n await provider.createRelationship(repoId, makeRelationship('r1', 'connects', 'a', 'b'));\n await provider.createRelationship(repoId, makeRelationship('r2', 'connects', 'c', 'a'));\n\n const result = await provider.getEntityRelationships(repoId, 'a');\n expect(result.items).toHaveLength(2);\n });\n\n it('filters relationships by direction', async () => {\n await provider.createRelationship(repoId, makeRelationship('r1', 'connects', 'a', 'b'));\n await provider.createRelationship(repoId, makeRelationship('r2', 'connects', 'c', 'a'));\n\n const outbound = await provider.getEntityRelationships(repoId, 'a', { direction: 'outbound' });\n expect(outbound.items).toHaveLength(1);\n expect(outbound.items[0]!.targetEntityId).toBe('b');\n\n const inbound = await provider.getEntityRelationships(repoId, 'a', { direction: 'inbound' });\n expect(inbound.items).toHaveLength(1);\n expect(inbound.items[0]!.sourceEntityId).toBe('c');\n });\n\n it('deletes a relationship', async () => {\n await provider.createRelationship(repoId, makeRelationship('r1', 'connects', 'a', 'b'));\n await provider.deleteRelationship(repoId, 'r1');\n expect(await provider.getRelationship(repoId, 'r1')).toBeNull();\n });\n });\n\n // ─── Graph Traversal ────────────────────────────────────\n\n describe('graph traversal', () => {\n beforeEach(async () => {\n await provider.createEntity(repoId, makeEntity('a', 'node', 'A'));\n await provider.createEntity(repoId, makeEntity('b', 'node', 'B'));\n await provider.createEntity(repoId, makeEntity('c', 'node', 'C'));\n await provider.createRelationship(repoId, makeRelationship('r1', 'links', 'a', 'b'));\n await provider.createRelationship(repoId, makeRelationship('r2', 'links', 'b', 'c'));\n });\n\n it('explores neighborhood at depth 1', async () => {\n const result = await provider.exploreNeighborhood(repoId, 'a', {\n depth: 1,\n direction: 'both',\n limitPerType: 10,\n offsetPerType: 0,\n });\n expect(result.centerId).toBe('a');\n expect(result.layers).toHaveLength(1);\n });\n\n it('finds paths between connected entities', async () => {\n const result = await provider.findPaths(repoId, 'a', 'c', {\n maxDepth: 3,\n limit: 5,\n offset: 0,\n });\n expect(result.paths.length).toBeGreaterThanOrEqual(1);\n const firstPath = result.paths[0]!;\n expect(firstPath.entityIds[0]).toBe('a');\n expect(firstPath.entityIds[firstPath.entityIds.length - 1]).toBe('c');\n });\n\n it('returns empty paths when no connection', async () => {\n await provider.createEntity(repoId, makeEntity('isolated', 'node', 'Isolated'));\n const result = await provider.findPaths(repoId, 'a', 'isolated', {\n maxDepth: 3,\n limit: 5,\n offset: 0,\n });\n expect(result.paths).toHaveLength(0);\n });\n\n it('finds paths through non-bidirectional inbound edges', async () => {\n // Graph: a → b ← d (both edges are non-bidirectional)\n // Path from a to d should traverse: a →(outbound) b ←(inbound) d\n await provider.createEntity(repoId, makeEntity('d', 'node', 'D'));\n await provider.createRelationship(repoId, makeRelationship('r3', 'links', 'd', 'b'));\n const result = await provider.findPaths(repoId, 'a', 'd', {\n maxDepth: 3,\n limit: 5,\n offset: 0,\n });\n expect(result.paths.length).toBeGreaterThanOrEqual(1);\n const firstPath = result.paths[0]!;\n expect(firstPath.entityIds[0]).toBe('a');\n expect(firstPath.entityIds[firstPath.entityIds.length - 1]).toBe('d');\n });\n });\n\n // ─── Timeline ───────────────────────────────────────────\n\n describe('timeline', () => {\n it('returns timeline events', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n const result = await provider.getTimeline(repoId, 'e1', {\n limit: 10,\n offset: 0,\n });\n expect(result.events.length).toBeGreaterThanOrEqual(1);\n });\n });\n\n // ─── Bulk Operations ────────────────────────────────────\n\n describe('bulk operations', () => {\n it('exports data', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n\n const chunks = [];\n for await (const chunk of provider.exportAll(repoId)) {\n chunks.push(chunk);\n }\n expect(chunks.length).toBeGreaterThanOrEqual(1);\n });\n\n it('imports data', async () => {\n const result = await provider.importBulk(repoId, [\n { entities: [makeEntity('imported-1'), makeEntity('imported-2')] },\n { relationships: [makeRelationship('ir1', 'links', 'imported-1', 'imported-2')] },\n ]);\n expect(result.entitiesImported).toBe(2);\n expect(result.relationshipsImported).toBe(1);\n\n // Verify imported data is accessible\n const e = await provider.getEntity(repoId, 'imported-1');\n expect(e).not.toBeNull();\n });\n });\n\n // ─── Delete All Contents ───────────────────────────────\n\n describe('deleteAllContents', () => {\n it('deletes all entities and relationships but preserves the repository', async () => {\n await provider.createEntity(repoId, makeEntity('e1', 'alpha'));\n await provider.createEntity(repoId, makeEntity('e2', 'beta'));\n await provider.createRelationship(repoId, makeRelationship('r1', 'links', 'e1', 'e2'));\n\n const result = await provider.deleteAllContents(repoId);\n expect(result.deletedEntities).toBe(2);\n expect(result.deletedRelationships).toBe(1);\n\n // Repository still exists\n const repo = await provider.getRepository(repoId);\n expect(repo).not.toBeNull();\n\n // Vocabulary still exists\n const vocab = await provider.getVocabulary(repoId);\n expect(vocab).toBeDefined();\n\n // Contents are gone\n const stats = await provider.getRepositoryStats(repoId);\n expect(stats.entityCount).toBe(0);\n expect(stats.relationshipCount).toBe(0);\n });\n\n it('returns zero counts on an empty repository', async () => {\n const result = await provider.deleteAllContents(repoId);\n expect(result.deletedEntities).toBe(0);\n expect(result.deletedRelationships).toBe(0);\n });\n });\n\n // ─── Stats after data ───────────────────────────────────\n\n describe('stats reflect data', () => {\n it('counts entities and relationships', async () => {\n await provider.createEntity(repoId, makeEntity('e1', 'alpha'));\n await provider.createEntity(repoId, makeEntity('e2', 'alpha'));\n await provider.createEntity(repoId, makeEntity('e3', 'beta'));\n await provider.createRelationship(repoId, makeRelationship('r1', 'links', 'e1', 'e2'));\n\n const stats = await provider.getRepositoryStats(repoId);\n expect(stats.entityCount).toBe(3);\n expect(stats.relationshipCount).toBe(1);\n expect(stats.entityTypeBreakdown['alpha']).toBe(2);\n expect(stats.entityTypeBreakdown['beta']).toBe(1);\n expect(stats.relationshipTypeBreakdown['links']).toBe(1);\n });\n });\n });\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/providers-builtin/conformance.ts"],"sourcesContent":["// Provider Conformance Test Suite\n// Any StorageProvider implementer can import and run these tests to verify conformance.\n//\n// Usage:\n// import { runStorageProviderConformanceTests } from '@utaba/deep-memory';\n// runStorageProviderConformanceTests(() => new MyStorageProvider());\n\nimport { describe, it, expect, beforeEach } from 'vitest';\nimport type { StorageProvider } from '../providers/StorageProvider.js';\nimport type { StoredEntity } from '../types/entities.js';\nimport type { StoredRelationship } from '../types/relationships.js';\nimport type { Provenance } from '../types/provenance.js';\n\nfunction makeProvenance(): Provenance {\n const now = new Date().toISOString();\n return {\n createdBy: 'conformance-test',\n createdByType: 'agent',\n createdAt: now,\n modifiedBy: 'conformance-test',\n modifiedByType: 'agent',\n modifiedAt: now,\n };\n}\n\nfunction makeEntity(id: string, type = 'test-type', label?: string): StoredEntity {\n return {\n id,\n slug: `${type}:${(label ?? id).toLowerCase().replace(/[^a-z0-9]+/g, '-')}`,\n entityType: type,\n label: label ?? id,\n summary: `Summary for ${id}`,\n properties: { key: 'value' },\n provenance: makeProvenance(),\n };\n}\n\nfunction makeRelationship(\n id: string,\n type: string,\n sourceId: string,\n targetId: string,\n bidirectional = false,\n): StoredRelationship {\n return {\n id,\n relationshipType: type,\n sourceEntityId: sourceId,\n targetEntityId: targetId,\n properties: {},\n bidirectional,\n provenance: makeProvenance(),\n };\n}\n\n/**\n * Run the full StorageProvider conformance test suite.\n *\n * @param factory - A function that creates a fresh, empty StorageProvider instance.\n * Called before each test to ensure isolation.\n */\nexport function runStorageProviderConformanceTests(\n factory: () => StorageProvider | Promise<StorageProvider>,\n): void {\n // Use a stable GUID so external cleanup scripts can target it\n const repoId = '40000000-0000-4000-a000-000000000001';\n\n let provider: StorageProvider;\n\n async function setup(): Promise<void> {\n provider = await factory();\n if (provider.initialize) await provider.initialize();\n\n await provider.createRepository({\n repositoryId: repoId,\n label: 'Conformance Test',\n governanceConfig: { mode: 'open' },\n createdAt: new Date().toISOString(),\n createdBy: 'conformance-test',\n });\n }\n\n describe('StorageProvider Conformance Tests', () => {\n beforeEach(async () => {\n await setup();\n });\n\n // ─── Repository ─────────────────────────────────────────\n\n describe('repository operations', () => {\n it('creates a repository', async () => {\n const repo = await provider.getRepository(repoId);\n expect(repo).not.toBeNull();\n expect(repo!.repositoryId).toBe(repoId);\n expect(repo!.label).toBe('Conformance Test');\n });\n\n it('returns null for non-existent repository', async () => {\n const repo = await provider.getRepository('ffffffff-ffff-4fff-afff-ffffffffffff');\n expect(repo).toBeNull();\n });\n\n it('lists repositories', async () => {\n const list = await provider.listRepositories();\n expect(list.items.length).toBeGreaterThanOrEqual(1);\n expect(list.items.some((r) => r.repositoryId === repoId)).toBe(true);\n });\n\n it('updates a repository', async () => {\n const updated = await provider.updateRepository(repoId, {\n label: 'Updated Label',\n description: 'Updated description',\n governanceConfig: { mode: 'open', defaultSimilarityThreshold: 0.4 },\n });\n expect(updated.label).toBe('Updated Label');\n expect(updated.description).toBe('Updated description');\n expect(updated.governanceConfig.defaultSimilarityThreshold).toBe(0.4);\n\n // Verify persistence\n const fetched = await provider.getRepository(repoId);\n expect(fetched!.label).toBe('Updated Label');\n expect(fetched!.governanceConfig.defaultSimilarityThreshold).toBe(0.4);\n });\n\n it('deletes a repository', async () => {\n await provider.deleteRepository(repoId);\n expect(await provider.getRepository(repoId)).toBeNull();\n });\n\n it('returns repository stats', async () => {\n const stats = await provider.getRepositoryStats(repoId);\n expect(stats.entityCount).toBe(0);\n expect(stats.relationshipCount).toBe(0);\n expect(typeof stats.vocabularyVersion).toBe('string');\n });\n });\n\n // ─── Vocabulary ─────────────────────────────────────────\n\n describe('vocabulary operations', () => {\n it('gets and saves vocabulary', async () => {\n const vocab = await provider.getVocabulary(repoId);\n expect(vocab).toBeDefined();\n expect(typeof vocab.version).toBe('string');\n\n const updated = { ...vocab, version: '1.0.0' };\n await provider.saveVocabulary(repoId, updated);\n\n const fetched = await provider.getVocabulary(repoId);\n expect(fetched.version).toBe('1.0.0');\n });\n\n it('returns vocabulary change log', async () => {\n const log = await provider.getVocabularyChangeLog(repoId);\n expect(Array.isArray(log.items)).toBe(true);\n });\n });\n\n // ─── Entities ───────────────────────────────────────────\n\n describe('entity operations', () => {\n it('creates and retrieves an entity', async () => {\n const entity = makeEntity('e1');\n await provider.createEntity(repoId, entity);\n\n const retrieved = await provider.getEntity(repoId, 'e1');\n expect(retrieved).not.toBeNull();\n expect(retrieved!.id).toBe('e1');\n expect(retrieved!.label).toBe('e1');\n });\n\n it('retrieves an entity by slug', async () => {\n const entity = makeEntity('e1', 'test-type', 'Alpha');\n await provider.createEntity(repoId, entity);\n\n const retrieved = await provider.getEntityBySlug(repoId, entity.slug);\n expect(retrieved).not.toBeNull();\n expect(retrieved!.id).toBe('e1');\n expect(retrieved!.slug).toBe(entity.slug);\n });\n\n it('returns null for non-existent entity', async () => {\n const result = await provider.getEntity(repoId, 'nonexistent');\n expect(result).toBeNull();\n });\n\n it('returns null for non-existent slug', async () => {\n const result = await provider.getEntityBySlug(repoId, 'nonexistent:slug');\n expect(result).toBeNull();\n });\n\n it('batch retrieves entities', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n await provider.createEntity(repoId, makeEntity('e2'));\n\n const map = await provider.getEntities(repoId, ['e1', 'e2', 'missing']);\n expect(map.size).toBe(2);\n expect(map.has('e1')).toBe(true);\n expect(map.has('e2')).toBe(true);\n expect(map.has('missing')).toBe(false);\n });\n\n it('updates an entity', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n const updated = await provider.updateEntity(repoId, 'e1', {\n label: 'Updated Label',\n provenance: makeProvenance(),\n });\n expect(updated.label).toBe('Updated Label');\n\n const fetched = await provider.getEntity(repoId, 'e1');\n expect(fetched!.label).toBe('Updated Label');\n });\n\n it('clears summary/data/dataFormat when null is passed', async () => {\n const entity: StoredEntity = {\n ...makeEntity('e1'),\n summary: 'starting summary',\n data: 'raw content',\n dataFormat: 'text/plain',\n };\n await provider.createEntity(repoId, entity);\n\n await provider.updateEntity(repoId, 'e1', {\n summary: null,\n data: null,\n dataFormat: null,\n provenance: makeProvenance(),\n });\n\n const fetched = await provider.getEntity(repoId, 'e1');\n expect(fetched!.summary).toBeUndefined();\n expect(fetched!.data).toBeUndefined();\n expect(fetched!.dataFormat).toBeUndefined();\n });\n\n it('preserves summary/data/dataFormat when undefined is passed', async () => {\n const entity: StoredEntity = {\n ...makeEntity('e1'),\n summary: 'keep me',\n data: 'keep me too',\n dataFormat: 'text/plain',\n };\n await provider.createEntity(repoId, entity);\n\n await provider.updateEntity(repoId, 'e1', {\n label: 'Renamed',\n provenance: makeProvenance(),\n });\n\n const fetched = await provider.getEntity(repoId, 'e1');\n expect(fetched!.summary).toBe('keep me');\n expect(fetched!.data).toBe('keep me too');\n expect(fetched!.dataFormat).toBe('text/plain');\n });\n\n it('deletes an entity', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n await provider.deleteEntity(repoId, 'e1');\n expect(await provider.getEntity(repoId, 'e1')).toBeNull();\n });\n\n it('finds entities by search term', async () => {\n await provider.createEntity(repoId, makeEntity('e1', 'test-type', 'Alpha'));\n await provider.createEntity(repoId, makeEntity('e2', 'test-type', 'Beta'));\n\n const result = await provider.findEntities(repoId, {\n searchTerm: 'alpha',\n limit: 10,\n offset: 0,\n });\n expect(result.items).toHaveLength(1);\n expect(result.items[0]!.label).toBe('Alpha');\n });\n\n it('finds entities by search term case-insensitively', async () => {\n // Locks the invariant that searchTerm matching is case-insensitive on\n // every provider. In-memory lowercases both sides; SQL Server's LIKE\n // is case-insensitive under the default *_CI_AS collation; Cosmos\n // routes through CONTAINS(..., @term, true) on the Document endpoint.\n await provider.createEntity(repoId, makeEntity('e1', 'test-type', 'Alpha'));\n\n const result = await provider.findEntities(repoId, {\n searchTerm: 'ALPHA',\n limit: 10,\n offset: 0,\n });\n expect(result.items).toHaveLength(1);\n expect(result.items[0]!.label).toBe('Alpha');\n });\n\n it('finds entities by type filter', async () => {\n await provider.createEntity(repoId, makeEntity('e1', 'type-a', 'A'));\n await provider.createEntity(repoId, makeEntity('e2', 'type-b', 'B'));\n\n const result = await provider.findEntities(repoId, {\n entityTypes: ['type-a'],\n limit: 10,\n offset: 0,\n });\n expect(result.items).toHaveLength(1);\n expect(result.items[0]!.entityType).toBe('type-a');\n });\n\n it('paginates find results', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n await provider.createEntity(repoId, makeEntity('e2'));\n await provider.createEntity(repoId, makeEntity('e3'));\n\n const page1 = await provider.findEntities(repoId, { limit: 2, offset: 0 });\n expect(page1.items).toHaveLength(2);\n expect(page1.hasMore).toBe(true);\n\n const page2 = await provider.findEntities(repoId, { limit: 2, offset: 2 });\n expect(page2.items).toHaveLength(1);\n expect(page2.hasMore).toBe(false);\n });\n });\n\n // ─── Relationships ──────────────────────────────────────\n\n describe('relationship operations', () => {\n beforeEach(async () => {\n await provider.createEntity(repoId, makeEntity('a'));\n await provider.createEntity(repoId, makeEntity('b'));\n await provider.createEntity(repoId, makeEntity('c'));\n });\n\n it('creates and retrieves a relationship', async () => {\n const rel = makeRelationship('r1', 'connects', 'a', 'b');\n await provider.createRelationship(repoId, rel);\n\n const retrieved = await provider.getRelationship(repoId, 'r1');\n expect(retrieved).not.toBeNull();\n expect(retrieved!.sourceEntityId).toBe('a');\n expect(retrieved!.targetEntityId).toBe('b');\n });\n\n it('returns null for non-existent relationship', async () => {\n expect(await provider.getRelationship(repoId, 'nonexistent')).toBeNull();\n });\n\n it('gets entity relationships', async () => {\n await provider.createRelationship(repoId, makeRelationship('r1', 'connects', 'a', 'b'));\n await provider.createRelationship(repoId, makeRelationship('r2', 'connects', 'c', 'a'));\n\n const result = await provider.getEntityRelationships(repoId, 'a');\n expect(result.items).toHaveLength(2);\n });\n\n it('filters relationships by direction', async () => {\n await provider.createRelationship(repoId, makeRelationship('r1', 'connects', 'a', 'b'));\n await provider.createRelationship(repoId, makeRelationship('r2', 'connects', 'c', 'a'));\n\n const outbound = await provider.getEntityRelationships(repoId, 'a', { direction: 'out' });\n expect(outbound.items).toHaveLength(1);\n expect(outbound.items[0]!.targetEntityId).toBe('b');\n\n const inbound = await provider.getEntityRelationships(repoId, 'a', { direction: 'in' });\n expect(inbound.items).toHaveLength(1);\n expect(inbound.items[0]!.sourceEntityId).toBe('c');\n });\n\n it('deletes a relationship', async () => {\n await provider.createRelationship(repoId, makeRelationship('r1', 'connects', 'a', 'b'));\n await provider.deleteRelationship(repoId, 'r1');\n expect(await provider.getRelationship(repoId, 'r1')).toBeNull();\n });\n });\n\n // ─── Graph Traversal ────────────────────────────────────\n\n describe('graph traversal', () => {\n beforeEach(async () => {\n await provider.createEntity(repoId, makeEntity('a', 'node', 'A'));\n await provider.createEntity(repoId, makeEntity('b', 'node', 'B'));\n await provider.createEntity(repoId, makeEntity('c', 'node', 'C'));\n await provider.createRelationship(repoId, makeRelationship('r1', 'links', 'a', 'b'));\n await provider.createRelationship(repoId, makeRelationship('r2', 'links', 'b', 'c'));\n });\n\n it('explores neighborhood at depth 1', async () => {\n const result = await provider.exploreNeighborhood(repoId, 'a', {\n depth: 1,\n direction: 'both',\n limitPerType: 10,\n offsetPerType: 0,\n });\n expect(result.centerId).toBe('a');\n expect(result.layers).toHaveLength(1);\n });\n\n it('dedupes connected entities per (layer, relationship-type) bucket', async () => {\n // Two same-type edges connect the same pair (b ↔ c), modelling a\n // logically-symmetric relationship as two directed half-edges. The\n // bucket should still report c exactly once at depth 2 from a.\n await provider.createRelationship(repoId, makeRelationship('r3', 'links', 'c', 'b'));\n const result = await provider.exploreNeighborhood(repoId, 'a', {\n depth: 2,\n direction: 'both',\n limitPerType: 10,\n offsetPerType: 0,\n });\n const linksLayer2 = result.layers[1]?.['links'];\n expect(linksLayer2).toBeDefined();\n expect(linksLayer2!.total).toBe(1);\n expect(linksLayer2!.entities.map((e) => e.id)).toEqual(['c']);\n });\n\n it('finds paths between connected entities', async () => {\n const result = await provider.findPaths(repoId, 'a', 'c', {\n maxDepth: 3,\n limit: 5,\n offset: 0,\n });\n expect(result.paths.length).toBeGreaterThanOrEqual(1);\n const firstPath = result.paths[0]!;\n expect(firstPath.entityIds[0]).toBe('a');\n expect(firstPath.entityIds[firstPath.entityIds.length - 1]).toBe('c');\n });\n\n it('returns empty paths when no connection', async () => {\n await provider.createEntity(repoId, makeEntity('isolated', 'node', 'Isolated'));\n const result = await provider.findPaths(repoId, 'a', 'isolated', {\n maxDepth: 3,\n limit: 5,\n offset: 0,\n });\n expect(result.paths).toHaveLength(0);\n });\n\n it('returns only simple paths (no repeated vertices)', async () => {\n // Set up a graph where a non-simple walk could otherwise emerge:\n // a — b — c (the target)\n // c — d via two distinct edges\n // A walk of length 4 along (a)-(b)-(c)-(d)-(c) reuses neither edge\n // but visits c twice. The contract requires findPaths to drop walks\n // that visit any vertex more than once.\n await provider.createEntity(repoId, makeEntity('d', 'node', 'D'));\n await provider.createRelationship(repoId, makeRelationship('r3', 'links', 'c', 'd'));\n await provider.createRelationship(repoId, makeRelationship('r4', 'links', 'c', 'd'));\n const result = await provider.findPaths(repoId, 'a', 'c', {\n maxDepth: 4,\n limit: 50,\n offset: 0,\n });\n for (const path of result.paths) {\n expect(new Set(path.entityIds).size).toBe(path.entityIds.length);\n expect(path.entityIds[path.entityIds.length - 1]).toBe('c');\n }\n // Exactly one simple path from a to c at depth ≤ 4: a-b-c.\n expect(result.totalPaths).toBe(1);\n });\n\n it('finds paths through non-bidirectional inbound edges', async () => {\n // Graph: a → b ← d (both edges are non-bidirectional)\n // Path from a to d should traverse: a →(outbound) b ←(inbound) d\n await provider.createEntity(repoId, makeEntity('d', 'node', 'D'));\n await provider.createRelationship(repoId, makeRelationship('r3', 'links', 'd', 'b'));\n const result = await provider.findPaths(repoId, 'a', 'd', {\n maxDepth: 3,\n limit: 5,\n offset: 0,\n });\n expect(result.paths.length).toBeGreaterThanOrEqual(1);\n const firstPath = result.paths[0]!;\n expect(firstPath.entityIds[0]).toBe('a');\n expect(firstPath.entityIds[firstPath.entityIds.length - 1]).toBe('d');\n });\n });\n\n // ─── Timeline ───────────────────────────────────────────\n\n describe('timeline', () => {\n it('returns timeline events', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n const result = await provider.getTimeline(repoId, 'e1', {\n limit: 10,\n offset: 0,\n });\n expect(result.events.length).toBeGreaterThanOrEqual(1);\n });\n });\n\n // ─── Bulk Operations ────────────────────────────────────\n\n describe('bulk operations', () => {\n it('exports data', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n\n const chunks = [];\n for await (const chunk of provider.exportAll(repoId)) {\n chunks.push(chunk);\n }\n expect(chunks.length).toBeGreaterThanOrEqual(1);\n });\n\n it('imports data', async () => {\n const result = await provider.importBulk(repoId, [\n { entities: [makeEntity('imported-1'), makeEntity('imported-2')] },\n { relationships: [makeRelationship('ir1', 'links', 'imported-1', 'imported-2')] },\n ]);\n expect(result.entitiesImported).toBe(2);\n expect(result.relationshipsImported).toBe(1);\n\n // Verify imported data is accessible\n const e = await provider.getEntity(repoId, 'imported-1');\n expect(e).not.toBeNull();\n });\n });\n\n // ─── Delete All Contents ───────────────────────────────\n\n describe('deleteAllContents', () => {\n it('deletes all entities and relationships but preserves the repository', async () => {\n await provider.createEntity(repoId, makeEntity('e1', 'alpha'));\n await provider.createEntity(repoId, makeEntity('e2', 'beta'));\n await provider.createRelationship(repoId, makeRelationship('r1', 'links', 'e1', 'e2'));\n\n const result = await provider.deleteAllContents(repoId);\n expect(result.deletedEntities).toBe(2);\n expect(result.deletedRelationships).toBe(1);\n\n // Repository still exists\n const repo = await provider.getRepository(repoId);\n expect(repo).not.toBeNull();\n\n // Vocabulary still exists\n const vocab = await provider.getVocabulary(repoId);\n expect(vocab).toBeDefined();\n\n // Contents are gone\n const stats = await provider.getRepositoryStats(repoId);\n expect(stats.entityCount).toBe(0);\n expect(stats.relationshipCount).toBe(0);\n });\n\n it('returns zero counts on an empty repository', async () => {\n const result = await provider.deleteAllContents(repoId);\n expect(result.deletedEntities).toBe(0);\n expect(result.deletedRelationships).toBe(0);\n });\n });\n\n // ─── Stats after data ───────────────────────────────────\n\n describe('stats reflect data', () => {\n it('counts entities and relationships', async () => {\n await provider.createEntity(repoId, makeEntity('e1', 'alpha'));\n await provider.createEntity(repoId, makeEntity('e2', 'alpha'));\n await provider.createEntity(repoId, makeEntity('e3', 'beta'));\n await provider.createRelationship(repoId, makeRelationship('r1', 'links', 'e1', 'e2'));\n\n const stats = await provider.getRepositoryStats(repoId);\n expect(stats.entityCount).toBe(3);\n expect(stats.relationshipCount).toBe(1);\n expect(stats.entityTypeBreakdown['alpha']).toBe(2);\n expect(stats.entityTypeBreakdown['beta']).toBe(1);\n expect(stats.relationshipTypeBreakdown['links']).toBe(1);\n });\n });\n });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as StorageProvider } from '../StorageProvider-
|
|
2
|
-
import '../portability-
|
|
1
|
+
import { S as StorageProvider } from '../StorageProvider-RPhT7vtH.cjs';
|
|
2
|
+
import '../portability-BHyXmv5-.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Run the full StorageProvider conformance test suite.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as StorageProvider } from '../StorageProvider-
|
|
2
|
-
import '../portability-
|
|
1
|
+
import { S as StorageProvider } from '../StorageProvider-CgWRfvIZ.js';
|
|
2
|
+
import '../portability-BHyXmv5-.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Run the full StorageProvider conformance test suite.
|
|
@@ -261,10 +261,10 @@ function runStorageProviderConformanceTests(factory) {
|
|
|
261
261
|
it("filters relationships by direction", async () => {
|
|
262
262
|
await provider.createRelationship(repoId, makeRelationship("r1", "connects", "a", "b"));
|
|
263
263
|
await provider.createRelationship(repoId, makeRelationship("r2", "connects", "c", "a"));
|
|
264
|
-
const outbound = await provider.getEntityRelationships(repoId, "a", { direction: "
|
|
264
|
+
const outbound = await provider.getEntityRelationships(repoId, "a", { direction: "out" });
|
|
265
265
|
expect(outbound.items).toHaveLength(1);
|
|
266
266
|
expect(outbound.items[0].targetEntityId).toBe("b");
|
|
267
|
-
const inbound = await provider.getEntityRelationships(repoId, "a", { direction: "
|
|
267
|
+
const inbound = await provider.getEntityRelationships(repoId, "a", { direction: "in" });
|
|
268
268
|
expect(inbound.items).toHaveLength(1);
|
|
269
269
|
expect(inbound.items[0].sourceEntityId).toBe("c");
|
|
270
270
|
});
|
|
@@ -292,6 +292,19 @@ function runStorageProviderConformanceTests(factory) {
|
|
|
292
292
|
expect(result.centerId).toBe("a");
|
|
293
293
|
expect(result.layers).toHaveLength(1);
|
|
294
294
|
});
|
|
295
|
+
it("dedupes connected entities per (layer, relationship-type) bucket", async () => {
|
|
296
|
+
await provider.createRelationship(repoId, makeRelationship("r3", "links", "c", "b"));
|
|
297
|
+
const result = await provider.exploreNeighborhood(repoId, "a", {
|
|
298
|
+
depth: 2,
|
|
299
|
+
direction: "both",
|
|
300
|
+
limitPerType: 10,
|
|
301
|
+
offsetPerType: 0
|
|
302
|
+
});
|
|
303
|
+
const linksLayer2 = result.layers[1]?.["links"];
|
|
304
|
+
expect(linksLayer2).toBeDefined();
|
|
305
|
+
expect(linksLayer2.total).toBe(1);
|
|
306
|
+
expect(linksLayer2.entities.map((e) => e.id)).toEqual(["c"]);
|
|
307
|
+
});
|
|
295
308
|
it("finds paths between connected entities", async () => {
|
|
296
309
|
const result = await provider.findPaths(repoId, "a", "c", {
|
|
297
310
|
maxDepth: 3,
|
|
@@ -312,6 +325,21 @@ function runStorageProviderConformanceTests(factory) {
|
|
|
312
325
|
});
|
|
313
326
|
expect(result.paths).toHaveLength(0);
|
|
314
327
|
});
|
|
328
|
+
it("returns only simple paths (no repeated vertices)", async () => {
|
|
329
|
+
await provider.createEntity(repoId, makeEntity("d", "node", "D"));
|
|
330
|
+
await provider.createRelationship(repoId, makeRelationship("r3", "links", "c", "d"));
|
|
331
|
+
await provider.createRelationship(repoId, makeRelationship("r4", "links", "c", "d"));
|
|
332
|
+
const result = await provider.findPaths(repoId, "a", "c", {
|
|
333
|
+
maxDepth: 4,
|
|
334
|
+
limit: 50,
|
|
335
|
+
offset: 0
|
|
336
|
+
});
|
|
337
|
+
for (const path of result.paths) {
|
|
338
|
+
expect(new Set(path.entityIds).size).toBe(path.entityIds.length);
|
|
339
|
+
expect(path.entityIds[path.entityIds.length - 1]).toBe("c");
|
|
340
|
+
}
|
|
341
|
+
expect(result.totalPaths).toBe(1);
|
|
342
|
+
});
|
|
315
343
|
it("finds paths through non-bidirectional inbound edges", async () => {
|
|
316
344
|
await provider.createEntity(repoId, makeEntity("d", "node", "D"));
|
|
317
345
|
await provider.createRelationship(repoId, makeRelationship("r3", "links", "d", "b"));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/providers-builtin/conformance.ts"],"sourcesContent":["// Provider Conformance Test Suite\n// Any StorageProvider implementer can import and run these tests to verify conformance.\n//\n// Usage:\n// import { runStorageProviderConformanceTests } from '@utaba/deep-memory';\n// runStorageProviderConformanceTests(() => new MyStorageProvider());\n\nimport { describe, it, expect, beforeEach } from 'vitest';\nimport type { StorageProvider } from '../providers/StorageProvider.js';\nimport type { StoredEntity } from '../types/entities.js';\nimport type { StoredRelationship } from '../types/relationships.js';\nimport type { Provenance } from '../types/provenance.js';\n\nfunction makeProvenance(): Provenance {\n const now = new Date().toISOString();\n return {\n createdBy: 'conformance-test',\n createdByType: 'agent',\n createdAt: now,\n modifiedBy: 'conformance-test',\n modifiedByType: 'agent',\n modifiedAt: now,\n };\n}\n\nfunction makeEntity(id: string, type = 'test-type', label?: string): StoredEntity {\n return {\n id,\n slug: `${type}:${(label ?? id).toLowerCase().replace(/[^a-z0-9]+/g, '-')}`,\n entityType: type,\n label: label ?? id,\n summary: `Summary for ${id}`,\n properties: { key: 'value' },\n provenance: makeProvenance(),\n };\n}\n\nfunction makeRelationship(\n id: string,\n type: string,\n sourceId: string,\n targetId: string,\n bidirectional = false,\n): StoredRelationship {\n return {\n id,\n relationshipType: type,\n sourceEntityId: sourceId,\n targetEntityId: targetId,\n properties: {},\n bidirectional,\n provenance: makeProvenance(),\n };\n}\n\n/**\n * Run the full StorageProvider conformance test suite.\n *\n * @param factory - A function that creates a fresh, empty StorageProvider instance.\n * Called before each test to ensure isolation.\n */\nexport function runStorageProviderConformanceTests(\n factory: () => StorageProvider | Promise<StorageProvider>,\n): void {\n // Use a stable GUID so external cleanup scripts can target it\n const repoId = '40000000-0000-4000-a000-000000000001';\n\n let provider: StorageProvider;\n\n async function setup(): Promise<void> {\n provider = await factory();\n if (provider.initialize) await provider.initialize();\n\n await provider.createRepository({\n repositoryId: repoId,\n label: 'Conformance Test',\n governanceConfig: { mode: 'open' },\n createdAt: new Date().toISOString(),\n createdBy: 'conformance-test',\n });\n }\n\n describe('StorageProvider Conformance Tests', () => {\n beforeEach(async () => {\n await setup();\n });\n\n // ─── Repository ─────────────────────────────────────────\n\n describe('repository operations', () => {\n it('creates a repository', async () => {\n const repo = await provider.getRepository(repoId);\n expect(repo).not.toBeNull();\n expect(repo!.repositoryId).toBe(repoId);\n expect(repo!.label).toBe('Conformance Test');\n });\n\n it('returns null for non-existent repository', async () => {\n const repo = await provider.getRepository('ffffffff-ffff-4fff-afff-ffffffffffff');\n expect(repo).toBeNull();\n });\n\n it('lists repositories', async () => {\n const list = await provider.listRepositories();\n expect(list.items.length).toBeGreaterThanOrEqual(1);\n expect(list.items.some((r) => r.repositoryId === repoId)).toBe(true);\n });\n\n it('updates a repository', async () => {\n const updated = await provider.updateRepository(repoId, {\n label: 'Updated Label',\n description: 'Updated description',\n governanceConfig: { mode: 'open', defaultSimilarityThreshold: 0.4 },\n });\n expect(updated.label).toBe('Updated Label');\n expect(updated.description).toBe('Updated description');\n expect(updated.governanceConfig.defaultSimilarityThreshold).toBe(0.4);\n\n // Verify persistence\n const fetched = await provider.getRepository(repoId);\n expect(fetched!.label).toBe('Updated Label');\n expect(fetched!.governanceConfig.defaultSimilarityThreshold).toBe(0.4);\n });\n\n it('deletes a repository', async () => {\n await provider.deleteRepository(repoId);\n expect(await provider.getRepository(repoId)).toBeNull();\n });\n\n it('returns repository stats', async () => {\n const stats = await provider.getRepositoryStats(repoId);\n expect(stats.entityCount).toBe(0);\n expect(stats.relationshipCount).toBe(0);\n expect(typeof stats.vocabularyVersion).toBe('string');\n });\n });\n\n // ─── Vocabulary ─────────────────────────────────────────\n\n describe('vocabulary operations', () => {\n it('gets and saves vocabulary', async () => {\n const vocab = await provider.getVocabulary(repoId);\n expect(vocab).toBeDefined();\n expect(typeof vocab.version).toBe('string');\n\n const updated = { ...vocab, version: '1.0.0' };\n await provider.saveVocabulary(repoId, updated);\n\n const fetched = await provider.getVocabulary(repoId);\n expect(fetched.version).toBe('1.0.0');\n });\n\n it('returns vocabulary change log', async () => {\n const log = await provider.getVocabularyChangeLog(repoId);\n expect(Array.isArray(log.items)).toBe(true);\n });\n });\n\n // ─── Entities ───────────────────────────────────────────\n\n describe('entity operations', () => {\n it('creates and retrieves an entity', async () => {\n const entity = makeEntity('e1');\n await provider.createEntity(repoId, entity);\n\n const retrieved = await provider.getEntity(repoId, 'e1');\n expect(retrieved).not.toBeNull();\n expect(retrieved!.id).toBe('e1');\n expect(retrieved!.label).toBe('e1');\n });\n\n it('retrieves an entity by slug', async () => {\n const entity = makeEntity('e1', 'test-type', 'Alpha');\n await provider.createEntity(repoId, entity);\n\n const retrieved = await provider.getEntityBySlug(repoId, entity.slug);\n expect(retrieved).not.toBeNull();\n expect(retrieved!.id).toBe('e1');\n expect(retrieved!.slug).toBe(entity.slug);\n });\n\n it('returns null for non-existent entity', async () => {\n const result = await provider.getEntity(repoId, 'nonexistent');\n expect(result).toBeNull();\n });\n\n it('returns null for non-existent slug', async () => {\n const result = await provider.getEntityBySlug(repoId, 'nonexistent:slug');\n expect(result).toBeNull();\n });\n\n it('batch retrieves entities', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n await provider.createEntity(repoId, makeEntity('e2'));\n\n const map = await provider.getEntities(repoId, ['e1', 'e2', 'missing']);\n expect(map.size).toBe(2);\n expect(map.has('e1')).toBe(true);\n expect(map.has('e2')).toBe(true);\n expect(map.has('missing')).toBe(false);\n });\n\n it('updates an entity', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n const updated = await provider.updateEntity(repoId, 'e1', {\n label: 'Updated Label',\n provenance: makeProvenance(),\n });\n expect(updated.label).toBe('Updated Label');\n\n const fetched = await provider.getEntity(repoId, 'e1');\n expect(fetched!.label).toBe('Updated Label');\n });\n\n it('clears summary/data/dataFormat when null is passed', async () => {\n const entity: StoredEntity = {\n ...makeEntity('e1'),\n summary: 'starting summary',\n data: 'raw content',\n dataFormat: 'text/plain',\n };\n await provider.createEntity(repoId, entity);\n\n await provider.updateEntity(repoId, 'e1', {\n summary: null,\n data: null,\n dataFormat: null,\n provenance: makeProvenance(),\n });\n\n const fetched = await provider.getEntity(repoId, 'e1');\n expect(fetched!.summary).toBeUndefined();\n expect(fetched!.data).toBeUndefined();\n expect(fetched!.dataFormat).toBeUndefined();\n });\n\n it('preserves summary/data/dataFormat when undefined is passed', async () => {\n const entity: StoredEntity = {\n ...makeEntity('e1'),\n summary: 'keep me',\n data: 'keep me too',\n dataFormat: 'text/plain',\n };\n await provider.createEntity(repoId, entity);\n\n await provider.updateEntity(repoId, 'e1', {\n label: 'Renamed',\n provenance: makeProvenance(),\n });\n\n const fetched = await provider.getEntity(repoId, 'e1');\n expect(fetched!.summary).toBe('keep me');\n expect(fetched!.data).toBe('keep me too');\n expect(fetched!.dataFormat).toBe('text/plain');\n });\n\n it('deletes an entity', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n await provider.deleteEntity(repoId, 'e1');\n expect(await provider.getEntity(repoId, 'e1')).toBeNull();\n });\n\n it('finds entities by search term', async () => {\n await provider.createEntity(repoId, makeEntity('e1', 'test-type', 'Alpha'));\n await provider.createEntity(repoId, makeEntity('e2', 'test-type', 'Beta'));\n\n const result = await provider.findEntities(repoId, {\n searchTerm: 'alpha',\n limit: 10,\n offset: 0,\n });\n expect(result.items).toHaveLength(1);\n expect(result.items[0]!.label).toBe('Alpha');\n });\n\n it('finds entities by search term case-insensitively', async () => {\n // Locks the invariant that searchTerm matching is case-insensitive on\n // every provider. In-memory lowercases both sides; SQL Server's LIKE\n // is case-insensitive under the default *_CI_AS collation; Cosmos\n // routes through CONTAINS(..., @term, true) on the Document endpoint.\n await provider.createEntity(repoId, makeEntity('e1', 'test-type', 'Alpha'));\n\n const result = await provider.findEntities(repoId, {\n searchTerm: 'ALPHA',\n limit: 10,\n offset: 0,\n });\n expect(result.items).toHaveLength(1);\n expect(result.items[0]!.label).toBe('Alpha');\n });\n\n it('finds entities by type filter', async () => {\n await provider.createEntity(repoId, makeEntity('e1', 'type-a', 'A'));\n await provider.createEntity(repoId, makeEntity('e2', 'type-b', 'B'));\n\n const result = await provider.findEntities(repoId, {\n entityTypes: ['type-a'],\n limit: 10,\n offset: 0,\n });\n expect(result.items).toHaveLength(1);\n expect(result.items[0]!.entityType).toBe('type-a');\n });\n\n it('paginates find results', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n await provider.createEntity(repoId, makeEntity('e2'));\n await provider.createEntity(repoId, makeEntity('e3'));\n\n const page1 = await provider.findEntities(repoId, { limit: 2, offset: 0 });\n expect(page1.items).toHaveLength(2);\n expect(page1.hasMore).toBe(true);\n\n const page2 = await provider.findEntities(repoId, { limit: 2, offset: 2 });\n expect(page2.items).toHaveLength(1);\n expect(page2.hasMore).toBe(false);\n });\n });\n\n // ─── Relationships ──────────────────────────────────────\n\n describe('relationship operations', () => {\n beforeEach(async () => {\n await provider.createEntity(repoId, makeEntity('a'));\n await provider.createEntity(repoId, makeEntity('b'));\n await provider.createEntity(repoId, makeEntity('c'));\n });\n\n it('creates and retrieves a relationship', async () => {\n const rel = makeRelationship('r1', 'connects', 'a', 'b');\n await provider.createRelationship(repoId, rel);\n\n const retrieved = await provider.getRelationship(repoId, 'r1');\n expect(retrieved).not.toBeNull();\n expect(retrieved!.sourceEntityId).toBe('a');\n expect(retrieved!.targetEntityId).toBe('b');\n });\n\n it('returns null for non-existent relationship', async () => {\n expect(await provider.getRelationship(repoId, 'nonexistent')).toBeNull();\n });\n\n it('gets entity relationships', async () => {\n await provider.createRelationship(repoId, makeRelationship('r1', 'connects', 'a', 'b'));\n await provider.createRelationship(repoId, makeRelationship('r2', 'connects', 'c', 'a'));\n\n const result = await provider.getEntityRelationships(repoId, 'a');\n expect(result.items).toHaveLength(2);\n });\n\n it('filters relationships by direction', async () => {\n await provider.createRelationship(repoId, makeRelationship('r1', 'connects', 'a', 'b'));\n await provider.createRelationship(repoId, makeRelationship('r2', 'connects', 'c', 'a'));\n\n const outbound = await provider.getEntityRelationships(repoId, 'a', { direction: 'outbound' });\n expect(outbound.items).toHaveLength(1);\n expect(outbound.items[0]!.targetEntityId).toBe('b');\n\n const inbound = await provider.getEntityRelationships(repoId, 'a', { direction: 'inbound' });\n expect(inbound.items).toHaveLength(1);\n expect(inbound.items[0]!.sourceEntityId).toBe('c');\n });\n\n it('deletes a relationship', async () => {\n await provider.createRelationship(repoId, makeRelationship('r1', 'connects', 'a', 'b'));\n await provider.deleteRelationship(repoId, 'r1');\n expect(await provider.getRelationship(repoId, 'r1')).toBeNull();\n });\n });\n\n // ─── Graph Traversal ────────────────────────────────────\n\n describe('graph traversal', () => {\n beforeEach(async () => {\n await provider.createEntity(repoId, makeEntity('a', 'node', 'A'));\n await provider.createEntity(repoId, makeEntity('b', 'node', 'B'));\n await provider.createEntity(repoId, makeEntity('c', 'node', 'C'));\n await provider.createRelationship(repoId, makeRelationship('r1', 'links', 'a', 'b'));\n await provider.createRelationship(repoId, makeRelationship('r2', 'links', 'b', 'c'));\n });\n\n it('explores neighborhood at depth 1', async () => {\n const result = await provider.exploreNeighborhood(repoId, 'a', {\n depth: 1,\n direction: 'both',\n limitPerType: 10,\n offsetPerType: 0,\n });\n expect(result.centerId).toBe('a');\n expect(result.layers).toHaveLength(1);\n });\n\n it('finds paths between connected entities', async () => {\n const result = await provider.findPaths(repoId, 'a', 'c', {\n maxDepth: 3,\n limit: 5,\n offset: 0,\n });\n expect(result.paths.length).toBeGreaterThanOrEqual(1);\n const firstPath = result.paths[0]!;\n expect(firstPath.entityIds[0]).toBe('a');\n expect(firstPath.entityIds[firstPath.entityIds.length - 1]).toBe('c');\n });\n\n it('returns empty paths when no connection', async () => {\n await provider.createEntity(repoId, makeEntity('isolated', 'node', 'Isolated'));\n const result = await provider.findPaths(repoId, 'a', 'isolated', {\n maxDepth: 3,\n limit: 5,\n offset: 0,\n });\n expect(result.paths).toHaveLength(0);\n });\n\n it('finds paths through non-bidirectional inbound edges', async () => {\n // Graph: a → b ← d (both edges are non-bidirectional)\n // Path from a to d should traverse: a →(outbound) b ←(inbound) d\n await provider.createEntity(repoId, makeEntity('d', 'node', 'D'));\n await provider.createRelationship(repoId, makeRelationship('r3', 'links', 'd', 'b'));\n const result = await provider.findPaths(repoId, 'a', 'd', {\n maxDepth: 3,\n limit: 5,\n offset: 0,\n });\n expect(result.paths.length).toBeGreaterThanOrEqual(1);\n const firstPath = result.paths[0]!;\n expect(firstPath.entityIds[0]).toBe('a');\n expect(firstPath.entityIds[firstPath.entityIds.length - 1]).toBe('d');\n });\n });\n\n // ─── Timeline ───────────────────────────────────────────\n\n describe('timeline', () => {\n it('returns timeline events', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n const result = await provider.getTimeline(repoId, 'e1', {\n limit: 10,\n offset: 0,\n });\n expect(result.events.length).toBeGreaterThanOrEqual(1);\n });\n });\n\n // ─── Bulk Operations ────────────────────────────────────\n\n describe('bulk operations', () => {\n it('exports data', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n\n const chunks = [];\n for await (const chunk of provider.exportAll(repoId)) {\n chunks.push(chunk);\n }\n expect(chunks.length).toBeGreaterThanOrEqual(1);\n });\n\n it('imports data', async () => {\n const result = await provider.importBulk(repoId, [\n { entities: [makeEntity('imported-1'), makeEntity('imported-2')] },\n { relationships: [makeRelationship('ir1', 'links', 'imported-1', 'imported-2')] },\n ]);\n expect(result.entitiesImported).toBe(2);\n expect(result.relationshipsImported).toBe(1);\n\n // Verify imported data is accessible\n const e = await provider.getEntity(repoId, 'imported-1');\n expect(e).not.toBeNull();\n });\n });\n\n // ─── Delete All Contents ───────────────────────────────\n\n describe('deleteAllContents', () => {\n it('deletes all entities and relationships but preserves the repository', async () => {\n await provider.createEntity(repoId, makeEntity('e1', 'alpha'));\n await provider.createEntity(repoId, makeEntity('e2', 'beta'));\n await provider.createRelationship(repoId, makeRelationship('r1', 'links', 'e1', 'e2'));\n\n const result = await provider.deleteAllContents(repoId);\n expect(result.deletedEntities).toBe(2);\n expect(result.deletedRelationships).toBe(1);\n\n // Repository still exists\n const repo = await provider.getRepository(repoId);\n expect(repo).not.toBeNull();\n\n // Vocabulary still exists\n const vocab = await provider.getVocabulary(repoId);\n expect(vocab).toBeDefined();\n\n // Contents are gone\n const stats = await provider.getRepositoryStats(repoId);\n expect(stats.entityCount).toBe(0);\n expect(stats.relationshipCount).toBe(0);\n });\n\n it('returns zero counts on an empty repository', async () => {\n const result = await provider.deleteAllContents(repoId);\n expect(result.deletedEntities).toBe(0);\n expect(result.deletedRelationships).toBe(0);\n });\n });\n\n // ─── Stats after data ───────────────────────────────────\n\n describe('stats reflect data', () => {\n it('counts entities and relationships', async () => {\n await provider.createEntity(repoId, makeEntity('e1', 'alpha'));\n await provider.createEntity(repoId, makeEntity('e2', 'alpha'));\n await provider.createEntity(repoId, makeEntity('e3', 'beta'));\n await provider.createRelationship(repoId, makeRelationship('r1', 'links', 'e1', 'e2'));\n\n const stats = await provider.getRepositoryStats(repoId);\n expect(stats.entityCount).toBe(3);\n expect(stats.relationshipCount).toBe(1);\n expect(stats.entityTypeBreakdown['alpha']).toBe(2);\n expect(stats.entityTypeBreakdown['beta']).toBe(1);\n expect(stats.relationshipTypeBreakdown['links']).toBe(1);\n });\n });\n });\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/providers-builtin/conformance.ts"],"sourcesContent":["// Provider Conformance Test Suite\n// Any StorageProvider implementer can import and run these tests to verify conformance.\n//\n// Usage:\n// import { runStorageProviderConformanceTests } from '@utaba/deep-memory';\n// runStorageProviderConformanceTests(() => new MyStorageProvider());\n\nimport { describe, it, expect, beforeEach } from 'vitest';\nimport type { StorageProvider } from '../providers/StorageProvider.js';\nimport type { StoredEntity } from '../types/entities.js';\nimport type { StoredRelationship } from '../types/relationships.js';\nimport type { Provenance } from '../types/provenance.js';\n\nfunction makeProvenance(): Provenance {\n const now = new Date().toISOString();\n return {\n createdBy: 'conformance-test',\n createdByType: 'agent',\n createdAt: now,\n modifiedBy: 'conformance-test',\n modifiedByType: 'agent',\n modifiedAt: now,\n };\n}\n\nfunction makeEntity(id: string, type = 'test-type', label?: string): StoredEntity {\n return {\n id,\n slug: `${type}:${(label ?? id).toLowerCase().replace(/[^a-z0-9]+/g, '-')}`,\n entityType: type,\n label: label ?? id,\n summary: `Summary for ${id}`,\n properties: { key: 'value' },\n provenance: makeProvenance(),\n };\n}\n\nfunction makeRelationship(\n id: string,\n type: string,\n sourceId: string,\n targetId: string,\n bidirectional = false,\n): StoredRelationship {\n return {\n id,\n relationshipType: type,\n sourceEntityId: sourceId,\n targetEntityId: targetId,\n properties: {},\n bidirectional,\n provenance: makeProvenance(),\n };\n}\n\n/**\n * Run the full StorageProvider conformance test suite.\n *\n * @param factory - A function that creates a fresh, empty StorageProvider instance.\n * Called before each test to ensure isolation.\n */\nexport function runStorageProviderConformanceTests(\n factory: () => StorageProvider | Promise<StorageProvider>,\n): void {\n // Use a stable GUID so external cleanup scripts can target it\n const repoId = '40000000-0000-4000-a000-000000000001';\n\n let provider: StorageProvider;\n\n async function setup(): Promise<void> {\n provider = await factory();\n if (provider.initialize) await provider.initialize();\n\n await provider.createRepository({\n repositoryId: repoId,\n label: 'Conformance Test',\n governanceConfig: { mode: 'open' },\n createdAt: new Date().toISOString(),\n createdBy: 'conformance-test',\n });\n }\n\n describe('StorageProvider Conformance Tests', () => {\n beforeEach(async () => {\n await setup();\n });\n\n // ─── Repository ─────────────────────────────────────────\n\n describe('repository operations', () => {\n it('creates a repository', async () => {\n const repo = await provider.getRepository(repoId);\n expect(repo).not.toBeNull();\n expect(repo!.repositoryId).toBe(repoId);\n expect(repo!.label).toBe('Conformance Test');\n });\n\n it('returns null for non-existent repository', async () => {\n const repo = await provider.getRepository('ffffffff-ffff-4fff-afff-ffffffffffff');\n expect(repo).toBeNull();\n });\n\n it('lists repositories', async () => {\n const list = await provider.listRepositories();\n expect(list.items.length).toBeGreaterThanOrEqual(1);\n expect(list.items.some((r) => r.repositoryId === repoId)).toBe(true);\n });\n\n it('updates a repository', async () => {\n const updated = await provider.updateRepository(repoId, {\n label: 'Updated Label',\n description: 'Updated description',\n governanceConfig: { mode: 'open', defaultSimilarityThreshold: 0.4 },\n });\n expect(updated.label).toBe('Updated Label');\n expect(updated.description).toBe('Updated description');\n expect(updated.governanceConfig.defaultSimilarityThreshold).toBe(0.4);\n\n // Verify persistence\n const fetched = await provider.getRepository(repoId);\n expect(fetched!.label).toBe('Updated Label');\n expect(fetched!.governanceConfig.defaultSimilarityThreshold).toBe(0.4);\n });\n\n it('deletes a repository', async () => {\n await provider.deleteRepository(repoId);\n expect(await provider.getRepository(repoId)).toBeNull();\n });\n\n it('returns repository stats', async () => {\n const stats = await provider.getRepositoryStats(repoId);\n expect(stats.entityCount).toBe(0);\n expect(stats.relationshipCount).toBe(0);\n expect(typeof stats.vocabularyVersion).toBe('string');\n });\n });\n\n // ─── Vocabulary ─────────────────────────────────────────\n\n describe('vocabulary operations', () => {\n it('gets and saves vocabulary', async () => {\n const vocab = await provider.getVocabulary(repoId);\n expect(vocab).toBeDefined();\n expect(typeof vocab.version).toBe('string');\n\n const updated = { ...vocab, version: '1.0.0' };\n await provider.saveVocabulary(repoId, updated);\n\n const fetched = await provider.getVocabulary(repoId);\n expect(fetched.version).toBe('1.0.0');\n });\n\n it('returns vocabulary change log', async () => {\n const log = await provider.getVocabularyChangeLog(repoId);\n expect(Array.isArray(log.items)).toBe(true);\n });\n });\n\n // ─── Entities ───────────────────────────────────────────\n\n describe('entity operations', () => {\n it('creates and retrieves an entity', async () => {\n const entity = makeEntity('e1');\n await provider.createEntity(repoId, entity);\n\n const retrieved = await provider.getEntity(repoId, 'e1');\n expect(retrieved).not.toBeNull();\n expect(retrieved!.id).toBe('e1');\n expect(retrieved!.label).toBe('e1');\n });\n\n it('retrieves an entity by slug', async () => {\n const entity = makeEntity('e1', 'test-type', 'Alpha');\n await provider.createEntity(repoId, entity);\n\n const retrieved = await provider.getEntityBySlug(repoId, entity.slug);\n expect(retrieved).not.toBeNull();\n expect(retrieved!.id).toBe('e1');\n expect(retrieved!.slug).toBe(entity.slug);\n });\n\n it('returns null for non-existent entity', async () => {\n const result = await provider.getEntity(repoId, 'nonexistent');\n expect(result).toBeNull();\n });\n\n it('returns null for non-existent slug', async () => {\n const result = await provider.getEntityBySlug(repoId, 'nonexistent:slug');\n expect(result).toBeNull();\n });\n\n it('batch retrieves entities', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n await provider.createEntity(repoId, makeEntity('e2'));\n\n const map = await provider.getEntities(repoId, ['e1', 'e2', 'missing']);\n expect(map.size).toBe(2);\n expect(map.has('e1')).toBe(true);\n expect(map.has('e2')).toBe(true);\n expect(map.has('missing')).toBe(false);\n });\n\n it('updates an entity', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n const updated = await provider.updateEntity(repoId, 'e1', {\n label: 'Updated Label',\n provenance: makeProvenance(),\n });\n expect(updated.label).toBe('Updated Label');\n\n const fetched = await provider.getEntity(repoId, 'e1');\n expect(fetched!.label).toBe('Updated Label');\n });\n\n it('clears summary/data/dataFormat when null is passed', async () => {\n const entity: StoredEntity = {\n ...makeEntity('e1'),\n summary: 'starting summary',\n data: 'raw content',\n dataFormat: 'text/plain',\n };\n await provider.createEntity(repoId, entity);\n\n await provider.updateEntity(repoId, 'e1', {\n summary: null,\n data: null,\n dataFormat: null,\n provenance: makeProvenance(),\n });\n\n const fetched = await provider.getEntity(repoId, 'e1');\n expect(fetched!.summary).toBeUndefined();\n expect(fetched!.data).toBeUndefined();\n expect(fetched!.dataFormat).toBeUndefined();\n });\n\n it('preserves summary/data/dataFormat when undefined is passed', async () => {\n const entity: StoredEntity = {\n ...makeEntity('e1'),\n summary: 'keep me',\n data: 'keep me too',\n dataFormat: 'text/plain',\n };\n await provider.createEntity(repoId, entity);\n\n await provider.updateEntity(repoId, 'e1', {\n label: 'Renamed',\n provenance: makeProvenance(),\n });\n\n const fetched = await provider.getEntity(repoId, 'e1');\n expect(fetched!.summary).toBe('keep me');\n expect(fetched!.data).toBe('keep me too');\n expect(fetched!.dataFormat).toBe('text/plain');\n });\n\n it('deletes an entity', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n await provider.deleteEntity(repoId, 'e1');\n expect(await provider.getEntity(repoId, 'e1')).toBeNull();\n });\n\n it('finds entities by search term', async () => {\n await provider.createEntity(repoId, makeEntity('e1', 'test-type', 'Alpha'));\n await provider.createEntity(repoId, makeEntity('e2', 'test-type', 'Beta'));\n\n const result = await provider.findEntities(repoId, {\n searchTerm: 'alpha',\n limit: 10,\n offset: 0,\n });\n expect(result.items).toHaveLength(1);\n expect(result.items[0]!.label).toBe('Alpha');\n });\n\n it('finds entities by search term case-insensitively', async () => {\n // Locks the invariant that searchTerm matching is case-insensitive on\n // every provider. In-memory lowercases both sides; SQL Server's LIKE\n // is case-insensitive under the default *_CI_AS collation; Cosmos\n // routes through CONTAINS(..., @term, true) on the Document endpoint.\n await provider.createEntity(repoId, makeEntity('e1', 'test-type', 'Alpha'));\n\n const result = await provider.findEntities(repoId, {\n searchTerm: 'ALPHA',\n limit: 10,\n offset: 0,\n });\n expect(result.items).toHaveLength(1);\n expect(result.items[0]!.label).toBe('Alpha');\n });\n\n it('finds entities by type filter', async () => {\n await provider.createEntity(repoId, makeEntity('e1', 'type-a', 'A'));\n await provider.createEntity(repoId, makeEntity('e2', 'type-b', 'B'));\n\n const result = await provider.findEntities(repoId, {\n entityTypes: ['type-a'],\n limit: 10,\n offset: 0,\n });\n expect(result.items).toHaveLength(1);\n expect(result.items[0]!.entityType).toBe('type-a');\n });\n\n it('paginates find results', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n await provider.createEntity(repoId, makeEntity('e2'));\n await provider.createEntity(repoId, makeEntity('e3'));\n\n const page1 = await provider.findEntities(repoId, { limit: 2, offset: 0 });\n expect(page1.items).toHaveLength(2);\n expect(page1.hasMore).toBe(true);\n\n const page2 = await provider.findEntities(repoId, { limit: 2, offset: 2 });\n expect(page2.items).toHaveLength(1);\n expect(page2.hasMore).toBe(false);\n });\n });\n\n // ─── Relationships ──────────────────────────────────────\n\n describe('relationship operations', () => {\n beforeEach(async () => {\n await provider.createEntity(repoId, makeEntity('a'));\n await provider.createEntity(repoId, makeEntity('b'));\n await provider.createEntity(repoId, makeEntity('c'));\n });\n\n it('creates and retrieves a relationship', async () => {\n const rel = makeRelationship('r1', 'connects', 'a', 'b');\n await provider.createRelationship(repoId, rel);\n\n const retrieved = await provider.getRelationship(repoId, 'r1');\n expect(retrieved).not.toBeNull();\n expect(retrieved!.sourceEntityId).toBe('a');\n expect(retrieved!.targetEntityId).toBe('b');\n });\n\n it('returns null for non-existent relationship', async () => {\n expect(await provider.getRelationship(repoId, 'nonexistent')).toBeNull();\n });\n\n it('gets entity relationships', async () => {\n await provider.createRelationship(repoId, makeRelationship('r1', 'connects', 'a', 'b'));\n await provider.createRelationship(repoId, makeRelationship('r2', 'connects', 'c', 'a'));\n\n const result = await provider.getEntityRelationships(repoId, 'a');\n expect(result.items).toHaveLength(2);\n });\n\n it('filters relationships by direction', async () => {\n await provider.createRelationship(repoId, makeRelationship('r1', 'connects', 'a', 'b'));\n await provider.createRelationship(repoId, makeRelationship('r2', 'connects', 'c', 'a'));\n\n const outbound = await provider.getEntityRelationships(repoId, 'a', { direction: 'out' });\n expect(outbound.items).toHaveLength(1);\n expect(outbound.items[0]!.targetEntityId).toBe('b');\n\n const inbound = await provider.getEntityRelationships(repoId, 'a', { direction: 'in' });\n expect(inbound.items).toHaveLength(1);\n expect(inbound.items[0]!.sourceEntityId).toBe('c');\n });\n\n it('deletes a relationship', async () => {\n await provider.createRelationship(repoId, makeRelationship('r1', 'connects', 'a', 'b'));\n await provider.deleteRelationship(repoId, 'r1');\n expect(await provider.getRelationship(repoId, 'r1')).toBeNull();\n });\n });\n\n // ─── Graph Traversal ────────────────────────────────────\n\n describe('graph traversal', () => {\n beforeEach(async () => {\n await provider.createEntity(repoId, makeEntity('a', 'node', 'A'));\n await provider.createEntity(repoId, makeEntity('b', 'node', 'B'));\n await provider.createEntity(repoId, makeEntity('c', 'node', 'C'));\n await provider.createRelationship(repoId, makeRelationship('r1', 'links', 'a', 'b'));\n await provider.createRelationship(repoId, makeRelationship('r2', 'links', 'b', 'c'));\n });\n\n it('explores neighborhood at depth 1', async () => {\n const result = await provider.exploreNeighborhood(repoId, 'a', {\n depth: 1,\n direction: 'both',\n limitPerType: 10,\n offsetPerType: 0,\n });\n expect(result.centerId).toBe('a');\n expect(result.layers).toHaveLength(1);\n });\n\n it('dedupes connected entities per (layer, relationship-type) bucket', async () => {\n // Two same-type edges connect the same pair (b ↔ c), modelling a\n // logically-symmetric relationship as two directed half-edges. The\n // bucket should still report c exactly once at depth 2 from a.\n await provider.createRelationship(repoId, makeRelationship('r3', 'links', 'c', 'b'));\n const result = await provider.exploreNeighborhood(repoId, 'a', {\n depth: 2,\n direction: 'both',\n limitPerType: 10,\n offsetPerType: 0,\n });\n const linksLayer2 = result.layers[1]?.['links'];\n expect(linksLayer2).toBeDefined();\n expect(linksLayer2!.total).toBe(1);\n expect(linksLayer2!.entities.map((e) => e.id)).toEqual(['c']);\n });\n\n it('finds paths between connected entities', async () => {\n const result = await provider.findPaths(repoId, 'a', 'c', {\n maxDepth: 3,\n limit: 5,\n offset: 0,\n });\n expect(result.paths.length).toBeGreaterThanOrEqual(1);\n const firstPath = result.paths[0]!;\n expect(firstPath.entityIds[0]).toBe('a');\n expect(firstPath.entityIds[firstPath.entityIds.length - 1]).toBe('c');\n });\n\n it('returns empty paths when no connection', async () => {\n await provider.createEntity(repoId, makeEntity('isolated', 'node', 'Isolated'));\n const result = await provider.findPaths(repoId, 'a', 'isolated', {\n maxDepth: 3,\n limit: 5,\n offset: 0,\n });\n expect(result.paths).toHaveLength(0);\n });\n\n it('returns only simple paths (no repeated vertices)', async () => {\n // Set up a graph where a non-simple walk could otherwise emerge:\n // a — b — c (the target)\n // c — d via two distinct edges\n // A walk of length 4 along (a)-(b)-(c)-(d)-(c) reuses neither edge\n // but visits c twice. The contract requires findPaths to drop walks\n // that visit any vertex more than once.\n await provider.createEntity(repoId, makeEntity('d', 'node', 'D'));\n await provider.createRelationship(repoId, makeRelationship('r3', 'links', 'c', 'd'));\n await provider.createRelationship(repoId, makeRelationship('r4', 'links', 'c', 'd'));\n const result = await provider.findPaths(repoId, 'a', 'c', {\n maxDepth: 4,\n limit: 50,\n offset: 0,\n });\n for (const path of result.paths) {\n expect(new Set(path.entityIds).size).toBe(path.entityIds.length);\n expect(path.entityIds[path.entityIds.length - 1]).toBe('c');\n }\n // Exactly one simple path from a to c at depth ≤ 4: a-b-c.\n expect(result.totalPaths).toBe(1);\n });\n\n it('finds paths through non-bidirectional inbound edges', async () => {\n // Graph: a → b ← d (both edges are non-bidirectional)\n // Path from a to d should traverse: a →(outbound) b ←(inbound) d\n await provider.createEntity(repoId, makeEntity('d', 'node', 'D'));\n await provider.createRelationship(repoId, makeRelationship('r3', 'links', 'd', 'b'));\n const result = await provider.findPaths(repoId, 'a', 'd', {\n maxDepth: 3,\n limit: 5,\n offset: 0,\n });\n expect(result.paths.length).toBeGreaterThanOrEqual(1);\n const firstPath = result.paths[0]!;\n expect(firstPath.entityIds[0]).toBe('a');\n expect(firstPath.entityIds[firstPath.entityIds.length - 1]).toBe('d');\n });\n });\n\n // ─── Timeline ───────────────────────────────────────────\n\n describe('timeline', () => {\n it('returns timeline events', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n const result = await provider.getTimeline(repoId, 'e1', {\n limit: 10,\n offset: 0,\n });\n expect(result.events.length).toBeGreaterThanOrEqual(1);\n });\n });\n\n // ─── Bulk Operations ────────────────────────────────────\n\n describe('bulk operations', () => {\n it('exports data', async () => {\n await provider.createEntity(repoId, makeEntity('e1'));\n\n const chunks = [];\n for await (const chunk of provider.exportAll(repoId)) {\n chunks.push(chunk);\n }\n expect(chunks.length).toBeGreaterThanOrEqual(1);\n });\n\n it('imports data', async () => {\n const result = await provider.importBulk(repoId, [\n { entities: [makeEntity('imported-1'), makeEntity('imported-2')] },\n { relationships: [makeRelationship('ir1', 'links', 'imported-1', 'imported-2')] },\n ]);\n expect(result.entitiesImported).toBe(2);\n expect(result.relationshipsImported).toBe(1);\n\n // Verify imported data is accessible\n const e = await provider.getEntity(repoId, 'imported-1');\n expect(e).not.toBeNull();\n });\n });\n\n // ─── Delete All Contents ───────────────────────────────\n\n describe('deleteAllContents', () => {\n it('deletes all entities and relationships but preserves the repository', async () => {\n await provider.createEntity(repoId, makeEntity('e1', 'alpha'));\n await provider.createEntity(repoId, makeEntity('e2', 'beta'));\n await provider.createRelationship(repoId, makeRelationship('r1', 'links', 'e1', 'e2'));\n\n const result = await provider.deleteAllContents(repoId);\n expect(result.deletedEntities).toBe(2);\n expect(result.deletedRelationships).toBe(1);\n\n // Repository still exists\n const repo = await provider.getRepository(repoId);\n expect(repo).not.toBeNull();\n\n // Vocabulary still exists\n const vocab = await provider.getVocabulary(repoId);\n expect(vocab).toBeDefined();\n\n // Contents are gone\n const stats = await provider.getRepositoryStats(repoId);\n expect(stats.entityCount).toBe(0);\n expect(stats.relationshipCount).toBe(0);\n });\n\n it('returns zero counts on an empty repository', async () => {\n const result = await provider.deleteAllContents(repoId);\n expect(result.deletedEntities).toBe(0);\n expect(result.deletedRelationships).toBe(0);\n });\n });\n\n // ─── Stats after data ───────────────────────────────────\n\n describe('stats reflect data', () => {\n it('counts entities and relationships', async () => {\n await provider.createEntity(repoId, makeEntity('e1', 'alpha'));\n await provider.createEntity(repoId, makeEntity('e2', 'alpha'));\n await provider.createEntity(repoId, makeEntity('e3', 'beta'));\n await provider.createRelationship(repoId, makeRelationship('r1', 'links', 'e1', 'e2'));\n\n const stats = await provider.getRepositoryStats(repoId);\n expect(stats.entityCount).toBe(3);\n expect(stats.relationshipCount).toBe(1);\n expect(stats.entityTypeBreakdown['alpha']).toBe(2);\n expect(stats.entityTypeBreakdown['beta']).toBe(1);\n expect(stats.relationshipTypeBreakdown['links']).toBe(1);\n });\n });\n });\n}\n"],"mappings":"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","names":[]}
|