UNPKG

@wearesage/schema

Version:

A flexible schema definition and validation system for TypeScript with multi-database support

219 lines (169 loc) 9.5 kB
import { MetadataRegistry, SchemaBuilder } from "../../core"; import { User } from "../../examples/blog/User"; import { Post } from "../../examples/blog/Post"; import { Tag } from "../../examples/blog/Tag"; import "reflect-metadata"; describe("SchemaBuilder", () => { test("should register entities with their properties and relationships", () => { // Create registry and builder const registry = new MetadataRegistry(); const builder = new SchemaBuilder(registry); // Register entities builder.registerEntities([User, Post]); // Verify entity registration expect(registry.getEntityMetadata(User)).toBeDefined(); expect(registry.getEntityMetadata(Post)).toBeDefined(); // Verify property registration expect(registry.getPropertyMetadata(User, "name")).toEqual({ required: true }); expect(registry.getPropertyMetadata(User, "email")).toEqual({ required: true, unique: true }); expect(registry.getPropertyMetadata(Post, "title")).toEqual({ required: true }); expect(registry.getPropertyMetadata(Post, "content")).toEqual({ required: true }); // Verify ID registration expect(registry.getIdProperties(User)?.has("id")).toBe(true); expect(registry.getIdProperties(Post)?.has("id")).toBe(true); // Verify relationship registration const userPostsRel = registry.getRelationshipMetadata(User, "posts"); expect(userPostsRel).toBeDefined(); expect(userPostsRel?.cardinality).toBe("many"); expect(userPostsRel?.inverse).toBe("author"); expect(userPostsRel?.name).toBe("AUTHORED"); const postAuthorRel = registry.getRelationshipMetadata(Post, "author"); expect(postAuthorRel).toBeDefined(); expect(postAuthorRel?.cardinality).toBe("one"); expect(postAuthorRel?.inverse).toBe("posts"); expect(postAuthorRel?.name).toBe("AUTHORED"); }); test("should allow registering entities individually", () => { // Create registry and builder const registry = new MetadataRegistry(); const builder = new SchemaBuilder(registry); // Register just one entity builder.registerEntity(User); // Verify only User is registered expect(registry.getEntityMetadata(User)).toBeDefined(); expect(registry.getEntityMetadata(Post)).toBeUndefined(); // Now register Post builder.registerEntity(Post); expect(registry.getEntityMetadata(Post)).toBeDefined(); }); test("should handle decorators with reflection metadata correctly", () => { // Create registry and builder const registry = new MetadataRegistry(); const builder = new SchemaBuilder(registry); // Register entities builder.registerEntities([User, Post]); // Verify that ID properties are also registered as properties const idProp = registry.getPropertyMetadata(User, "id"); expect(idProp).toBeDefined(); expect(idProp?.required).toBe(true); expect(idProp?.unique).toBe(true); }); test("getRegistry should return the underlying registry instance", () => { // Create registry and builder const registry = new MetadataRegistry(); const builder = new SchemaBuilder(registry); // Verify the registry instance is returned correctly expect(builder.getRegistry()).toBe(registry); expect(builder.getRegistry()).toBeInstanceOf(MetadataRegistry); }); test("should handle entities with missing metadata", () => { // Create registry and builder const registry = new MetadataRegistry(); const builder = new SchemaBuilder(registry); // Create a mock class without proper decorator metadata class MockEntityNoMetadata {} // Register the entity - should not throw expect(() => builder.registerEntity(MockEntityNoMetadata)).not.toThrow(); // Entity should be registered but with default empty options const metadata = registry.getEntityMetadata(MockEntityNoMetadata); expect(metadata).toEqual({}); }); test("should handle entities with missing property metadata", () => { // Create registry and builder const registry = new MetadataRegistry(); const builder = new SchemaBuilder(registry); // Create a mock class with entity metadata but no property metadata class MockEntityNoProps {} // Set only entity metadata, but not properties array Reflect.defineMetadata("entity:options", { name: "mock_entity" }, MockEntityNoProps); // Register without throwing expect(() => builder.registerEntity(MockEntityNoProps)).not.toThrow(); // Should have entity metadata but no properties expect(registry.getEntityMetadata(MockEntityNoProps)).toEqual({ name: "mock_entity" }); }); test("should handle entities with missing relationship metadata", () => { // Create registry and builder const registry = new MetadataRegistry(); const builder = new SchemaBuilder(registry); // Mock class with entity and property metadata but no relationship metadata class MockEntityNoRels {} // Set entity and property metadata but not relationships array Reflect.defineMetadata("entity:options", { name: "mock_entity" }, MockEntityNoRels); Reflect.defineMetadata("entity:properties", ["name"], MockEntityNoRels); Reflect.defineMetadata("property:options", { required: true }, MockEntityNoRels.prototype, "name"); // Register without throwing expect(() => builder.registerEntity(MockEntityNoRels)).not.toThrow(); // Should have entity metadata and property metadata but no relationships expect(registry.getEntityMetadata(MockEntityNoRels)).toEqual({ name: "mock_entity" }); expect(registry.getPropertyMetadata(MockEntityNoRels, "name")).toEqual({ required: true }); }); test("should handle properties with missing options metadata", () => { // Create registry and builder const registry = new MetadataRegistry(); const builder = new SchemaBuilder(registry); // Create a mock class with property keys but no options metadata class MockEntityNoPropertyOptions {} // Set entity metadata and properties array, but no property:options metadata Reflect.defineMetadata("entity:options", { name: "mock_entity" }, MockEntityNoPropertyOptions); Reflect.defineMetadata("entity:properties", ["propWithNoOptions"], MockEntityNoPropertyOptions); // Register without throwing expect(() => builder.registerEntity(MockEntityNoPropertyOptions)).not.toThrow(); // Property should be registered with empty options expect(registry.getPropertyMetadata(MockEntityNoPropertyOptions, "propWithNoOptions")).toEqual({}); }); test("should handle relationships with missing options or type metadata", () => { // Create registry and builder const registry = new MetadataRegistry(); const builder = new SchemaBuilder(registry); // Create a mock class with relationship keys but missing metadata class MockEntityIncompleteRel {} // Set entity metadata and relationships array, but no relationship metadata Reflect.defineMetadata("entity:options", { name: "mock_entity" }, MockEntityIncompleteRel); Reflect.defineMetadata("entity:relationships", ["relWithNoOptions"], MockEntityIncompleteRel); // Register without throwing expect(() => builder.registerEntity(MockEntityIncompleteRel)).not.toThrow(); // Relationship should be registered with default values const relMetadata = registry.getRelationshipMetadata(MockEntityIncompleteRel, "relWithNoOptions"); expect(relMetadata).toBeDefined(); expect(relMetadata?.cardinality).toBe("one"); // Default cardinality when type is missing }); test("should correctly handle different relationship types for cardinality", () => { // Create registry and builder const registry = new MetadataRegistry(); const builder = new SchemaBuilder(registry); // Create mock classes for testing relationship cardinality class MockEntity {} // Set up different relationship types Reflect.defineMetadata("entity:options", {}, MockEntity); Reflect.defineMetadata("entity:relationships", ["oneToOne", "oneToMany", "manyToOne", "manyToMany"], MockEntity); // Set relationship types Reflect.defineMetadata("relationship:options", {}, MockEntity.prototype, "oneToOne"); Reflect.defineMetadata("relationship:type", "one-to-one", MockEntity.prototype, "oneToOne"); Reflect.defineMetadata("relationship:options", {}, MockEntity.prototype, "oneToMany"); Reflect.defineMetadata("relationship:type", "one-to-many", MockEntity.prototype, "oneToMany"); Reflect.defineMetadata("relationship:options", {}, MockEntity.prototype, "manyToOne"); Reflect.defineMetadata("relationship:type", "many-to-one", MockEntity.prototype, "manyToOne"); Reflect.defineMetadata("relationship:options", {}, MockEntity.prototype, "manyToMany"); Reflect.defineMetadata("relationship:type", "many-to-many", MockEntity.prototype, "manyToMany"); // Register without throwing builder.registerEntity(MockEntity); // Check cardinality for each relationship type expect(registry.getRelationshipMetadata(MockEntity, "oneToOne")?.cardinality).toBe("one"); expect(registry.getRelationshipMetadata(MockEntity, "oneToMany")?.cardinality).toBe("many"); expect(registry.getRelationshipMetadata(MockEntity, "manyToOne")?.cardinality).toBe("one"); expect(registry.getRelationshipMetadata(MockEntity, "manyToMany")?.cardinality).toBe("many"); }); });