alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
158 lines (135 loc) • 4.93 kB
text/typescript
import { Alepha, z } from "alepha";
import { describe, expect, it } from "vitest";
import { $entity, $repository, db } from "../core/index.ts";
import { AlephaOrmPostgres } from "../postgres/index.ts";
describe("db.ref - automatic onDelete behavior", () => {
const categories = $entity({
name: "categories",
schema: z.object({
id: db.identityPrimaryKey(),
__v: db.version(),
name: z.text(),
}),
});
const products = $entity({
name: "products",
schema: z.object({
id: db.identityPrimaryKey(),
__v: db.version(),
name: z.text(),
// Optional reference - should automatically set onDelete: "set null"
categoryId: db.ref(z.integer().optional(), () => categories.cols.id),
}),
});
const orders = $entity({
name: "orders",
schema: z.object({
id: db.identityPrimaryKey(),
__v: db.version(),
orderNumber: z.text(),
// Required reference - should automatically set onDelete: "cascade"
productId: db.ref(z.integer(), () => products.cols.id),
}),
});
class App {
categories = $repository(categories);
products = $repository(products);
orders = $repository(orders);
}
it("should cascade delete for required references", async () => {
const alepha = Alepha.create().with(AlephaOrmPostgres);
const app = alepha.inject(App);
await alepha.start();
// Create a product with category
const category = await app.categories.create({ name: "Electronics" });
const product = await app.products.create({
name: "Laptop",
categoryId: category.id,
});
const order = await app.orders.create({
orderNumber: "ORD-001",
productId: product.id,
});
// Verify data exists
expect(await app.products.findMany()).toEqual([
{ id: product.id, name: "Laptop", categoryId: category.id, __v: 0 },
]);
expect(await app.orders.findMany()).toEqual([
{ id: order.id, orderNumber: "ORD-001", productId: product.id, __v: 0 },
]);
// Delete product - order should cascade delete (required reference)
await app.products.deleteById(product.id);
// Order should be deleted due to cascade
expect(await app.products.findMany()).toEqual([]);
expect(await app.orders.findMany()).toEqual([]);
});
it("should set null for optional references", async () => {
const alepha = Alepha.create().with(AlephaOrmPostgres);
const app = alepha.inject(App);
await alepha.start();
// Create product with category
const category = await app.categories.create({ name: "Electronics" });
const product = await app.products.create({
name: "Laptop",
categoryId: category.id,
});
// Verify product has category
expect(await app.products.findMany()).toEqual([
{ id: product.id, name: "Laptop", categoryId: category.id, __v: 0 },
]);
// Delete category - product should remain but categoryId set to null
await app.categories.deleteById(category.id);
// Product should still exist with null categoryId (optional reference)
expect(await app.categories.findMany()).toEqual([]);
expect(await app.products.findMany()).toEqual([
{ id: product.id, name: "Laptop", __v: 0 },
]);
});
it("should allow explicit actions to override auto behavior", async () => {
// Test that explicit actions are not overridden by auto behavior
const customCategories = $entity({
name: "custom_categories",
schema: z.object({
id: db.identityPrimaryKey(),
__v: db.version(),
name: z.text(),
}),
});
const customProducts = $entity({
name: "custom_products",
schema: z.object({
id: db.identityPrimaryKey(),
__v: db.version(),
name: z.text(),
// Optional reference but explicitly set to cascade
categoryId: db.ref(
z.integer().optional(),
() => customCategories.cols.id,
{
onDelete: "cascade",
},
),
}),
});
class CustomApp {
categories = $repository(customCategories);
products = $repository(customProducts);
}
const alepha = Alepha.create().with(AlephaOrmPostgres);
const app = alepha.inject(CustomApp);
await alepha.start();
const category = await app.categories.create({ name: "Books" });
const product = await app.products.create({
name: "Novel",
categoryId: category.id,
});
expect(await app.products.findMany()).toEqual([
{ id: product.id, name: "Novel", categoryId: category.id, __v: 0 },
]);
// Delete category - product should cascade delete even though reference is optional
await app.categories.deleteById(category.id);
// Product should be deleted due to explicit cascade
expect(await app.categories.findMany()).toEqual([]);
expect(await app.products.findMany()).toEqual([]);
});
});