import { Types } from "mongoose"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; import { CategoryModel } from "@/models/category"; import { setupSeededDb, teardownDb } from "@/test/seeded-db"; import { createCategory, installDefaultCategories, listCategories, setCategoryArchived, updateCategory, } from "./service"; let ids: Awaited>; let cakir = ""; let annemler = ""; beforeAll(async () => { ids = await setupSeededDb(); cakir = ids.household("Çakır Ailesi"); annemler = ids.household("Annemler"); }); afterAll(teardownDb); const byKey = (key: string, householdId = cakir) => CategoryModel.findOne({ householdId, defaultKey: key }) .lean() .then((c) => String(c!._id)); const form = (over: Partial[2]> = {}) => ({ kind: "expense" as const, name: "Test", parentId: null, icon: "tag" as const, color: "blue" as const, kakeiboGroup: null, bankCost: false, ...over, }); describe("varsayılan kategoriler", () => { it("tekrar yüklemede kopya oluşmaz", async () => { const before = await CategoryModel.countDocuments({ householdId: cakir }); const added = await installDefaultCategories( new Types.ObjectId(cakir), new Types.ObjectId(ids.user("admin")), ); expect(added).toBe(0); expect(await CategoryModel.countDocuments({ householdId: cakir })).toBe(before); }); it("alt kategoriler doğru ancestors zinciriyle yüklenir", async () => { const elektrik = await CategoryModel.findOne({ householdId: cakir, defaultKey: "faturalar.elektrik", }).lean(); const faturalar = await byKey("faturalar"); expect(elektrik?.ancestors.map(String)).toEqual([faturalar]); }); }); describe("izolasyon ve roller", () => { it("başka hanenin kategorilerini okuyamaz ve değiştiremez", async () => { expect(await listCategories(ids.user("fatma"), cakir)).toBeNull(); const kira = await byKey("kira"); // fatma kendi hanesinin kimliğiyle, Çakır'ın kategorisini hedefler → bulunamaz. const r = await updateCategory( ids.user("fatma"), annemler, kira, form({ name: "Ele geçirildi" }), ); expect(r.ok).toBe(false); expect((await CategoryModel.findById(kira).lean())?.name).toBe("Kira"); const r2 = await setCategoryArchived(ids.user("fatma"), annemler, kira, true); expect(r2.ok).toBe(false); }); it("izleyici ekleyemez, üye ekler", async () => { expect((await createCategory(ids.user("zeynep"), cakir, form({ name: "Harçlık" }))).ok).toBe( false, ); expect((await createCategory(ids.user("emre"), cakir, form({ name: "Harçlık" }))).ok).toBe( true, ); }); it("başka hanenin kategorisini üst kategori yapamaz", async () => { const annemlerMarket = await byKey("market", annemler); const r = await createCategory( ids.user("ayse"), cakir, form({ name: "Sızma", parentId: annemlerMarket }), ); expect(r.ok).toBe(false); }); }); describe("ağaç kuralları", () => { it("aynı düzeyde aynı ad (Türkçe normalize) reddedilir", async () => { const r = await createCategory(ids.user("ayse"), cakir, form({ name: " KİRA " })); expect(r.ok).toBe(false); // farklı düzeyde aynı ad serbest const market = await byKey("market"); expect( (await createCategory(ids.user("ayse"), cakir, form({ name: "Kira", parentId: market }))).ok, ).toBe(true); }); it("taşıma alt ağacın ancestors alanlarını günceller", async () => { const ulasim = await byKey("ulasim"); const yakit = await byKey("ulasim.yakit"); const created = await createCategory( ids.user("ayse"), cakir, form({ name: "LPG", parentId: yakit }), ); const lpg = created.ok ? created.data.id : ""; const ev = await byKey("ev"); // Yakıt'ı (alt kategorisi LPG ile) "Ev ve mobilya" altına taşı. const r = await updateCategory( ids.user("ayse"), cakir, yakit, form({ name: "Yakıt", parentId: ev, icon: "fuel" }), ); expect(r.ok).toBe(true); expect((await CategoryModel.findById(yakit).lean())?.ancestors.map(String)).toEqual([ev]); expect((await CategoryModel.findById(lpg).lean())?.ancestors.map(String)).toEqual([ev, yakit]); expect((await CategoryModel.findById(yakit).lean())?.parentId?.toString()).not.toBe(ulasim); }); it("döngü ve tür uyuşmazlığı reddedilir", async () => { const market = await byKey("market"); const gida = await byKey("market.gida"); expect( ( await updateCategory( ids.user("ayse"), cakir, market, form({ name: "Market", parentId: gida }), ) ).ok, ).toBe(false); const maas = await byKey("maas"); expect( (await updateCategory(ids.user("ayse"), cakir, gida, form({ name: "Gıda", parentId: maas }))) .ok, ).toBe(false); }); it("arşivleme alt ağaçla birlikte yapılır; üstü arşivliyken alt kategori geri alınamaz", async () => { const saglik = await byKey("saglik"); const ilac = await byKey("saglik.ilac"); expect((await setCategoryArchived(ids.user("emre"), cakir, saglik, true)).ok).toBe(true); const active = (await listCategories(ids.user("emre"), cakir))!.map((c) => c.id); expect(active).not.toContain(saglik); expect(active).not.toContain(ilac); expect((await setCategoryArchived(ids.user("emre"), cakir, ilac, false)).ok).toBe(false); expect((await setCategoryArchived(ids.user("emre"), cakir, saglik, false)).ok).toBe(true); }); });