@wearesage/schema
Version:
A flexible schema definition and validation system for TypeScript with multi-database support
308 lines (251 loc) • 8.5 kB
text/typescript
import "reflect-metadata";
import { UniversalEntityService, EntityContext } from "../../services/UniversalEntityService";
import { Entity, Property, Id, Auth } from "../../core/decorators";
import { MetadataRegistry } from "../../core/MetadataRegistry";
// Test entities
()
({ permissions: ['user'] })
class TestUser {
()
id: string;
({ required: true })
name: string;
({ required: true })
email: string;
()
createdAt: Date;
()
updatedAt: Date;
}
()
({ roles: ['admin'] })
class TestSpace {
()
id: string;
({ required: true })
name: string;
()
description: string;
({ required: true })
spaceType: string;
({ required: true })
visibility: string;
({ required: true })
ownedByType: string;
({ required: true })
ownedById: string;
()
status: string;
()
metadata: any;
()
createdAt: Date;
()
updatedAt: Date;
}
describe("UniversalEntityService", () => {
let service: UniversalEntityService;
let userContext: EntityContext;
let adminContext: EntityContext;
beforeEach(() => {
// Get fresh instance for each test
service = UniversalEntityService.getInstance();
// Register test entities
service.registerEntities([TestUser, TestSpace]);
// Create test contexts
userContext = {
user: {
id: 'user123',
username: 'testuser',
email: 'test@example.com',
role: 0,
permissions: ['user']
}
};
adminContext = {
user: {
id: 'admin123',
username: 'admin',
email: 'admin@example.com',
role: 999,
permissions: ['user', 'admin']
}
};
});
afterAll(async () => {
// Close any open database connections
if ((service as any).adapter && (service as any).adapter.close) {
await (service as any).adapter.close();
}
});
describe("Entity Registration", () => {
test("should register entities successfully", () => {
// This should not throw
expect(() => {
service.registerEntities([TestUser, TestSpace]);
}).not.toThrow();
});
test("should extract metadata from registered entities", () => {
// This is tested implicitly by the CRUD operations below
expect(true).toBe(true);
});
});
describe("Permission Checks", () => {
test("should allow user access to TestUser (user permission)", async () => {
const userData = {
name: 'Test User',
email: 'test@example.com'
};
// This should not throw
await expect(
service.create(TestUser, userData, userContext)
).resolves.toBeDefined();
});
test("should deny user access to TestSpace (admin role required)", async () => {
const spaceData = {
name: 'Test Space',
spaceType: 'project',
visibility: 'private',
ownedByType: 'user',
ownedById: 'user123'
};
await expect(
service.create(TestSpace, spaceData, userContext)
).rejects.toThrow('Insufficient role permissions');
});
test("should allow admin access to TestSpace", async () => {
const spaceData = {
name: 'Test Space',
spaceType: 'project',
visibility: 'private',
ownedByType: 'user',
ownedById: 'admin123'
};
await expect(
service.create(TestSpace, spaceData, adminContext)
).resolves.toBeDefined();
});
});
describe("Validation", () => {
test("should enforce required fields", async () => {
const invalidUserData = {
name: 'Test User'
// Missing required email field
};
await expect(
service.create(TestUser, invalidUserData, userContext)
).rejects.toThrow("Required field 'email' is missing");
});
test("should allow valid data", async () => {
const validUserData = {
name: 'Test User',
email: 'test@example.com'
};
const user = await service.create(TestUser, validUserData, userContext);
expect(user.name).toBe('Test User');
expect(user.email).toBe('test@example.com');
expect(user.id).toBeDefined();
expect(user.createdAt).toBeDefined();
expect(user.updatedAt).toBeDefined();
});
});
describe("CRUD Operations", () => {
test("should create entity with auto-generated fields", async () => {
const userData = {
name: 'Test User',
email: 'test@example.com'
};
const user = await service.create(TestUser, userData, userContext);
expect(user.id).toBeDefined();
expect(user.id).toMatch(/^entity_/);
expect(user.createdAt).toBeInstanceOf(Date);
expect(user.updatedAt).toBeInstanceOf(Date);
expect(user.name).toBe('Test User');
expect(user.email).toBe('test@example.com');
});
test("should find entity by ID", async () => {
const userData = {
name: 'Test User',
email: 'test@example.com'
};
const createdUser = await service.create(TestUser, userData, userContext);
const foundUser = await service.findById(TestUser, createdUser.id, userContext);
expect(foundUser).toBeDefined();
expect(foundUser?.id).toBe(createdUser.id);
expect(foundUser?.name).toBe('Test User');
});
test("should find all entities", async () => {
const userData1 = { name: 'User 1', email: 'user1@example.com' };
const userData2 = { name: 'User 2', email: 'user2@example.com' };
await service.create(TestUser, userData1, userContext);
await service.create(TestUser, userData2, userContext);
const allUsers = await service.findAll(TestUser, {}, userContext);
expect(allUsers.length).toBeGreaterThanOrEqual(2);
});
test("should update entity", async () => {
const userData = {
name: 'Original Name',
email: 'test@example.com'
};
const createdUser = await service.create(TestUser, userData, userContext);
const updatedUser = await service.update(
TestUser,
createdUser.id,
{ name: 'Updated Name' },
userContext
);
expect(updatedUser?.name).toBe('Updated Name');
expect(updatedUser?.email).toBe('test@example.com'); // Should remain unchanged
expect(updatedUser?.updatedAt).toBeInstanceOf(Date);
});
test("should delete entity", async () => {
const userData = {
name: 'Test User',
email: 'test@example.com'
};
const createdUser = await service.create(TestUser, userData, userContext);
const deleteResult = await service.delete(TestUser, createdUser.id, userContext);
expect(deleteResult).toBe(true);
const foundUser = await service.findById(TestUser, createdUser.id, userContext);
expect(foundUser).toBeNull();
});
});
describe("Entity Instance Creation", () => {
test("should create proper entity instances", async () => {
const userData = {
name: 'Test User',
email: 'test@example.com'
};
const user = await service.create(TestUser, userData, userContext);
// Should be an instance of the TestUser class
expect(user).toBeInstanceOf(TestUser);
expect(user.constructor.name).toBe('TestUser');
});
});
describe("Error Handling", () => {
test("should handle non-existent entity IDs gracefully", async () => {
const foundUser = await service.findById(TestUser, 'non-existent-id', userContext);
expect(foundUser).toBeNull();
});
test("should handle update of non-existent entity", async () => {
const updatedUser = await service.update(
TestUser,
'non-existent-id',
{ name: 'New Name' },
userContext
);
expect(updatedUser).toBeNull();
});
test("should handle delete of non-existent entity", async () => {
const deleteResult = await service.delete(TestUser, 'non-existent-id', userContext);
expect(deleteResult).toBe(false);
});
});
});
describe("UniversalEntityService Integration", () => {
test("should work with real entities from API project", () => {
// This test would import actual entities from the API project
// and verify they work with the Universal Entity Service
expect(true).toBe(true); // Placeholder
});
});