2 Commits

3 changed files with 68 additions and 27 deletions

View File

@@ -4,9 +4,6 @@ import { inventoryService } from "@/modules/inventory/inventory.service";
import { userService } from "@/modules/user/user.service";
import { createErrorEmbed } from "@lib/embeds";
import { getItemUseResultEmbed } from "@/modules/inventory/inventory.view";
import { inventory, items } from "@/db/schema";
import { eq, and, like } from "drizzle-orm";
import { DrizzleClient } from "@/lib/DrizzleClient";
import type { ItemUsageData } from "@/lib/types";
import { UserError } from "@/lib/errors";
import { config } from "@/lib/config";
@@ -75,28 +72,8 @@ export const use = createCommand({
const focusedValue = interaction.options.getFocused();
const userId = interaction.user.id;
// Fetch owned items that match the search query
// We join with items table to filter by name directly in the database
const entries = await DrizzleClient.select({
quantity: inventory.quantity,
item: items
})
.from(inventory)
.innerJoin(items, eq(inventory.itemId, items.id))
.where(and(
eq(inventory.userId, BigInt(userId)),
like(items.name, `%${focusedValue}%`)
))
.limit(20); // Fetch up to 20 matching items
const results = await inventoryService.getAutocompleteItems(userId, focusedValue);
const filtered = entries.filter(entry => {
const usageData = entry.item.usageData as ItemUsageData | null;
const isUsable = usageData && usageData.effects && usageData.effects.length > 0;
return isUsable;
});
await interaction.respond(
filtered.map(entry => ({ name: `${entry.item.name} (${entry.quantity})`, value: entry.item.id }))
);
await interaction.respond(results);
}
});

View File

@@ -18,6 +18,8 @@ const mockWhere = mock();
const mockSelect = mock();
const mockFrom = mock();
const mockOnConflictDoUpdate = mock();
const mockInnerJoin = mock();
const mockLimit = mock();
// Chain setup
mockInsert.mockReturnValue({ values: mockValues });
@@ -34,7 +36,10 @@ mockWhere.mockReturnValue({ returning: mockReturning });
mockDelete.mockReturnValue({ where: mockWhere });
mockSelect.mockReturnValue({ from: mockFrom });
mockFrom.mockReturnValue({ where: mockWhere });
mockFrom.mockReturnValue({ where: mockWhere, innerJoin: mockInnerJoin });
mockInnerJoin.mockReturnValue({ where: mockWhere });
mockWhere.mockReturnValue({ returning: mockReturning, limit: mockLimit });
mockLimit.mockResolvedValue([]);
// Mock DrizzleClient
mock.module("@/lib/DrizzleClient", () => {
@@ -239,4 +244,39 @@ describe("inventoryService", () => {
expect(mockDelete).toHaveBeenCalledWith(inventory); // Consume
});
});
describe("getAutocompleteItems", () => {
it("should return formatted autocomplete results with rarity", async () => {
const mockItems = [
{
item: { id: 1, name: "Common Sword", rarity: "Common", usageData: { effects: [{}] } },
quantity: 5n
},
{
item: { id: 2, name: "Epic Shield", rarity: "Epic", usageData: { effects: [{}] } },
quantity: 1n
}
];
mockLimit.mockResolvedValue(mockItems);
// Restore mocks that might have been polluted by other tests
mockFrom.mockReturnValue({ where: mockWhere, innerJoin: mockInnerJoin });
mockWhere.mockReturnValue({ returning: mockReturning, limit: mockLimit });
const result = await inventoryService.getAutocompleteItems("1", "Sw");
expect(mockSelect).toHaveBeenCalled();
expect(mockFrom).toHaveBeenCalledWith(inventory);
expect(mockInnerJoin).toHaveBeenCalled(); // checks join
expect(mockWhere).toHaveBeenCalled(); // checks filters
expect(mockLimit).toHaveBeenCalledWith(20);
expect(result).toHaveLength(2);
expect(result[0]?.name).toBe("Common Sword (5) [Common]");
expect(result[0]?.value).toBe(1);
expect(result[1]?.name).toBe("Epic Shield (1) [Epic]");
expect(result[1]?.value).toBe(2);
});
});
});

View File

@@ -1,5 +1,5 @@
import { inventory, items, users, userTimers } from "@/db/schema";
import { eq, and, sql, count } from "drizzle-orm";
import { eq, and, sql, count, ilike } from "drizzle-orm";
import { DrizzleClient } from "@/lib/DrizzleClient";
import { economyService } from "@/modules/economy/economy.service";
import { levelingService } from "@/modules/leveling/leveling.service";
@@ -181,5 +181,29 @@ export const inventoryService = {
return { success: true, results, usageData, item };
}, tx);
},
getAutocompleteItems: async (userId: string, query: string) => {
const entries = await DrizzleClient.select({
quantity: inventory.quantity,
item: items
})
.from(inventory)
.innerJoin(items, eq(inventory.itemId, items.id))
.where(and(
eq(inventory.userId, BigInt(userId)),
ilike(items.name, `%${query}%`)
))
.limit(20);
const filtered = entries.filter(entry => {
const usageData = entry.item.usageData as ItemUsageData | null;
return usageData && usageData.effects && usageData.effects.length > 0;
});
return filtered.map(entry => ({
name: `${entry.item.name} (${entry.quantity}) [${entry.item.rarity || 'Common'}]`,
value: entry.item.id
}));
}
};