From 223c2fc8ffe8dbfef34aabdd8edd0b6f5389ab7b Mon Sep 17 00:00:00 2001 From: Duda Nogueira Date: Wed, 11 Feb 2026 16:19:55 -0300 Subject: [PATCH 1/4] implements exportToJson and createFromJson --- src/collections/index.ts | 7 + test/collections/integration.test.ts | 252 +++++++++++++++++++++++++++ 2 files changed, 259 insertions(+) diff --git a/src/collections/index.ts b/src/collections/index.ts index 942f3456..30eff51a 100644 --- a/src/collections/index.ts +++ b/src/collections/index.ts @@ -120,6 +120,10 @@ const collections = (connection: Connection, dbVersionSupport: DbVersionSupport) const { class: name } = await new ClassCreator(connection).withClass(config).do(); return collection(connection, name as string, dbVersionSupport); }, + createFromJson: async (schemaJson: any) => { + const { class: name } = await connection.postReturn('/schema', schemaJson); + return collection(connection, name as string, dbVersionSupport); + }, delete: deleteCollection, deleteAll: () => listAll().then((configs) => Promise.all(configs?.map((c) => deleteCollection(c.name)))), exists: (name: string) => new ClassExists(connection).withClassName(name).do(), @@ -128,6 +132,7 @@ const collections = (connection: Connection, dbVersionSupport: DbVersionSupport) .withClassName(name) .do() .then(classToCollection), + exportToJson: (name: string) => connection.get(`/schema/${name}`, true), listAll: listAll, get: ( name: TName @@ -151,10 +156,12 @@ export interface Collections { config: CollectionConfigCreate ): Promise>; createFromSchema(config: WeaviateClass): Promise>; + createFromJson(schemaJson: any): Promise>; delete(collection: string): Promise; deleteAll(): Promise; exists(name: string): Promise; export(name: string): Promise; + exportToJson(name: string): Promise; get( name: TName ): Collection; diff --git a/test/collections/integration.test.ts b/test/collections/integration.test.ts index f1a6a53d..fe0859e5 100644 --- a/test/collections/integration.test.ts +++ b/test/collections/integration.test.ts @@ -710,3 +710,255 @@ describe('Testing of the collections.create method', () => { }); }); }); + +describe('Testing of collections.exportToJson and collections.createFromJson methods', () => { + let client: WeaviateClient; + + beforeAll(async () => { + client = await weaviate.connectToLocal({ + port: 8080, + grpcPort: 50051, + }); + }); + + afterAll(() => client.collections.deleteAll()); + + it('should export a collection schema to JSON using exportToJson', async () => { + const collectionName = 'TestCollectionExportToJson'; + + // First, create a collection + await client.collections.create({ + name: collectionName, + description: 'A test collection for JSON export', + properties: [ + { + name: 'title', + dataType: 'text', + }, + { + name: 'content', + dataType: 'text', + }, + { + name: 'publishDate', + dataType: 'date', + }, + ], + }); + + // Export the schema to JSON + const exportedSchema = await client.collections.exportToJson(collectionName); + + // Verify the exported schema has the expected structure + expect(exportedSchema).toBeDefined(); + expect(exportedSchema.class).toEqual(collectionName); + expect(exportedSchema.description).toEqual('A test collection for JSON export'); + expect(exportedSchema.properties).toBeDefined(); + expect(exportedSchema.properties?.length).toEqual(3); + + // Verify properties + const titleProp = exportedSchema.properties?.find((p) => p.name === 'title'); + expect(titleProp?.dataType).toEqual(['text']); + + const contentProp = exportedSchema.properties?.find((p) => p.name === 'content'); + expect(contentProp?.dataType).toEqual(['text']); + + const publishDateProp = exportedSchema.properties?.find((p) => p.name === 'publishDate'); + expect(publishDateProp?.dataType).toEqual(['date']); + }); + + it('should create a collection from JSON schema using createFromJson', async () => { + const collectionName = 'TestCollectionCreateFromJson'; + + // Define a schema as JSON + const schemaJson = { + class: collectionName, + description: 'A test collection created from JSON', + properties: [ + { + name: 'author', + dataType: ['text'], + }, + { + name: 'rating', + dataType: ['number'], + }, + ], + }; + + // Create collection from JSON + const collection = await client.collections.createFromJson(schemaJson); + + // Verify the collection was created + expect(collection).toBeDefined(); + expect(await collection.exists()).toEqual(true); + + // Verify the collection configuration + const config = await collection.config.get(); + expect(config.name).toEqual(collectionName); + expect(config.description).toEqual('A test collection created from JSON'); + expect(config.properties?.length).toEqual(2); + + const authorProp = config.properties?.find((p) => p.name === 'author'); + expect(authorProp?.dataType).toEqual('text'); + + const ratingProp = config.properties?.find((p) => p.name === 'rating'); + expect(ratingProp?.dataType).toEqual('number'); + }); + + it('should export and re-import a collection schema (round-trip)', async () => { + const originalName = 'TestCollectionRoundTrip'; + const reimportedName = 'TestCollectionRoundTripReimported'; + + // Create original collection + await client.collections.create({ + name: originalName, + description: 'Original collection for round-trip test', + properties: [ + { + name: 'title', + dataType: 'text', + }, + { + name: 'views', + dataType: 'int', + }, + { + name: 'published', + dataType: 'boolean', + }, + ], + }); + + // Export the schema + const exportedSchema = await client.collections.exportToJson(originalName); + + // Modify the name for re-import + exportedSchema.class = reimportedName; + exportedSchema.description = 'Reimported collection from exported schema'; + + // Re-import with new name + const reimportedCollection = await client.collections.createFromJson(exportedSchema); + + // Verify both collections exist + expect(await client.collections.exists(originalName)).toEqual(true); + expect(await client.collections.exists(reimportedName)).toEqual(true); + + // Verify the reimported collection has the same properties + const originalConfig = await client.collections.get(originalName).config.get(); + const reimportedConfig = await reimportedCollection.config.get(); + + expect(reimportedConfig.properties?.length).toEqual(originalConfig.properties?.length); + expect(reimportedConfig.properties?.map((p) => p.name).sort()).toEqual( + originalConfig.properties?.map((p) => p.name).sort() + ); + }); + + it('should export a collection with complex configuration', async () => { + const collectionName = 'TestCollectionComplexExport'; + + // Create a collection with complex configuration + await client.collections.create({ + name: collectionName, + description: 'Complex collection with nested properties', + properties: [ + { + name: 'metadata', + dataType: 'object', + nestedProperties: [ + { + name: 'author', + dataType: 'text', + }, + { + name: 'tags', + dataType: 'text[]', + }, + ], + }, + { + name: 'score', + dataType: 'number', + }, + ], + invertedIndex: { + indexTimestamps: true, + indexPropertyLength: true, + }, + }); + + // Export the schema + const exportedSchema = await client.collections.exportToJson(collectionName); + + // Verify complex configuration is preserved + expect(exportedSchema.class).toEqual(collectionName); + expect(exportedSchema.properties?.length).toEqual(2); + + // Verify nested properties + const metadataProp = exportedSchema.properties?.find((p) => p.name === 'metadata'); + expect(metadataProp?.dataType).toEqual(['object']); + expect(metadataProp?.nestedProperties).toBeDefined(); + expect(metadataProp?.nestedProperties?.length).toEqual(2); + + // Verify inverted index config + expect(exportedSchema.invertedIndexConfig).toBeDefined(); + expect(exportedSchema.invertedIndexConfig?.indexTimestamps).toEqual(true); + expect(exportedSchema.invertedIndexConfig?.indexPropertyLength).toEqual(true); + }); + + it('should create a collection from minimal JSON schema', async () => { + const collectionName = 'TestCollectionMinimalJson'; + + // Define minimal schema + const minimalSchema = { + class: collectionName, + properties: [ + { + name: 'text', + dataType: ['text'], + }, + ], + }; + + // Create collection from minimal JSON + const collection = await client.collections.createFromJson(minimalSchema); + + // Verify the collection was created with defaults + expect(await collection.exists()).toEqual(true); + const config = await collection.config.get(); + expect(config.name).toEqual(collectionName); + expect(config.properties?.length).toEqual(1); + }); + + it('should handle collection with named vectors in export/import', async () => { + const originalName = 'TestCollectionNamedVectorsOriginal'; + const reimportedName = 'TestCollectionNamedVectorsReimported'; + + // Create collection with named vector + await client.collections.create({ + name: originalName, + properties: [ + { + name: 'content', + dataType: 'text', + }, + ], + vectorizers: weaviate.configure.vectors.none({ name: 'custom_vector' }), + }); + + // Export the schema + const exportedSchema = await client.collections.exportToJson(originalName); + + // Verify vector configuration is in the export + expect(exportedSchema.vectorConfig).toBeDefined(); + + // Re-import with new name + exportedSchema.class = reimportedName; + const reimportedCollection = await client.collections.createFromJson(exportedSchema); + + // Verify the collection was created + expect(await reimportedCollection.exists()).toEqual(true); + const config = await reimportedCollection.config.get(); + expect(config.name).toEqual(reimportedName); + }); +}); From bc64ed66b011d465e6e675bc0ef36927275f1289 Mon Sep 17 00:00:00 2001 From: Duda Nogueira Date: Wed, 11 Feb 2026 16:59:24 -0300 Subject: [PATCH 2/4] fix test by deleting the created collections separately --- test/collections/integration.test.ts | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/test/collections/integration.test.ts b/test/collections/integration.test.ts index fe0859e5..e34b43f1 100644 --- a/test/collections/integration.test.ts +++ b/test/collections/integration.test.ts @@ -721,7 +721,29 @@ describe('Testing of collections.exportToJson and collections.createFromJson met }); }); - afterAll(() => client.collections.deleteAll()); + afterAll(async () => { + // Delete only the collections created by these tests + const collectionsToDelete = [ + 'TestCollectionExportToJson', + 'TestCollectionCreateFromJson', + 'TestCollectionRoundTrip', + 'TestCollectionRoundTripReimported', + 'TestCollectionComplexExport', + 'TestCollectionMinimalJson', + 'TestCollectionNamedVectorsOriginal', + 'TestCollectionNamedVectorsReimported', + ]; + + await Promise.all( + collectionsToDelete.map(async (name) => { + try { + await client.collections.delete(name); + } catch (e) { + // Ignore errors if collection doesn't exist + } + }) + ); + }); it('should export a collection schema to JSON using exportToJson', async () => { const collectionName = 'TestCollectionExportToJson'; From 8d36666bd773c2ecd3f80e90411c29c46e547cf5 Mon Sep 17 00:00:00 2001 From: Duda Nogueira Date: Wed, 11 Feb 2026 17:15:22 -0300 Subject: [PATCH 3/4] remove integration tests and keep only unit tests. --- test/collections/integration.test.ts | 274 ------------------ test/collections/schema-json.mock.test.ts | 336 ++++++++++++++++++++++ 2 files changed, 336 insertions(+), 274 deletions(-) create mode 100644 test/collections/schema-json.mock.test.ts diff --git a/test/collections/integration.test.ts b/test/collections/integration.test.ts index e34b43f1..f1a6a53d 100644 --- a/test/collections/integration.test.ts +++ b/test/collections/integration.test.ts @@ -710,277 +710,3 @@ describe('Testing of the collections.create method', () => { }); }); }); - -describe('Testing of collections.exportToJson and collections.createFromJson methods', () => { - let client: WeaviateClient; - - beforeAll(async () => { - client = await weaviate.connectToLocal({ - port: 8080, - grpcPort: 50051, - }); - }); - - afterAll(async () => { - // Delete only the collections created by these tests - const collectionsToDelete = [ - 'TestCollectionExportToJson', - 'TestCollectionCreateFromJson', - 'TestCollectionRoundTrip', - 'TestCollectionRoundTripReimported', - 'TestCollectionComplexExport', - 'TestCollectionMinimalJson', - 'TestCollectionNamedVectorsOriginal', - 'TestCollectionNamedVectorsReimported', - ]; - - await Promise.all( - collectionsToDelete.map(async (name) => { - try { - await client.collections.delete(name); - } catch (e) { - // Ignore errors if collection doesn't exist - } - }) - ); - }); - - it('should export a collection schema to JSON using exportToJson', async () => { - const collectionName = 'TestCollectionExportToJson'; - - // First, create a collection - await client.collections.create({ - name: collectionName, - description: 'A test collection for JSON export', - properties: [ - { - name: 'title', - dataType: 'text', - }, - { - name: 'content', - dataType: 'text', - }, - { - name: 'publishDate', - dataType: 'date', - }, - ], - }); - - // Export the schema to JSON - const exportedSchema = await client.collections.exportToJson(collectionName); - - // Verify the exported schema has the expected structure - expect(exportedSchema).toBeDefined(); - expect(exportedSchema.class).toEqual(collectionName); - expect(exportedSchema.description).toEqual('A test collection for JSON export'); - expect(exportedSchema.properties).toBeDefined(); - expect(exportedSchema.properties?.length).toEqual(3); - - // Verify properties - const titleProp = exportedSchema.properties?.find((p) => p.name === 'title'); - expect(titleProp?.dataType).toEqual(['text']); - - const contentProp = exportedSchema.properties?.find((p) => p.name === 'content'); - expect(contentProp?.dataType).toEqual(['text']); - - const publishDateProp = exportedSchema.properties?.find((p) => p.name === 'publishDate'); - expect(publishDateProp?.dataType).toEqual(['date']); - }); - - it('should create a collection from JSON schema using createFromJson', async () => { - const collectionName = 'TestCollectionCreateFromJson'; - - // Define a schema as JSON - const schemaJson = { - class: collectionName, - description: 'A test collection created from JSON', - properties: [ - { - name: 'author', - dataType: ['text'], - }, - { - name: 'rating', - dataType: ['number'], - }, - ], - }; - - // Create collection from JSON - const collection = await client.collections.createFromJson(schemaJson); - - // Verify the collection was created - expect(collection).toBeDefined(); - expect(await collection.exists()).toEqual(true); - - // Verify the collection configuration - const config = await collection.config.get(); - expect(config.name).toEqual(collectionName); - expect(config.description).toEqual('A test collection created from JSON'); - expect(config.properties?.length).toEqual(2); - - const authorProp = config.properties?.find((p) => p.name === 'author'); - expect(authorProp?.dataType).toEqual('text'); - - const ratingProp = config.properties?.find((p) => p.name === 'rating'); - expect(ratingProp?.dataType).toEqual('number'); - }); - - it('should export and re-import a collection schema (round-trip)', async () => { - const originalName = 'TestCollectionRoundTrip'; - const reimportedName = 'TestCollectionRoundTripReimported'; - - // Create original collection - await client.collections.create({ - name: originalName, - description: 'Original collection for round-trip test', - properties: [ - { - name: 'title', - dataType: 'text', - }, - { - name: 'views', - dataType: 'int', - }, - { - name: 'published', - dataType: 'boolean', - }, - ], - }); - - // Export the schema - const exportedSchema = await client.collections.exportToJson(originalName); - - // Modify the name for re-import - exportedSchema.class = reimportedName; - exportedSchema.description = 'Reimported collection from exported schema'; - - // Re-import with new name - const reimportedCollection = await client.collections.createFromJson(exportedSchema); - - // Verify both collections exist - expect(await client.collections.exists(originalName)).toEqual(true); - expect(await client.collections.exists(reimportedName)).toEqual(true); - - // Verify the reimported collection has the same properties - const originalConfig = await client.collections.get(originalName).config.get(); - const reimportedConfig = await reimportedCollection.config.get(); - - expect(reimportedConfig.properties?.length).toEqual(originalConfig.properties?.length); - expect(reimportedConfig.properties?.map((p) => p.name).sort()).toEqual( - originalConfig.properties?.map((p) => p.name).sort() - ); - }); - - it('should export a collection with complex configuration', async () => { - const collectionName = 'TestCollectionComplexExport'; - - // Create a collection with complex configuration - await client.collections.create({ - name: collectionName, - description: 'Complex collection with nested properties', - properties: [ - { - name: 'metadata', - dataType: 'object', - nestedProperties: [ - { - name: 'author', - dataType: 'text', - }, - { - name: 'tags', - dataType: 'text[]', - }, - ], - }, - { - name: 'score', - dataType: 'number', - }, - ], - invertedIndex: { - indexTimestamps: true, - indexPropertyLength: true, - }, - }); - - // Export the schema - const exportedSchema = await client.collections.exportToJson(collectionName); - - // Verify complex configuration is preserved - expect(exportedSchema.class).toEqual(collectionName); - expect(exportedSchema.properties?.length).toEqual(2); - - // Verify nested properties - const metadataProp = exportedSchema.properties?.find((p) => p.name === 'metadata'); - expect(metadataProp?.dataType).toEqual(['object']); - expect(metadataProp?.nestedProperties).toBeDefined(); - expect(metadataProp?.nestedProperties?.length).toEqual(2); - - // Verify inverted index config - expect(exportedSchema.invertedIndexConfig).toBeDefined(); - expect(exportedSchema.invertedIndexConfig?.indexTimestamps).toEqual(true); - expect(exportedSchema.invertedIndexConfig?.indexPropertyLength).toEqual(true); - }); - - it('should create a collection from minimal JSON schema', async () => { - const collectionName = 'TestCollectionMinimalJson'; - - // Define minimal schema - const minimalSchema = { - class: collectionName, - properties: [ - { - name: 'text', - dataType: ['text'], - }, - ], - }; - - // Create collection from minimal JSON - const collection = await client.collections.createFromJson(minimalSchema); - - // Verify the collection was created with defaults - expect(await collection.exists()).toEqual(true); - const config = await collection.config.get(); - expect(config.name).toEqual(collectionName); - expect(config.properties?.length).toEqual(1); - }); - - it('should handle collection with named vectors in export/import', async () => { - const originalName = 'TestCollectionNamedVectorsOriginal'; - const reimportedName = 'TestCollectionNamedVectorsReimported'; - - // Create collection with named vector - await client.collections.create({ - name: originalName, - properties: [ - { - name: 'content', - dataType: 'text', - }, - ], - vectorizers: weaviate.configure.vectors.none({ name: 'custom_vector' }), - }); - - // Export the schema - const exportedSchema = await client.collections.exportToJson(originalName); - - // Verify vector configuration is in the export - expect(exportedSchema.vectorConfig).toBeDefined(); - - // Re-import with new name - exportedSchema.class = reimportedName; - const reimportedCollection = await client.collections.createFromJson(exportedSchema); - - // Verify the collection was created - expect(await reimportedCollection.exists()).toEqual(true); - const config = await reimportedCollection.config.get(); - expect(config.name).toEqual(reimportedName); - }); -}); diff --git a/test/collections/schema-json.mock.test.ts b/test/collections/schema-json.mock.test.ts new file mode 100644 index 00000000..c3a7e1ac --- /dev/null +++ b/test/collections/schema-json.mock.test.ts @@ -0,0 +1,336 @@ +import express from 'express'; +import { Server as HttpServer } from 'http'; +import { Server as GrpcServer, createServer } from 'nice-grpc'; +import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; +import weaviate, { WeaviateClient } from '../../src/index.js'; +import { + HealthCheckRequest, + HealthCheckResponse, + HealthCheckResponse_ServingStatus, + HealthDefinition, + HealthServiceImplementation, +} from '../../src/proto/google/health/v1/health.js'; +import { WeaviateClass } from '../../src/v2/index.js'; + +// Mock schema data +const mockExportedSchema: WeaviateClass = { + class: 'TestCollection', + description: 'A test collection for JSON export', + properties: [ + { + name: 'title', + dataType: ['text'], + }, + { + name: 'content', + dataType: ['text'], + }, + { + name: 'publishDate', + dataType: ['date'], + }, + ], + vectorConfig: { + default: { + vectorIndexType: 'hnsw', + vectorizer: { + 'text2vec-contextionary': { + vectorizeClassName: true, + }, + }, + }, + }, +}; + +const mockComplexSchema: WeaviateClass = { + class: 'ComplexCollection', + description: 'Complex collection with nested properties', + properties: [ + { + name: 'metadata', + dataType: ['object'], + nestedProperties: [ + { + name: 'author', + dataType: ['text'], + }, + { + name: 'tags', + dataType: ['text[]'], + }, + ], + }, + { + name: 'score', + dataType: ['number'], + }, + ], + invertedIndexConfig: { + indexTimestamps: true, + indexPropertyLength: true, + }, +}; + +const mockNamedVectorSchema: WeaviateClass = { + class: 'NamedVectorCollection', + properties: [ + { + name: 'content', + dataType: ['text'], + }, + ], + vectorConfig: { + custom_vector: { + vectorIndexType: 'hnsw', + vectorizer: { + none: {}, + }, + }, + }, +}; + +class SchemaJsonMock { + private grpc: GrpcServer; + private http: HttpServer; + private createdSchemas: Map = new Map(); + + constructor(grpc: GrpcServer, http: HttpServer) { + this.grpc = grpc; + this.http = http; + // Pre-populate with mock schemas + this.createdSchemas.set('TestCollection', mockExportedSchema); + this.createdSchemas.set('ComplexCollection', mockComplexSchema); + this.createdSchemas.set('NamedVectorCollection', mockNamedVectorSchema); + } + + public static use = async (version: string, httpPort: number, grpcPort: number) => { + const httpApp = express(); + httpApp.use(express.json()); + + // Meta endpoint required for client instantiation + httpApp.get('/v1/meta', (req, res) => res.send({ version })); + + const instance = new SchemaJsonMock(createServer(), null as any); + + // Export schema endpoint - GET /v1/schema/:className + httpApp.get('/v1/schema/:className', (req, res) => { + const className = req.params.className; + const schema = instance.createdSchemas.get(className); + + if (!schema) { + res.status(404).send({ error: `Collection ${className} not found` }); + return; + } + + res.send(schema); + }); + + // Create schema endpoint - POST /v1/schema + httpApp.post('/v1/schema', (req, res) => { + const schema: WeaviateClass = req.body; + + if (!schema.class) { + res.status(400).send({ error: 'Class name is required' }); + return; + } + + // Store the created schema + instance.createdSchemas.set(schema.class, schema); + + res.status(200).send(schema); + }); + + // gRPC health check required for client instantiation + const healthMockImpl: HealthServiceImplementation = { + check: (request: HealthCheckRequest): Promise => + Promise.resolve(HealthCheckResponse.create({ status: HealthCheckResponse_ServingStatus.SERVING })), + watch: vi.fn(), + }; + + instance.grpc.add(HealthDefinition, healthMockImpl); + + await instance.grpc.listen(`localhost:${grpcPort}`); + instance.http = await httpApp.listen(httpPort); + return instance; + }; + + public close = () => Promise.all([this.http.close(), this.grpc.shutdown()]); +} + +describe('Mock testing of exportToJson and createFromJson', () => { + let client: WeaviateClient; + let mock: SchemaJsonMock; + + beforeAll(async () => { + mock = await SchemaJsonMock.use('1.27.0', 8920, 8921); + client = await weaviate.connectToLocal({ port: 8920, grpcPort: 8921 }); + }); + + afterAll(() => mock.close()); + + describe('exportToJson', () => { + it('should export a simple collection schema to JSON', async () => { + const collectionName = 'TestCollection'; + const exportedSchema = await client.collections.exportToJson(collectionName); + + expect(exportedSchema).toBeDefined(); + expect(exportedSchema.class).toEqual(collectionName); + expect(exportedSchema.description).toEqual('A test collection for JSON export'); + expect(exportedSchema.properties).toBeDefined(); + expect(exportedSchema.properties?.length).toEqual(3); + }); + + it('should export collection with correct property types', async () => { + const exportedSchema = await client.collections.exportToJson('TestCollection'); + + const titleProp = exportedSchema.properties?.find((p) => p.name === 'title'); + expect(titleProp?.dataType).toEqual(['text']); + + const contentProp = exportedSchema.properties?.find((p) => p.name === 'content'); + expect(contentProp?.dataType).toEqual(['text']); + + const publishDateProp = exportedSchema.properties?.find((p) => p.name === 'publishDate'); + expect(publishDateProp?.dataType).toEqual(['date']); + }); + + it('should export a collection with complex configuration', async () => { + const exportedSchema = await client.collections.exportToJson('ComplexCollection'); + + expect(exportedSchema.class).toEqual('ComplexCollection'); + expect(exportedSchema.properties?.length).toEqual(2); + + const metadataProp = exportedSchema.properties?.find((p) => p.name === 'metadata'); + expect(metadataProp?.dataType).toEqual(['object']); + expect(metadataProp?.nestedProperties).toBeDefined(); + expect(metadataProp?.nestedProperties?.length).toEqual(2); + + expect(exportedSchema.invertedIndexConfig).toBeDefined(); + expect(exportedSchema.invertedIndexConfig?.indexTimestamps).toEqual(true); + expect(exportedSchema.invertedIndexConfig?.indexPropertyLength).toEqual(true); + }); + + it('should export collection with named vectors', async () => { + const exportedSchema = await client.collections.exportToJson('NamedVectorCollection'); + + expect(exportedSchema.vectorConfig).toBeDefined(); + expect(exportedSchema.vectorConfig?.custom_vector).toBeDefined(); + }); + }); + + describe('createFromJson', () => { + it('should create a collection from JSON schema', async () => { + const schemaJson: WeaviateClass = { + class: 'NewTestCollection', + description: 'A test collection created from JSON', + properties: [ + { + name: 'author', + dataType: ['text'], + }, + { + name: 'rating', + dataType: ['number'], + }, + ], + }; + + const collection = await client.collections.createFromJson(schemaJson); + + expect(collection).toBeDefined(); + }); + + it('should create a collection from minimal JSON schema', async () => { + const minimalSchema: WeaviateClass = { + class: 'MinimalCollection', + properties: [ + { + name: 'text', + dataType: ['text'], + }, + ], + }; + + const collection = await client.collections.createFromJson(minimalSchema); + + expect(collection).toBeDefined(); + }); + + it('should create a collection with complex nested properties', async () => { + const complexSchema: WeaviateClass = { + class: 'ComplexNestedCollection', + description: 'Collection with nested properties', + properties: [ + { + name: 'metadata', + dataType: ['object'], + nestedProperties: [ + { + name: 'author', + dataType: ['text'], + }, + { + name: 'tags', + dataType: ['text[]'], + }, + ], + }, + ], + }; + + const collection = await client.collections.createFromJson(complexSchema); + + expect(collection).toBeDefined(); + }); + + it('should create a collection with vector configuration', async () => { + const vectorSchema: WeaviateClass = { + class: 'VectorCollection', + properties: [ + { + name: 'content', + dataType: ['text'], + }, + ], + vectorConfig: { + named_vector: { + vectorIndexType: 'hnsw', + vectorizer: { + none: {}, + }, + }, + }, + }; + + const collection = await client.collections.createFromJson(vectorSchema); + + expect(collection).toBeDefined(); + }); + }); + + describe('round-trip export and import', () => { + it('should export and re-import a schema successfully', async () => { + // Export existing schema + const exportedSchema = await client.collections.exportToJson('TestCollection'); + + // Modify for re-import + exportedSchema.class = 'ReimportedCollection'; + exportedSchema.description = 'Reimported from exported schema'; + + // Create new collection from exported schema + const reimportedCollection = await client.collections.createFromJson(exportedSchema); + + expect(reimportedCollection).toBeDefined(); + }); + + it('should preserve complex configuration in round-trip', async () => { + const exportedSchema = await client.collections.exportToJson('ComplexCollection'); + + // Change name and re-import + exportedSchema.class = 'ReimportedComplexCollection'; + + const collection = await client.collections.createFromJson(exportedSchema); + + expect(collection).toBeDefined(); + }); + }); +}); From 44131ad2f320259baffc3ddace3e89cd3f864f81 Mon Sep 17 00:00:00 2001 From: Duda Nogueira Date: Wed, 11 Feb 2026 17:42:20 -0300 Subject: [PATCH 4/4] make mock test runnable. --- ...{schema-json.mock.test.ts => mock.test.ts} | 118 +++++++++--------- 1 file changed, 60 insertions(+), 58 deletions(-) rename test/collections/{schema-json.mock.test.ts => mock.test.ts} (76%) diff --git a/test/collections/schema-json.mock.test.ts b/test/collections/mock.test.ts similarity index 76% rename from test/collections/schema-json.mock.test.ts rename to test/collections/mock.test.ts index c3a7e1ac..9e57ab79 100644 --- a/test/collections/schema-json.mock.test.ts +++ b/test/collections/mock.test.ts @@ -1,7 +1,8 @@ import express from 'express'; import { Server as HttpServer } from 'http'; -import { Server as GrpcServer, createServer } from 'nice-grpc'; +import { createServer, Server as GrpcServer } from 'nice-grpc'; import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; + import weaviate, { WeaviateClient } from '../../src/index.js'; import { HealthCheckRequest, @@ -89,84 +90,85 @@ const mockNamedVectorSchema: WeaviateClass = { }, }; -class SchemaJsonMock { - private grpc: GrpcServer; - private http: HttpServer; - private createdSchemas: Map = new Map(); - - constructor(grpc: GrpcServer, http: HttpServer) { - this.grpc = grpc; - this.http = http; - // Pre-populate with mock schemas - this.createdSchemas.set('TestCollection', mockExportedSchema); - this.createdSchemas.set('ComplexCollection', mockComplexSchema); - this.createdSchemas.set('NamedVectorCollection', mockNamedVectorSchema); - } +const makeRestApp = (version: string, createdSchemas: Map) => { + const httpApp = express(); + httpApp.use(express.json()); - public static use = async (version: string, httpPort: number, grpcPort: number) => { - const httpApp = express(); - httpApp.use(express.json()); + // Meta endpoint required for client instantiation + httpApp.get('/v1/meta', (req, res) => res.send({ version })); - // Meta endpoint required for client instantiation - httpApp.get('/v1/meta', (req, res) => res.send({ version })); + // Export schema endpoint - GET /v1/schema/:className + httpApp.get('/v1/schema/:className', (req, res) => { + const className = req.params.className; + const schema = createdSchemas.get(className); - const instance = new SchemaJsonMock(createServer(), null as any); + if (!schema) { + res.status(404).send({ error: `Collection ${className} not found` }); + return; + } - // Export schema endpoint - GET /v1/schema/:className - httpApp.get('/v1/schema/:className', (req, res) => { - const className = req.params.className; - const schema = instance.createdSchemas.get(className); + res.send(schema); + }); - if (!schema) { - res.status(404).send({ error: `Collection ${className} not found` }); - return; - } + // Create schema endpoint - POST /v1/schema + httpApp.post('/v1/schema', (req, res) => { + const schema: WeaviateClass = req.body; - res.send(schema); - }); + if (!schema.class) { + res.status(400).send({ error: 'Class name is required' }); + return; + } - // Create schema endpoint - POST /v1/schema - httpApp.post('/v1/schema', (req, res) => { - const schema: WeaviateClass = req.body; + // Store the created schema + createdSchemas.set(schema.class, schema); - if (!schema.class) { - res.status(400).send({ error: 'Class name is required' }); - return; - } - - // Store the created schema - instance.createdSchemas.set(schema.class, schema); + res.status(200).send(schema); + }); - res.status(200).send(schema); - }); + return httpApp; +}; - // gRPC health check required for client instantiation - const healthMockImpl: HealthServiceImplementation = { - check: (request: HealthCheckRequest): Promise => - Promise.resolve(HealthCheckResponse.create({ status: HealthCheckResponse_ServingStatus.SERVING })), - watch: vi.fn(), - }; +const makeGrpcApp = () => { + // gRPC health check required for client instantiation + const healthMockImpl: HealthServiceImplementation = { + check: (request: HealthCheckRequest): Promise => + Promise.resolve(HealthCheckResponse.create({ status: HealthCheckResponse_ServingStatus.SERVING })), + watch: vi.fn(), + }; - instance.grpc.add(HealthDefinition, healthMockImpl); + const grpcApp = createServer(); + grpcApp.add(HealthDefinition, healthMockImpl); - await instance.grpc.listen(`localhost:${grpcPort}`); - instance.http = await httpApp.listen(httpPort); - return instance; - }; + return grpcApp; +}; - public close = () => Promise.all([this.http.close(), this.grpc.shutdown()]); -} +const makeMockServers = async (weaviateVersion: string, httpPort: number, grpcAddress: string) => { + // Pre-populate with mock schemas + const createdSchemas = new Map(); + createdSchemas.set('TestCollection', mockExportedSchema); + createdSchemas.set('ComplexCollection', mockComplexSchema); + createdSchemas.set('NamedVectorCollection', mockNamedVectorSchema); + + const rest = makeRestApp(weaviateVersion, createdSchemas); + const grpc = makeGrpcApp(); + const server = await rest.listen(httpPort); + await grpc.listen(grpcAddress); + return { rest: server, grpc }; +}; describe('Mock testing of exportToJson and createFromJson', () => { + let servers: { + rest: HttpServer; + grpc: GrpcServer; + }; let client: WeaviateClient; - let mock: SchemaJsonMock; beforeAll(async () => { - mock = await SchemaJsonMock.use('1.27.0', 8920, 8921); + servers = await makeMockServers('1.27.0', 8920, 'localhost:8921'); client = await weaviate.connectToLocal({ port: 8920, grpcPort: 8921 }); }); - afterAll(() => mock.close()); + afterAll(() => Promise.all([servers.rest.close(), servers.grpc.shutdown()])); describe('exportToJson', () => { it('should export a simple collection schema to JSON', async () => {