feat(inventory): implement item name autocomplete with rarity and case-insensitive search
This commit is contained in:
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user