aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/server/routes/study.test.ts
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-12-31 01:18:37 +0900
committernsfisis <nsfisis@gmail.com>2025-12-31 01:20:00 +0900
commitef65c4e6d31b5df36bfff3254d614f61bf659bed (patch)
tree4cfab0983a1aa3f58c1b90c1a8bc17fa37c4049e /src/server/routes/study.test.ts
parent139dd1a9ec77921ad757ec6bb9b2b97f9b1162c4 (diff)
downloadkioku-ef65c4e6d31b5df36bfff3254d614f61bf659bed.tar.gz
kioku-ef65c4e6d31b5df36bfff3254d614f61bf659bed.tar.zst
kioku-ef65c4e6d31b5df36bfff3254d614f61bf659bed.zip
feat(api): include note data in Card and Study route responses
Update Card GET endpoint to return note and field values when available, and Study GET endpoint to include note data for card display rendering. This enables the frontend to render note-based cards with their templates. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'src/server/routes/study.test.ts')
-rw-r--r--src/server/routes/study.test.ts107
1 files changed, 100 insertions, 7 deletions
diff --git a/src/server/routes/study.test.ts b/src/server/routes/study.test.ts
index d709750..77cb15c 100644
--- a/src/server/routes/study.test.ts
+++ b/src/server/routes/study.test.ts
@@ -6,8 +6,11 @@ import { errorHandler } from "../middleware/index.js";
import type {
Card,
CardRepository,
+ CardWithNoteData,
Deck,
DeckRepository,
+ Note,
+ NoteFieldValue,
ReviewLog,
ReviewLogRepository,
} from "../repositories/index.js";
@@ -24,6 +27,7 @@ function createMockCardRepo(): CardRepository {
softDelete: vi.fn(),
softDeleteByNoteId: vi.fn(),
findDueCards: vi.fn(),
+ findDueCardsWithNoteData: vi.fn(),
updateFSRSFields: vi.fn(),
};
}
@@ -114,9 +118,47 @@ function createMockReviewLog(overrides: Partial<ReviewLog> = {}): ReviewLog {
};
}
+function createMockCardWithNoteData(
+ overrides: Partial<CardWithNoteData> = {},
+): CardWithNoteData {
+ return {
+ ...createMockCard(overrides),
+ note: overrides.note ?? null,
+ fieldValues: overrides.fieldValues ?? [],
+ };
+}
+
+function createMockNote(overrides: Partial<Note> = {}): Note {
+ return {
+ id: "note-uuid-123",
+ deckId: "deck-uuid-123",
+ noteTypeId: "note-type-uuid-123",
+ createdAt: new Date("2024-01-01"),
+ updatedAt: new Date("2024-01-01"),
+ deletedAt: null,
+ syncVersion: 0,
+ ...overrides,
+ };
+}
+
+function createMockNoteFieldValue(
+ overrides: Partial<NoteFieldValue> = {},
+): NoteFieldValue {
+ return {
+ id: "field-value-uuid-123",
+ noteId: "note-uuid-123",
+ noteFieldTypeId: "field-type-uuid-123",
+ value: "Test value",
+ createdAt: new Date("2024-01-01"),
+ updatedAt: new Date("2024-01-01"),
+ syncVersion: 0,
+ ...overrides,
+ };
+}
+
interface StudyResponse {
card?: Card;
- cards?: Card[];
+ cards?: CardWithNoteData[];
error?: {
code: string;
message: string;
@@ -153,7 +195,7 @@ describe("GET /api/decks/:deckId/study", () => {
vi.mocked(mockDeckRepo.findById).mockResolvedValue(
createMockDeck({ id: DECK_ID }),
);
- vi.mocked(mockCardRepo.findDueCards).mockResolvedValue([]);
+ vi.mocked(mockCardRepo.findDueCardsWithNoteData).mockResolvedValue([]);
const res = await app.request(`/api/decks/${DECK_ID}/study`, {
method: "GET",
@@ -167,22 +209,36 @@ describe("GET /api/decks/:deckId/study", () => {
DECK_ID,
"user-uuid-123",
);
- expect(mockCardRepo.findDueCards).toHaveBeenCalledWith(
+ expect(mockCardRepo.findDueCardsWithNoteData).toHaveBeenCalledWith(
DECK_ID,
expect.any(Date),
100,
);
});
- it("returns due cards", async () => {
+ it("returns due cards with note data", async () => {
const mockCards = [
- createMockCard({ id: "card-1", front: "Q1", back: "A1" }),
- createMockCard({ id: "card-2", front: "Q2", back: "A2" }),
+ createMockCardWithNoteData({
+ id: "card-1",
+ front: "Q1",
+ back: "A1",
+ note: null,
+ fieldValues: [],
+ }),
+ createMockCardWithNoteData({
+ id: "card-2",
+ front: "Q2",
+ back: "A2",
+ note: null,
+ fieldValues: [],
+ }),
];
vi.mocked(mockDeckRepo.findById).mockResolvedValue(
createMockDeck({ id: DECK_ID }),
);
- vi.mocked(mockCardRepo.findDueCards).mockResolvedValue(mockCards);
+ vi.mocked(mockCardRepo.findDueCardsWithNoteData).mockResolvedValue(
+ mockCards,
+ );
const res = await app.request(`/api/decks/${DECK_ID}/study`, {
method: "GET",
@@ -194,6 +250,43 @@ describe("GET /api/decks/:deckId/study", () => {
expect(body.cards).toHaveLength(2);
});
+ it("returns due cards with note and field values when available", async () => {
+ const mockNote = createMockNote({ id: "note-1" });
+ const mockFieldValues = [
+ createMockNoteFieldValue({ noteId: "note-1", value: "Front" }),
+ createMockNoteFieldValue({
+ id: "fv-2",
+ noteId: "note-1",
+ value: "Back",
+ }),
+ ];
+ const mockCards = [
+ createMockCardWithNoteData({
+ id: "card-1",
+ noteId: "note-1",
+ note: mockNote,
+ fieldValues: mockFieldValues,
+ }),
+ ];
+ vi.mocked(mockDeckRepo.findById).mockResolvedValue(
+ createMockDeck({ id: DECK_ID }),
+ );
+ vi.mocked(mockCardRepo.findDueCardsWithNoteData).mockResolvedValue(
+ mockCards,
+ );
+
+ const res = await app.request(`/api/decks/${DECK_ID}/study`, {
+ method: "GET",
+ headers: { Authorization: `Bearer ${authToken}` },
+ });
+
+ expect(res.status).toBe(200);
+ const body = (await res.json()) as StudyResponse;
+ expect(body.cards).toHaveLength(1);
+ expect(body.cards?.[0]?.note?.id).toBe("note-1");
+ expect(body.cards?.[0]?.fieldValues).toHaveLength(2);
+ });
+
it("returns 404 for non-existent deck", async () => {
vi.mocked(mockDeckRepo.findById).mockResolvedValue(undefined);