···137137- **Web**: Vitest with `.test.tsx` suffix
138138- **Mocking**: Use proper dependency injection for testability
139139140140+#### TanStack Query Anti-patterns
141141+142142+**Avoid using simple string keys with generated API clients:**
143143+The API client generates complex query key objects. Using simple string arrays won't match:
144144+145145+```typescript
146146+// DON'T do this - wrong query key structure
147147+queryClient.setQueryData(["authControllerMe"], null);
148148+queryClient.removeQueries({ queryKey: ["authControllerMe"] });
149149+150150+// DO this instead - use the generated query key function
151151+import { authControllerMeQueryKey } from "@opnshelf/api";
152152+const meQueryKey = authControllerMeQueryKey();
153153+queryClient.setQueryData(meQueryKey, null);
154154+queryClient.removeQueries({ queryKey: meQueryKey });
155155+```
156156+140157## Backend Testing
141158142159The backend uses **Jest** for testing with comprehensive test coverage for services, controllers, and guards.
+1-1
apps/mobile/app/(tabs)/index.tsx
···44import { SafeAreaView } from "react-native-safe-area-context";
55import { Button } from "@/components/ui/Button";
66import { Card, CardContent, CardHeader } from "@/components/ui/Card";
77-import { colors, spacing, borderRadius } from "@/constants/theme";
77+import { colors, spacing } from "@/constants/theme";
8899const features = [
1010 {
+2-4
apps/mobile/app/(tabs)/search.tsx
···1212import { Check, Loader2, Plus } from "lucide-react-native";
1313import { useCallback, useEffect, useMemo, useRef, useState } from "react";
1414import {
1515- ActivityIndicator,
1615 Pressable,
1716 StyleSheet,
1817 Text,
···2120import { SafeAreaView } from "react-native-safe-area-context";
2221import { useAuth } from "@/contexts/auth";
2322import { useToast } from "@/contexts/toast";
2424-import { Badge } from "@/components/ui/Badge";
2523import { SearchInput } from "@/components/ui/Input";
2624import { Skeleton } from "@/components/ui/Skeleton";
2725import { colors, spacing, borderRadius } from "@/constants/theme";
···193191 markMutation.mutate({ body: { movieId } });
194192 }
195193 },
196196- [user, markMutation, unmarkMutation, router, showToast]
194194+ [user, markMutation, unmarkMutation, showToast]
197195 );
198196199197 const handleMoviePress = useCallback(
···290288 {data && data.results.length === 0 && debouncedQuery && (
291289 <View style={styles.centerContent}>
292290 <Text style={styles.emptyText}>
293293- No results found for "{debouncedQuery}"
291291+ No results found for "{debouncedQuery}"
294292 </Text>
295293 </View>
296294 )}