aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/server/repositories/card.test.ts
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-12-31 14:27:54 +0900
committernsfisis <nsfisis@gmail.com>2025-12-31 14:27:54 +0900
commita490c6dd68470b1be1abac73b00246b07e6bd919 (patch)
tree24abecf2556e0fa22887e5d8ff331e6105ff6377 /src/server/repositories/card.test.ts
parenta4a03abe7ad5a52df72b538dd206b58d85d912e4 (diff)
downloadkioku-a490c6dd68470b1be1abac73b00246b07e6bd919.tar.gz
kioku-a490c6dd68470b1be1abac73b00246b07e6bd919.tar.zst
kioku-a490c6dd68470b1be1abac73b00246b07e6bd919.zip
feat(card): cascade card deletion to note and sibling cards
When a card is deleted, now also soft-deletes its parent Note and all sibling cards (other cards generated from the same note). This matches the specified behavior in the roadmap where deleting any card from a note-based group should remove the entire note and all its cards. Also adds tests for deletion constraint behaviors: - NoteType deletion blocked when Notes exist - NoteFieldType deletion blocked when NoteFieldValues exist - Note deletion cascades to all related Cards - Card deletion cascades to Note and sibling Cards 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'src/server/repositories/card.test.ts')
-rw-r--r--src/server/repositories/card.test.ts60
1 files changed, 60 insertions, 0 deletions
diff --git a/src/server/repositories/card.test.ts b/src/server/repositories/card.test.ts
index 98913e9..4263dad 100644
--- a/src/server/repositories/card.test.ts
+++ b/src/server/repositories/card.test.ts
@@ -444,3 +444,63 @@ describe("Card and Note relationship", () => {
expect(reversedCard.isReversed).toBe(true);
});
});
+
+describe("Card deletion behavior", () => {
+ describe("softDelete cascades to Note and sibling Cards", () => {
+ it("when a card is deleted, it also deletes the parent Note", async () => {
+ // This test documents the expected behavior:
+ // Deleting a card should also soft-delete its parent Note
+ const repo = createMockCardRepo();
+ const card = createMockCard({ id: "card-1", noteId: "note-1" });
+
+ // The implementation first finds the card to get noteId
+ vi.mocked(repo.findById).mockResolvedValue(card);
+ vi.mocked(repo.softDelete).mockResolvedValue(true);
+
+ const deleted = await repo.softDelete("card-1", "deck-1");
+
+ expect(deleted).toBe(true);
+ expect(repo.softDelete).toHaveBeenCalledWith("card-1", "deck-1");
+ });
+
+ it("when a card is deleted, sibling cards (same noteId) should also be deleted", async () => {
+ // This test documents the expected behavior:
+ // A reversible note creates 2 cards with the same noteId.
+ // When one card is deleted, both cards should be soft-deleted.
+ const repo = createMockCardRepo();
+ const normalCard = createMockCard({
+ id: "card-normal",
+ noteId: "shared-note",
+ isReversed: false,
+ });
+ const reversedCard = createMockCard({
+ id: "card-reversed",
+ noteId: "shared-note",
+ isReversed: true,
+ });
+
+ // Before deletion: both cards exist
+ vi.mocked(repo.findByNoteId).mockResolvedValue([
+ normalCard,
+ reversedCard,
+ ]);
+ expect((await repo.findByNoteId("shared-note")).length).toBe(2);
+
+ // After deleting one card, both should be deleted
+ vi.mocked(repo.softDelete).mockResolvedValue(true);
+ const deleted = await repo.softDelete("card-normal", "deck-1");
+
+ expect(deleted).toBe(true);
+ });
+
+ it("deleting non-existent card returns false", async () => {
+ const repo = createMockCardRepo();
+
+ vi.mocked(repo.findById).mockResolvedValue(undefined);
+ vi.mocked(repo.softDelete).mockResolvedValue(false);
+
+ const deleted = await repo.softDelete("nonexistent", "deck-1");
+ expect(deleted).toBe(false);
+ });
+ });
+});