689 lines
19 KiB
TypeScript
689 lines
19 KiB
TypeScript
import { beforeEach, describe, expect, it } from "bun:test";
|
|
import { eq } from "drizzle-orm";
|
|
import * as schema from "../../src/db/schema.ts";
|
|
import {
|
|
globalItems,
|
|
globalItemTags,
|
|
items,
|
|
tags,
|
|
} from "../../src/db/schema.ts";
|
|
import { seedGlobalItems } from "../../src/db/seed-global-items.ts";
|
|
import {
|
|
bulkUpsertGlobalItems,
|
|
deleteGlobalItem,
|
|
getGlobalItemWithOwnerCount,
|
|
listGlobalItemsForAdmin,
|
|
searchGlobalItems,
|
|
updateGlobalItemById,
|
|
upsertGlobalItem,
|
|
} from "../../src/server/services/global-item.service.ts";
|
|
import { createTestDb } from "../helpers/db.ts";
|
|
|
|
type TestDb = Awaited<ReturnType<typeof createTestDb>>;
|
|
|
|
async function insertManufacturer(
|
|
db: TestDb["db"],
|
|
name = "Apidura",
|
|
slug = "apidura",
|
|
) {
|
|
const [row] = await db
|
|
.insert(schema.manufacturers)
|
|
.values({ name, slug, website: `https://${slug}.com` })
|
|
.returning();
|
|
return row!;
|
|
}
|
|
|
|
async function insertGlobalItem(
|
|
db: TestDb["db"],
|
|
data: {
|
|
manufacturerId: number;
|
|
model: string;
|
|
category?: string;
|
|
weightGrams?: number;
|
|
priceCents?: number;
|
|
},
|
|
) {
|
|
const [row] = await db
|
|
.insert(globalItems)
|
|
.values({
|
|
manufacturerId: data.manufacturerId,
|
|
model: data.model,
|
|
category: data.category ?? null,
|
|
weightGrams: data.weightGrams ?? null,
|
|
priceCents: data.priceCents ?? null,
|
|
})
|
|
.returning();
|
|
return row!;
|
|
}
|
|
|
|
async function insertItem(
|
|
db: TestDb["db"],
|
|
name: string,
|
|
userId: number,
|
|
opts?: { globalItemId?: number },
|
|
) {
|
|
const [row] = await db
|
|
.insert(items)
|
|
.values({ name, categoryId: 1, userId, globalItemId: opts?.globalItemId })
|
|
.returning();
|
|
return row;
|
|
}
|
|
|
|
async function insertTag(db: TestDb["db"], name: string) {
|
|
const [row] = await db.insert(tags).values({ name }).returning();
|
|
return row;
|
|
}
|
|
|
|
async function tagGlobalItem(
|
|
db: TestDb["db"],
|
|
globalItemId: number,
|
|
tagId: number,
|
|
) {
|
|
await db.insert(globalItemTags).values({ globalItemId, tagId });
|
|
}
|
|
|
|
describe("Global Item Service", () => {
|
|
let db: TestDb["db"];
|
|
let userId: number;
|
|
|
|
beforeEach(async () => {
|
|
const testDb = await createTestDb();
|
|
db = testDb.db;
|
|
userId = testDb.userId;
|
|
});
|
|
|
|
describe("searchGlobalItems", () => {
|
|
it("returns all global items when no query provided", async () => {
|
|
const m1 = await insertManufacturer(
|
|
db,
|
|
"Revelate Designs",
|
|
"revelate-designs",
|
|
);
|
|
const m2 = await insertManufacturer(db, "Apidura", "apidura");
|
|
await insertGlobalItem(db, {
|
|
manufacturerId: m1.id,
|
|
model: "Terrapin System",
|
|
});
|
|
await insertGlobalItem(db, {
|
|
manufacturerId: m2.id,
|
|
model: "Handlebar Pack",
|
|
});
|
|
|
|
const results = await searchGlobalItems(db);
|
|
expect(results).toHaveLength(2);
|
|
});
|
|
|
|
it("returns items matching brand (case-insensitive)", async () => {
|
|
const m1 = await insertManufacturer(
|
|
db,
|
|
"Revelate Designs",
|
|
"revelate-designs",
|
|
);
|
|
const m2 = await insertManufacturer(db, "Apidura", "apidura");
|
|
await insertGlobalItem(db, {
|
|
manufacturerId: m1.id,
|
|
model: "Terrapin System",
|
|
});
|
|
await insertGlobalItem(db, {
|
|
manufacturerId: m2.id,
|
|
model: "Handlebar Pack",
|
|
});
|
|
|
|
const results = await searchGlobalItems(db, "revelate");
|
|
expect(results).toHaveLength(1);
|
|
expect(results[0].brand).toBe("Revelate Designs");
|
|
});
|
|
|
|
it("returns items matching model (case-insensitive)", async () => {
|
|
const m1 = await insertManufacturer(
|
|
db,
|
|
"Revelate Designs",
|
|
"revelate-designs",
|
|
);
|
|
const m2 = await insertManufacturer(db, "Apidura", "apidura");
|
|
await insertGlobalItem(db, {
|
|
manufacturerId: m1.id,
|
|
model: "Terrapin System",
|
|
});
|
|
await insertGlobalItem(db, {
|
|
manufacturerId: m2.id,
|
|
model: "Handlebar Pack",
|
|
});
|
|
|
|
const results = await searchGlobalItems(db, "HANDLEBAR");
|
|
expect(results).toHaveLength(1);
|
|
expect(results[0].model).toBe("Handlebar Pack");
|
|
});
|
|
|
|
it("does not match everything with wildcard chars", async () => {
|
|
const m1 = await insertManufacturer(
|
|
db,
|
|
"Revelate Designs",
|
|
"revelate-designs",
|
|
);
|
|
const m2 = await insertManufacturer(db, "Apidura", "apidura");
|
|
await insertGlobalItem(db, {
|
|
manufacturerId: m1.id,
|
|
model: "Terrapin System",
|
|
});
|
|
await insertGlobalItem(db, {
|
|
manufacturerId: m2.id,
|
|
model: "Handlebar Pack",
|
|
});
|
|
|
|
const results = await searchGlobalItems(db, "100%");
|
|
expect(results).toHaveLength(0);
|
|
});
|
|
|
|
it("returns all items when no tags provided", async () => {
|
|
const m1 = await insertManufacturer(
|
|
db,
|
|
"Revelate Designs",
|
|
"revelate-designs",
|
|
);
|
|
const m2 = await insertManufacturer(db, "Apidura", "apidura");
|
|
await insertGlobalItem(db, {
|
|
manufacturerId: m1.id,
|
|
model: "Terrapin System",
|
|
});
|
|
await insertGlobalItem(db, {
|
|
manufacturerId: m2.id,
|
|
model: "Handlebar Pack",
|
|
});
|
|
|
|
const results = await searchGlobalItems(db, undefined, undefined);
|
|
expect(results).toHaveLength(2);
|
|
});
|
|
|
|
it("filters by single tag", async () => {
|
|
const m1 = await insertManufacturer(
|
|
db,
|
|
"Revelate Designs",
|
|
"revelate-designs",
|
|
);
|
|
const m2 = await insertManufacturer(db, "Apidura", "apidura");
|
|
const gi1 = await insertGlobalItem(db, {
|
|
manufacturerId: m1.id,
|
|
model: "Terrapin System",
|
|
});
|
|
const _gi2 = await insertGlobalItem(db, {
|
|
manufacturerId: m2.id,
|
|
model: "Handlebar Pack",
|
|
});
|
|
|
|
const tag = await insertTag(db, "ultralight");
|
|
await tagGlobalItem(db, gi1.id, tag.id);
|
|
|
|
const results = await searchGlobalItems(db, undefined, ["ultralight"]);
|
|
expect(results).toHaveLength(1);
|
|
expect(results[0].brand).toBe("Revelate Designs");
|
|
});
|
|
|
|
it("filters by multiple tags with AND logic", async () => {
|
|
const m1 = await insertManufacturer(
|
|
db,
|
|
"Revelate Designs",
|
|
"revelate-designs",
|
|
);
|
|
const m2 = await insertManufacturer(db, "Apidura", "apidura");
|
|
const gi1 = await insertGlobalItem(db, {
|
|
manufacturerId: m1.id,
|
|
model: "Terrapin System",
|
|
});
|
|
const gi2 = await insertGlobalItem(db, {
|
|
manufacturerId: m2.id,
|
|
model: "Handlebar Pack",
|
|
});
|
|
|
|
const tagUL = await insertTag(db, "ultralight");
|
|
const tagBP = await insertTag(db, "bikepacking");
|
|
// gi1 has both tags
|
|
await tagGlobalItem(db, gi1.id, tagUL.id);
|
|
await tagGlobalItem(db, gi1.id, tagBP.id);
|
|
// gi2 has only bikepacking
|
|
await tagGlobalItem(db, gi2.id, tagBP.id);
|
|
|
|
const results = await searchGlobalItems(db, undefined, [
|
|
"ultralight",
|
|
"bikepacking",
|
|
]);
|
|
expect(results).toHaveLength(1);
|
|
expect(results[0].brand).toBe("Revelate Designs");
|
|
});
|
|
|
|
it("combines text search and tag filtering", async () => {
|
|
const m = await insertManufacturer(
|
|
db,
|
|
"Revelate Designs",
|
|
"revelate-designs",
|
|
);
|
|
const gi1 = await insertGlobalItem(db, {
|
|
manufacturerId: m.id,
|
|
model: "Terrapin System",
|
|
});
|
|
const gi2 = await insertGlobalItem(db, {
|
|
manufacturerId: m.id,
|
|
model: "Spinelock",
|
|
});
|
|
|
|
const tag = await insertTag(db, "bikepacking");
|
|
await tagGlobalItem(db, gi1.id, tag.id);
|
|
await tagGlobalItem(db, gi2.id, tag.id);
|
|
|
|
// Both tagged bikepacking, but only one matches "terrapin"
|
|
const results = await searchGlobalItems(db, "terrapin", ["bikepacking"]);
|
|
expect(results).toHaveLength(1);
|
|
expect(results[0].model).toBe("Terrapin System");
|
|
});
|
|
});
|
|
|
|
describe("getGlobalItemWithOwnerCount", () => {
|
|
it("returns item with ownerCount 0 when no items reference it", async () => {
|
|
const m = await insertManufacturer(db, "MSR", "msr");
|
|
const gi = await insertGlobalItem(db, {
|
|
manufacturerId: m.id,
|
|
model: "PocketRocket 2",
|
|
});
|
|
|
|
const result = await getGlobalItemWithOwnerCount(db, gi.id);
|
|
expect(result).not.toBeNull();
|
|
expect(result!.ownerCount).toBe(0);
|
|
expect(result!.brand).toBe("MSR");
|
|
});
|
|
|
|
it("returns ownerCount matching number of items with globalItemId", async () => {
|
|
const m = await insertManufacturer(db, "MSR", "msr");
|
|
const gi = await insertGlobalItem(db, {
|
|
manufacturerId: m.id,
|
|
model: "PocketRocket 2",
|
|
});
|
|
|
|
await insertItem(db, "My Stove", userId, { globalItemId: gi.id });
|
|
await insertItem(db, "Another Stove", userId, {
|
|
globalItemId: gi.id,
|
|
});
|
|
|
|
const result = await getGlobalItemWithOwnerCount(db, gi.id);
|
|
expect(result).not.toBeNull();
|
|
expect(result!.ownerCount).toBe(2);
|
|
});
|
|
|
|
it("returns null for non-existent id", async () => {
|
|
const result = await getGlobalItemWithOwnerCount(db, 9999);
|
|
expect(result).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("seedGlobalItems", () => {
|
|
it("inserts seed data on first call", async () => {
|
|
await seedGlobalItems(db);
|
|
const all = await db.select().from(globalItems);
|
|
expect(all.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it("is idempotent on second call", async () => {
|
|
await seedGlobalItems(db);
|
|
const countAfterFirst = (await db.select().from(globalItems)).length;
|
|
|
|
await seedGlobalItems(db);
|
|
const countAfterSecond = (await db.select().from(globalItems)).length;
|
|
|
|
expect(countAfterSecond).toBe(countAfterFirst);
|
|
});
|
|
});
|
|
|
|
describe("upsert operations", () => {
|
|
it("upsertGlobalItem creates new item and returns { item, created: true }", async () => {
|
|
await insertManufacturer(db, "Revelate Designs", "revelate-designs");
|
|
const result = await upsertGlobalItem(db, {
|
|
manufacturerSlug: "revelate-designs",
|
|
model: "Terrapin System",
|
|
category: "Bags",
|
|
weightGrams: 210,
|
|
});
|
|
|
|
expect(result.created).toBe(true);
|
|
expect(result.item.id).toBeDefined();
|
|
expect(result.item.model).toBe("Terrapin System");
|
|
});
|
|
|
|
it("upsertGlobalItem updates existing item on (manufacturerId, model) conflict and returns { item, created: false }", async () => {
|
|
await insertManufacturer(db, "MSR", "msr");
|
|
await upsertGlobalItem(db, {
|
|
manufacturerSlug: "msr",
|
|
model: "PocketRocket 2",
|
|
weightGrams: 83,
|
|
});
|
|
|
|
const second = await upsertGlobalItem(db, {
|
|
manufacturerSlug: "msr",
|
|
model: "PocketRocket 2",
|
|
weightGrams: 90,
|
|
});
|
|
|
|
expect(second.created).toBe(false);
|
|
expect(second.item.weightGrams).toBe(90);
|
|
|
|
// Only one row should exist
|
|
const all = await db.select().from(globalItems);
|
|
expect(all).toHaveLength(1);
|
|
});
|
|
|
|
it("upsertGlobalItem persists sourceUrl, imageCredit, imageSourceUrl", async () => {
|
|
await insertManufacturer(db, "Apidura", "apidura");
|
|
const result = await upsertGlobalItem(db, {
|
|
manufacturerSlug: "apidura",
|
|
model: "Handlebar Pack",
|
|
sourceUrl: "https://apidura.com/shop/handlebar-pack/",
|
|
imageCredit: "Apidura Ltd",
|
|
imageSourceUrl: "https://apidura.com/images/handlebar-pack.jpg",
|
|
});
|
|
|
|
expect(result.item.sourceUrl).toBe(
|
|
"https://apidura.com/shop/handlebar-pack/",
|
|
);
|
|
expect(result.item.imageCredit).toBe("Apidura Ltd");
|
|
expect(result.item.imageSourceUrl).toBe(
|
|
"https://apidura.com/images/handlebar-pack.jpg",
|
|
);
|
|
});
|
|
|
|
it("upsertGlobalItem with tags creates tags and links them", async () => {
|
|
await insertManufacturer(db, "Therm-a-Rest", "therm-a-rest");
|
|
const result = await upsertGlobalItem(db, {
|
|
manufacturerSlug: "therm-a-rest",
|
|
model: "NeoAir XLite",
|
|
tags: ["sleeping-pad", "ultralight"],
|
|
});
|
|
|
|
expect(result.created).toBe(true);
|
|
|
|
const linkedTags = await db
|
|
.select({ name: tags.name })
|
|
.from(globalItemTags)
|
|
.innerJoin(tags, eq(globalItemTags.tagId, tags.id))
|
|
.where(eq(globalItemTags.globalItemId, result.item.id));
|
|
|
|
expect(linkedTags).toHaveLength(2);
|
|
const tagNames = linkedTags.map((t) => t.name).sort();
|
|
expect(tagNames).toEqual(["sleeping-pad", "ultralight"]);
|
|
});
|
|
|
|
it("upsertGlobalItem without tags leaves existing tags untouched", async () => {
|
|
await insertManufacturer(db, "Sea to Summit", "sea-to-summit");
|
|
// Create item with tags
|
|
const first = await upsertGlobalItem(db, {
|
|
manufacturerSlug: "sea-to-summit",
|
|
model: "Spark III",
|
|
tags: ["sleeping-bag"],
|
|
});
|
|
|
|
// Upsert without tags
|
|
await upsertGlobalItem(db, {
|
|
manufacturerSlug: "sea-to-summit",
|
|
model: "Spark III",
|
|
weightGrams: 450,
|
|
});
|
|
|
|
// Tags should remain
|
|
const linkedTags = await db
|
|
.select()
|
|
.from(globalItemTags)
|
|
.where(eq(globalItemTags.globalItemId, first.item.id));
|
|
|
|
expect(linkedTags).toHaveLength(1);
|
|
});
|
|
|
|
it("upsertGlobalItem with empty tags array clears existing tags", async () => {
|
|
await insertManufacturer(db, "Big Agnes", "big-agnes");
|
|
// Create item with tags
|
|
const first = await upsertGlobalItem(db, {
|
|
manufacturerSlug: "big-agnes",
|
|
model: "Copper Spur HV UL2",
|
|
tags: ["tent", "ultralight"],
|
|
});
|
|
|
|
// Upsert with empty tags
|
|
await upsertGlobalItem(db, {
|
|
manufacturerSlug: "big-agnes",
|
|
model: "Copper Spur HV UL2",
|
|
tags: [],
|
|
});
|
|
|
|
// Tags should be cleared
|
|
const linkedTags = await db
|
|
.select()
|
|
.from(globalItemTags)
|
|
.where(eq(globalItemTags.globalItemId, first.item.id));
|
|
|
|
expect(linkedTags).toHaveLength(0);
|
|
});
|
|
|
|
it("bulkUpsertGlobalItems processes array and returns correct created/updated counts", async () => {
|
|
await insertManufacturer(db, "Petzl", "petzl");
|
|
await insertManufacturer(db, "Black Diamond", "black-diamond");
|
|
const result = await bulkUpsertGlobalItems(db, [
|
|
{ manufacturerSlug: "petzl", model: "Actik Core", weightGrams: 87 },
|
|
{
|
|
manufacturerSlug: "black-diamond",
|
|
model: "Spot 400",
|
|
weightGrams: 95,
|
|
},
|
|
{
|
|
manufacturerSlug: "black-diamond",
|
|
model: "Spot 350",
|
|
weightGrams: 90,
|
|
},
|
|
]);
|
|
|
|
expect(result.created).toBe(3);
|
|
expect(result.updated).toBe(0);
|
|
expect(result.items).toHaveLength(3);
|
|
});
|
|
|
|
it("bulkUpsertGlobalItems handles mix of new and existing items", async () => {
|
|
await insertManufacturer(db, "Petzl", "petzl");
|
|
await insertManufacturer(db, "Black Diamond", "black-diamond");
|
|
// Pre-insert one item
|
|
await upsertGlobalItem(db, {
|
|
manufacturerSlug: "petzl",
|
|
model: "Actik Core",
|
|
weightGrams: 87,
|
|
});
|
|
|
|
const result = await bulkUpsertGlobalItems(db, [
|
|
{ manufacturerSlug: "petzl", model: "Actik Core", weightGrams: 90 }, // existing
|
|
{
|
|
manufacturerSlug: "black-diamond",
|
|
model: "Spot 400",
|
|
weightGrams: 95,
|
|
}, // new
|
|
]);
|
|
|
|
expect(result.created).toBe(1);
|
|
expect(result.updated).toBe(1);
|
|
expect(result.items).toHaveLength(2);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("listGlobalItemsForAdmin", () => {
|
|
let db: TestDb["db"];
|
|
|
|
beforeEach(async () => {
|
|
({ db } = await createTestDb());
|
|
});
|
|
|
|
it("returns empty result when no items exist", async () => {
|
|
const result = await listGlobalItemsForAdmin(db);
|
|
expect(result.items).toHaveLength(0);
|
|
expect(result.total).toBe(0);
|
|
expect(result.hasMore).toBe(false);
|
|
});
|
|
|
|
it("returns paginated items with total count", async () => {
|
|
const mfr = await insertManufacturer(db);
|
|
await insertGlobalItem(db, { manufacturerId: mfr.id, model: "Alpha" });
|
|
await insertGlobalItem(db, { manufacturerId: mfr.id, model: "Beta" });
|
|
await insertGlobalItem(db, { manufacturerId: mfr.id, model: "Gamma" });
|
|
|
|
const result = await listGlobalItemsForAdmin(db, { limit: 2, offset: 0 });
|
|
expect(result.items).toHaveLength(2);
|
|
expect(result.total).toBe(3);
|
|
expect(result.hasMore).toBe(true);
|
|
expect(result.nextOffset).toBe(2);
|
|
});
|
|
|
|
it("filters by query string (brand/model)", async () => {
|
|
const mfr = await insertManufacturer(db, "Salsa", "salsa");
|
|
await insertGlobalItem(db, {
|
|
manufacturerId: mfr.id,
|
|
model: "Woodsmoke 700",
|
|
});
|
|
const mfr2 = await insertManufacturer(db, "Apidura", "apidura");
|
|
await insertGlobalItem(db, {
|
|
manufacturerId: mfr2.id,
|
|
model: "Racing Saddle Bag",
|
|
});
|
|
|
|
const result = await listGlobalItemsForAdmin(db, { query: "salsa" });
|
|
expect(result.items).toHaveLength(1);
|
|
expect(result.items[0]!.model).toBe("Woodsmoke 700");
|
|
});
|
|
|
|
it("includes tags and ownerCount per item", async () => {
|
|
const mfr = await insertManufacturer(db);
|
|
const globalItem = await insertGlobalItem(db, {
|
|
manufacturerId: mfr.id,
|
|
model: "Test Item",
|
|
});
|
|
const tag = await insertTag(db, "bikepacking");
|
|
await tagGlobalItem(db, globalItem.id, tag.id!);
|
|
|
|
// Insert a user and item linking to the global item
|
|
const [user] = await db
|
|
.insert(schema.users)
|
|
.values({ logtoSub: "test-sub" })
|
|
.returning();
|
|
await insertItem(db, "My Test Item", user!.id, {
|
|
globalItemId: globalItem.id,
|
|
});
|
|
|
|
const result = await listGlobalItemsForAdmin(db);
|
|
expect(result.items).toHaveLength(1);
|
|
expect(result.items[0]!.tags).toContain("bikepacking");
|
|
expect(result.items[0]!.ownerCount).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe("updateGlobalItemById", () => {
|
|
let db: TestDb["db"];
|
|
|
|
beforeEach(async () => {
|
|
({ db } = await createTestDb());
|
|
});
|
|
|
|
it("returns null for non-existent item", async () => {
|
|
const result = await updateGlobalItemById(db, 99999, { model: "Ghost" });
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("updates model field by id", async () => {
|
|
const mfr = await insertManufacturer(db);
|
|
const globalItem = await insertGlobalItem(db, {
|
|
manufacturerId: mfr.id,
|
|
model: "Original",
|
|
});
|
|
|
|
await updateGlobalItemById(db, globalItem.id, { model: "Updated" });
|
|
|
|
const updated = await getGlobalItemWithOwnerCount(db, globalItem.id);
|
|
expect(updated?.model).toBe("Updated");
|
|
});
|
|
|
|
it("syncs tags when tags array provided", async () => {
|
|
const mfr = await insertManufacturer(db);
|
|
const globalItem = await insertGlobalItem(db, {
|
|
manufacturerId: mfr.id,
|
|
model: "Tagged Item",
|
|
});
|
|
|
|
await updateGlobalItemById(db, globalItem.id, {
|
|
tags: ["cycling", "gravel"],
|
|
});
|
|
|
|
const result = await listGlobalItemsForAdmin(db);
|
|
const found = result.items.find((i) => i.id === globalItem.id);
|
|
expect(found?.tags).toContain("cycling");
|
|
expect(found?.tags).toContain("gravel");
|
|
});
|
|
});
|
|
|
|
describe("deleteGlobalItem", () => {
|
|
let db: TestDb["db"];
|
|
|
|
beforeEach(async () => {
|
|
({ db } = await createTestDb());
|
|
});
|
|
|
|
it("returns false for non-existent item", async () => {
|
|
const result = await deleteGlobalItem(db, 99999);
|
|
expect(result).toBe(false);
|
|
});
|
|
|
|
it("deletes item and returns true", async () => {
|
|
const mfr = await insertManufacturer(db);
|
|
const globalItem = await insertGlobalItem(db, {
|
|
manufacturerId: mfr.id,
|
|
model: "To Delete",
|
|
});
|
|
|
|
const result = await deleteGlobalItem(db, globalItem.id);
|
|
expect(result).toBe(true);
|
|
|
|
const found = await getGlobalItemWithOwnerCount(db, globalItem.id);
|
|
expect(found).toBeNull();
|
|
});
|
|
|
|
it("nullifies items.globalItemId before deleting", async () => {
|
|
const mfr = await insertManufacturer(db);
|
|
const globalItem = await insertGlobalItem(db, {
|
|
manufacturerId: mfr.id,
|
|
model: "Owned Item",
|
|
});
|
|
const [user] = await db
|
|
.insert(schema.users)
|
|
.values({ logtoSub: "delete-test-sub" })
|
|
.returning();
|
|
const userItem = await insertItem(db, "User Item", user!.id, {
|
|
globalItemId: globalItem.id,
|
|
});
|
|
|
|
await deleteGlobalItem(db, globalItem.id);
|
|
|
|
const [afterDelete] = await db
|
|
.select({ globalItemId: items.globalItemId })
|
|
.from(items)
|
|
.where(eq(items.id, userItem!.id));
|
|
expect(afterDelete?.globalItemId).toBeNull();
|
|
});
|
|
|
|
it("removes globalItemTags before deleting", async () => {
|
|
const mfr = await insertManufacturer(db);
|
|
const globalItem = await insertGlobalItem(db, {
|
|
manufacturerId: mfr.id,
|
|
model: "Tagged Delete",
|
|
});
|
|
const tag = await insertTag(db, "delete-tag");
|
|
await tagGlobalItem(db, globalItem.id, tag.id!);
|
|
|
|
await deleteGlobalItem(db, globalItem.id);
|
|
|
|
const remainingTags = await db
|
|
.select()
|
|
.from(globalItemTags)
|
|
.where(eq(globalItemTags.globalItemId, globalItem.id));
|
|
expect(remainingTags).toHaveLength(0);
|
|
});
|
|
});
|