@wearesage/schema
Version:
A flexible schema definition and validation system for TypeScript with multi-database support
191 lines (160 loc) • 7.04 kB
text/typescript
import "reflect-metadata";
import {
MetadataRegistry,
SchemaBuilder,
} from "../core";
import { SchemaReflector } from "../core/SchemaReflector";
import { MongoDBAdapter, Collection, MongoIndex } from "../adapters";
// Import example entities from e-commerce domain
import { User } from "../examples/ecommerce/User";
import { Product } from "../examples/ecommerce/Product";
import { Category } from "../examples/ecommerce/Category";
import { Order } from "../examples/ecommerce/Order";
import { OrderItem } from "../examples/ecommerce/OrderItem";
import { Review } from "../examples/ecommerce/Review";
import { Cart } from "../examples/ecommerce/Cart";
import { CartItem } from "../examples/ecommerce/CartItem";
// Add MongoDB-specific decorators to some entities
Collection("users")(User);
MongoIndex({ email: 1 }, { unique: true })(User);
describe("Integration Tests", () => {
let registry: MetadataRegistry;
let builder: SchemaBuilder;
let reflector: SchemaReflector;
let adapter: MongoDBAdapter;
beforeEach(() => {
// Set up the components
registry = new MetadataRegistry();
builder = new SchemaBuilder(registry);
reflector = new SchemaReflector(registry);
// Register all entities
builder.registerEntities([User, Product, Category, Order, OrderItem, Review, Cart, CartItem]);
// Create adapter
adapter = new MongoDBAdapter(registry, "mongodb://localhost:27017/test");
});
test("full schema registration and validation workflow", () => {
// 1. Check entity registration
expect(registry.getEntityMetadata(User)).toBeDefined();
expect(registry.getEntityMetadata(Order)).toBeDefined();
expect(registry.getEntityMetadata(Product)).toBeDefined();
// 2. Check properties registration
expect(registry.getPropertyMetadata(User, "name")).toEqual({ required: true });
expect(registry.getPropertyMetadata(User, "email")).toEqual({ required: true, unique: true });
// 3. Check ID properties
const userIdProps = registry.getIdProperties(User);
expect(userIdProps).toBeDefined();
expect(userIdProps?.has("id")).toBe(true);
// 4. Check relationships
const userOrdersRel = registry.getRelationshipMetadata(User, "orders");
expect(userOrdersRel).toBeDefined();
expect(userOrdersRel?.cardinality).toBe("many");
expect(userOrdersRel?.target).toBe(Order);
const orderUserRel = registry.getRelationshipMetadata(Order, "user");
expect(orderUserRel).toBeDefined();
expect(orderUserRel?.cardinality).toBe("one");
expect(orderUserRel?.name).toBe("BELONGS_TO");
// 5. Get complete schema for an entity
const userSchema = reflector.getEntitySchema(User);
expect(userSchema.name).toBeDefined();
expect(Object.keys(userSchema.properties)).toContain("name");
expect(Object.keys(userSchema.properties)).toContain("email");
expect(Object.keys(userSchema.relationships)).toContain("orders");
expect(Object.keys(userSchema.relationships)).toContain("reviews");
// 6. Entity validation
const validUser = new User();
validUser.id = "u123";
validUser.name = "John Doe";
validUser.email = "john@example.com";
const validationResult = reflector.validateEntity(validUser);
expect(validationResult.valid).toBe(true);
// 7. Invalid entity
const invalidUser = new User();
invalidUser.id = "u456";
// Missing required name and email
const invalidValidationResult = reflector.validateEntity(invalidUser);
expect(invalidValidationResult.valid).toBe(false);
expect(invalidValidationResult.errors).toContain("Required property 'name' is missing");
expect(invalidValidationResult.errors).toContain("Required property 'email' is missing");
});
test("MongoDB specific functionality", () => {
// Test MongoDB collection name lookup
const getCollectionName = (adapter as any).getCollectionName.bind(adapter);
expect(getCollectionName(User)).toBe("users"); // Uses @Collection decorator
expect(getCollectionName(Product)).toBe("products"); // Uses default pluralization
// Check MongoDB index registration
const emailIndex = Reflect.getMetadata("mongodb:indexes", User) || [];
expect(emailIndex.length).toBeGreaterThan(0);
expect(emailIndex[0].keys).toEqual({ email: 1 });
expect(emailIndex[0].options).toEqual({ unique: true });
});
test("end-to-end entity example", () => {
// Create complete object graph
const user = new User();
user.id = "user001";
user.name = "Jane Smith";
user.email = "jane@example.com";
const product1 = new Product();
product1.id = "prod001";
product1.name = "Widget A";
product1.price = 19.99;
const product2 = new Product();
product2.id = "prod002";
product2.name = "Widget B";
product2.price = 29.99;
const category = new Category();
category.id = "cat001";
category.name = "Widgets";
category.products.push(product1, product2);
product1.categories.push(category);
product2.categories.push(category);
const order = new Order();
order.id = "ord001";
order.orderNumber = "2025-0001";
order.createdAt = new Date();
order.user = user;
user.orders.push(order);
const item1 = new OrderItem();
item1.id = "item001";
item1.quantity = 2;
item1.unitPrice = product1.price;
item1.product = product1;
item1.order = order;
order.items.push(item1);
const item2 = new OrderItem();
item2.id = "item002";
item2.quantity = 1;
item2.unitPrice = product2.price;
item2.product = product2;
item2.order = order;
order.items.push(item2);
const review = new Review();
review.id = "rev001";
review.rating = 5;
review.content = "Great product!";
review.author = user;
review.product = product1;
user.reviews.push(review);
product1.reviews.push(review);
// Validate all entities
expect(reflector.validateEntity(user).valid).toBe(true);
expect(reflector.validateEntity(product1).valid).toBe(true);
expect(reflector.validateEntity(product2).valid).toBe(true);
expect(reflector.validateEntity(category).valid).toBe(true);
expect(reflector.validateEntity(order).valid).toBe(true);
expect(reflector.validateEntity(item1).valid).toBe(true);
expect(reflector.validateEntity(item2).valid).toBe(true);
expect(reflector.validateEntity(review).valid).toBe(true);
// Verify relationships were properly set up
expect(order.user).toBe(user);
expect(user.orders[0]).toBe(order);
expect(item1.order).toBe(order);
expect(order.items).toContain(item1);
expect(item1.product).toBe(product1);
expect(product1.categories[0]).toBe(category);
expect(category.products).toContain(product1);
expect(review.author).toBe(user);
expect(user.reviews[0]).toBe(review);
expect(review.product).toBe(product1);
expect(product1.reviews[0]).toBe(review);
});
});