feat: Implement interactive quest command allowing users to view active/available quests and accept new ones.

This commit is contained in:
syntaxbullet
2026-01-15 15:30:01 +01:00
parent eb108695d3
commit 9e5c6b5ac3
4 changed files with 212 additions and 14 deletions

View File

@@ -33,6 +33,7 @@ mock.module("@shared/db/DrizzleClient", () => {
const createMockTx = () => ({
query: {
userQuests: { findFirst: mockFindFirst, findMany: mockFindMany },
quests: { findMany: mockFindMany },
},
insert: mockInsert,
update: mockUpdate,
@@ -149,6 +150,31 @@ describe("questService", () => {
});
});
describe("getAvailableQuests", () => {
it("should return quests not yet accepted by user", async () => {
// First call to findMany (userQuests) returns accepted quest IDs
// Second call to findMany (quests) returns available quests
mockFindMany
.mockResolvedValueOnce([{ questId: 1 }]) // userQuests
.mockResolvedValueOnce([{ id: 2, name: "New Quest" }]); // quests
const result = await questService.getAvailableQuests("1");
expect(result).toEqual([{ id: 2, name: "New Quest" }] as any);
expect(mockFindMany).toHaveBeenCalledTimes(2);
});
it("should return all quests if user has no assigned quests", async () => {
mockFindMany
.mockResolvedValueOnce([]) // userQuests
.mockResolvedValueOnce([{ id: 1 }, { id: 2 }]); // quests
const result = await questService.getAvailableQuests("1");
expect(result).toEqual([{ id: 1 }, { id: 2 }] as any);
});
});
describe("handleEvent", () => {
it("should progress a quest with sub-events", async () => {
const mockUserQuest = {

View File

@@ -118,5 +118,20 @@ export const questService = {
quest: true,
}
});
},
getAvailableQuests: async (userId: string) => {
const userQuestIds = (await DrizzleClient.query.userQuests.findMany({
where: eq(userQuests.userId, BigInt(userId)),
columns: {
questId: true
}
})).map(uq => uq.questId);
return await DrizzleClient.query.quests.findMany({
where: (quests, { notInArray }) => userQuestIds.length > 0
? notInArray(quests.id, userQuestIds)
: undefined
});
}
};