mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-06 01:03:32 +08:00
fix: harden unicode safety checks
This commit is contained in:
@@ -597,7 +597,7 @@ For more detailed information, see the `docs/` directory:
|
||||
|
||||
## Contributers
|
||||
|
||||
- Himanshu Sharma [@ihimanss](https://github.com/ihimanss)
|
||||
- Himanshu Sharma [@ihimanss](https://github.com/ihimanss)
|
||||
- Sungmin Hong [@aws-hsungmin](https://github.com/aws-hsungmin)
|
||||
|
||||
|
||||
|
||||
@@ -4,12 +4,10 @@
|
||||
# Installs Everything Claude Code workflows into a Kiro project.
|
||||
#
|
||||
# Usage:
|
||||
# ./install.sh # Install to current directory
|
||||
# ./install.sh /path/to/dir # Install to specific directory
|
||||
# ./install.sh ~ # Install globally to ~/.kiro/
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
# ./install.sh # Install to current directory
|
||||
# ./install.sh /path/to/dir # Install to specific directory
|
||||
# ./install.sh ~ # Install globally to ~/.kiro/
|
||||
# set -euo pipefail
|
||||
|
||||
# When globs match nothing, expand to empty list instead of the literal pattern
|
||||
shopt -s nullglob
|
||||
|
||||
@@ -50,7 +50,7 @@ case "$FORMATTER" in
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
|
||||
prettier)
|
||||
if command -v npx &>/dev/null; then
|
||||
echo "Formatting $FILE with Prettier..."
|
||||
@@ -61,7 +61,7 @@ case "$FORMATTER" in
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
|
||||
none)
|
||||
echo "No formatter detected (biome.json, .prettierrc, or installed formatter)"
|
||||
echo "Skipping format for: $FILE"
|
||||
|
||||
@@ -36,7 +36,7 @@ detect_pm() {
|
||||
}
|
||||
|
||||
PM=$(detect_pm)
|
||||
echo "📦 Package manager: $PM"
|
||||
echo " Package manager: $PM"
|
||||
echo ""
|
||||
|
||||
# ── Helper: run a check ─────────────────────────────────────
|
||||
|
||||
@@ -62,10 +62,10 @@ Choose model tier based on task complexity:
|
||||
|
||||
- **Haiku**: Classification, boilerplate transforms, narrow edits
|
||||
- Example: Rename variable, add type annotation, format code
|
||||
|
||||
|
||||
- **Sonnet**: Implementation and refactors
|
||||
- Example: Implement feature, refactor module, write tests
|
||||
|
||||
|
||||
- **Opus**: Architecture, root-cause analysis, multi-file invariants
|
||||
- Example: Design system, debug complex issue, review architecture
|
||||
|
||||
@@ -75,10 +75,10 @@ Choose model tier based on task complexity:
|
||||
|
||||
- **Continue session** for closely-coupled units
|
||||
- Example: Implementing related functions in same module
|
||||
|
||||
|
||||
- **Start fresh session** after major phase transitions
|
||||
- Example: Moving from implementation to testing
|
||||
|
||||
|
||||
- **Compact after milestone completion**, not during active debugging
|
||||
- Example: After feature complete, before starting next feature
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Backend architecture patterns and best practices for scalable server-side applic
|
||||
### RESTful API Structure
|
||||
|
||||
```typescript
|
||||
// ✅ Resource-based URLs
|
||||
// PASS: Resource-based URLs
|
||||
GET /api/markets # List resources
|
||||
GET /api/markets/:id # Get single resource
|
||||
POST /api/markets # Create resource
|
||||
@@ -33,7 +33,7 @@ PUT /api/markets/:id # Replace resource
|
||||
PATCH /api/markets/:id # Update resource
|
||||
DELETE /api/markets/:id # Delete resource
|
||||
|
||||
// ✅ Query parameters for filtering, sorting, pagination
|
||||
// PASS: Query parameters for filtering, sorting, pagination
|
||||
GET /api/markets?status=active&sort=volume&limit=20&offset=0
|
||||
```
|
||||
|
||||
@@ -133,7 +133,7 @@ export default withAuth(async (req, res) => {
|
||||
### Query Optimization
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD: Select only needed columns
|
||||
// PASS: GOOD: Select only needed columns
|
||||
const { data } = await supabase
|
||||
.from('markets')
|
||||
.select('id, name, status, volume')
|
||||
@@ -141,7 +141,7 @@ const { data } = await supabase
|
||||
.order('volume', { ascending: false })
|
||||
.limit(10)
|
||||
|
||||
// ❌ BAD: Select everything
|
||||
// FAIL: BAD: Select everything
|
||||
const { data } = await supabase
|
||||
.from('markets')
|
||||
.select('*')
|
||||
@@ -150,13 +150,13 @@ const { data } = await supabase
|
||||
### N+1 Query Prevention
|
||||
|
||||
```typescript
|
||||
// ❌ BAD: N+1 query problem
|
||||
// FAIL: BAD: N+1 query problem
|
||||
const markets = await getMarkets()
|
||||
for (const market of markets) {
|
||||
market.creator = await getUser(market.creator_id) // N queries
|
||||
}
|
||||
|
||||
// ✅ GOOD: Batch fetch
|
||||
// PASS: GOOD: Batch fetch
|
||||
const markets = await getMarkets()
|
||||
const creatorIds = markets.map(m => m.creator_id)
|
||||
const creators = await getUsers(creatorIds) // 1 query
|
||||
|
||||
@@ -50,12 +50,12 @@ Universal coding standards applicable across all projects.
|
||||
### Variable Naming
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD: Descriptive names
|
||||
// PASS: GOOD: Descriptive names
|
||||
const marketSearchQuery = 'election'
|
||||
const isUserAuthenticated = true
|
||||
const totalRevenue = 1000
|
||||
|
||||
// ❌ BAD: Unclear names
|
||||
// FAIL: BAD: Unclear names
|
||||
const q = 'election'
|
||||
const flag = true
|
||||
const x = 1000
|
||||
@@ -64,12 +64,12 @@ const x = 1000
|
||||
### Function Naming
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD: Verb-noun pattern
|
||||
// PASS: GOOD: Verb-noun pattern
|
||||
async function fetchMarketData(marketId: string) { }
|
||||
function calculateSimilarity(a: number[], b: number[]) { }
|
||||
function isValidEmail(email: string): boolean { }
|
||||
|
||||
// ❌ BAD: Unclear or noun-only
|
||||
// FAIL: BAD: Unclear or noun-only
|
||||
async function market(id: string) { }
|
||||
function similarity(a, b) { }
|
||||
function email(e) { }
|
||||
@@ -78,7 +78,7 @@ function email(e) { }
|
||||
### Immutability Pattern (CRITICAL)
|
||||
|
||||
```typescript
|
||||
// ✅ ALWAYS use spread operator
|
||||
// PASS: ALWAYS use spread operator
|
||||
const updatedUser = {
|
||||
...user,
|
||||
name: 'New Name'
|
||||
@@ -86,7 +86,7 @@ const updatedUser = {
|
||||
|
||||
const updatedArray = [...items, newItem]
|
||||
|
||||
// ❌ NEVER mutate directly
|
||||
// FAIL: NEVER mutate directly
|
||||
user.name = 'New Name' // BAD
|
||||
items.push(newItem) // BAD
|
||||
```
|
||||
@@ -94,7 +94,7 @@ items.push(newItem) // BAD
|
||||
### Error Handling
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD: Comprehensive error handling
|
||||
// PASS: GOOD: Comprehensive error handling
|
||||
async function fetchData(url: string) {
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
@@ -110,7 +110,7 @@ async function fetchData(url: string) {
|
||||
}
|
||||
}
|
||||
|
||||
// ❌ BAD: No error handling
|
||||
// FAIL: BAD: No error handling
|
||||
async function fetchData(url) {
|
||||
const response = await fetch(url)
|
||||
return response.json()
|
||||
@@ -120,14 +120,14 @@ async function fetchData(url) {
|
||||
### Async/Await Best Practices
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD: Parallel execution when possible
|
||||
// PASS: GOOD: Parallel execution when possible
|
||||
const [users, markets, stats] = await Promise.all([
|
||||
fetchUsers(),
|
||||
fetchMarkets(),
|
||||
fetchStats()
|
||||
])
|
||||
|
||||
// ❌ BAD: Sequential when unnecessary
|
||||
// FAIL: BAD: Sequential when unnecessary
|
||||
const users = await fetchUsers()
|
||||
const markets = await fetchMarkets()
|
||||
const stats = await fetchStats()
|
||||
@@ -136,7 +136,7 @@ const stats = await fetchStats()
|
||||
### Type Safety
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD: Proper types
|
||||
// PASS: GOOD: Proper types
|
||||
interface Market {
|
||||
id: string
|
||||
name: string
|
||||
@@ -148,7 +148,7 @@ function getMarket(id: string): Promise<Market> {
|
||||
// Implementation
|
||||
}
|
||||
|
||||
// ❌ BAD: Using 'any'
|
||||
// FAIL: BAD: Using 'any'
|
||||
function getMarket(id: any): Promise<any> {
|
||||
// Implementation
|
||||
}
|
||||
@@ -159,7 +159,7 @@ function getMarket(id: any): Promise<any> {
|
||||
### Component Structure
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD: Functional component with types
|
||||
// PASS: GOOD: Functional component with types
|
||||
interface ButtonProps {
|
||||
children: React.ReactNode
|
||||
onClick: () => void
|
||||
@@ -184,7 +184,7 @@ export function Button({
|
||||
)
|
||||
}
|
||||
|
||||
// ❌ BAD: No types, unclear structure
|
||||
// FAIL: BAD: No types, unclear structure
|
||||
export function Button(props) {
|
||||
return <button onClick={props.onClick}>{props.children}</button>
|
||||
}
|
||||
@@ -193,7 +193,7 @@ export function Button(props) {
|
||||
### Custom Hooks
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD: Reusable custom hook
|
||||
// PASS: GOOD: Reusable custom hook
|
||||
export function useDebounce<T>(value: T, delay: number): T {
|
||||
const [debouncedValue, setDebouncedValue] = useState<T>(value)
|
||||
|
||||
@@ -215,25 +215,25 @@ const debouncedQuery = useDebounce(searchQuery, 500)
|
||||
### State Management
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD: Proper state updates
|
||||
// PASS: GOOD: Proper state updates
|
||||
const [count, setCount] = useState(0)
|
||||
|
||||
// Functional update for state based on previous state
|
||||
setCount(prev => prev + 1)
|
||||
|
||||
// ❌ BAD: Direct state reference
|
||||
// FAIL: BAD: Direct state reference
|
||||
setCount(count + 1) // Can be stale in async scenarios
|
||||
```
|
||||
|
||||
### Conditional Rendering
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD: Clear conditional rendering
|
||||
// PASS: GOOD: Clear conditional rendering
|
||||
{isLoading && <Spinner />}
|
||||
{error && <ErrorMessage error={error} />}
|
||||
{data && <DataDisplay data={data} />}
|
||||
|
||||
// ❌ BAD: Ternary hell
|
||||
// FAIL: BAD: Ternary hell
|
||||
{isLoading ? <Spinner /> : error ? <ErrorMessage error={error} /> : data ? <DataDisplay data={data} /> : null}
|
||||
```
|
||||
|
||||
@@ -256,7 +256,7 @@ GET /api/markets?status=active&limit=10&offset=0
|
||||
### Response Format
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD: Consistent response structure
|
||||
// PASS: GOOD: Consistent response structure
|
||||
interface ApiResponse<T> {
|
||||
success: boolean
|
||||
data?: T
|
||||
@@ -287,7 +287,7 @@ return NextResponse.json({
|
||||
```typescript
|
||||
import { z } from 'zod'
|
||||
|
||||
// ✅ GOOD: Schema validation
|
||||
// PASS: GOOD: Schema validation
|
||||
const CreateMarketSchema = z.object({
|
||||
name: z.string().min(1).max(200),
|
||||
description: z.string().min(1).max(2000),
|
||||
@@ -350,14 +350,14 @@ types/market.types.ts # camelCase with .types suffix
|
||||
### When to Comment
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD: Explain WHY, not WHAT
|
||||
// PASS: GOOD: Explain WHY, not WHAT
|
||||
// Use exponential backoff to avoid overwhelming the API during outages
|
||||
const delay = Math.min(1000 * Math.pow(2, retryCount), 30000)
|
||||
|
||||
// Deliberately using mutation here for performance with large arrays
|
||||
items.push(newItem)
|
||||
|
||||
// ❌ BAD: Stating the obvious
|
||||
// FAIL: BAD: Stating the obvious
|
||||
// Increment counter by 1
|
||||
count++
|
||||
|
||||
@@ -397,12 +397,12 @@ export async function searchMarkets(
|
||||
```typescript
|
||||
import { useMemo, useCallback } from 'react'
|
||||
|
||||
// ✅ GOOD: Memoize expensive computations
|
||||
// PASS: GOOD: Memoize expensive computations
|
||||
const sortedMarkets = useMemo(() => {
|
||||
return markets.sort((a, b) => b.volume - a.volume)
|
||||
}, [markets])
|
||||
|
||||
// ✅ GOOD: Memoize callbacks
|
||||
// PASS: GOOD: Memoize callbacks
|
||||
const handleSearch = useCallback((query: string) => {
|
||||
setSearchQuery(query)
|
||||
}, [])
|
||||
@@ -413,7 +413,7 @@ const handleSearch = useCallback((query: string) => {
|
||||
```typescript
|
||||
import { lazy, Suspense } from 'react'
|
||||
|
||||
// ✅ GOOD: Lazy load heavy components
|
||||
// PASS: GOOD: Lazy load heavy components
|
||||
const HeavyChart = lazy(() => import('./HeavyChart'))
|
||||
|
||||
export function Dashboard() {
|
||||
@@ -428,13 +428,13 @@ export function Dashboard() {
|
||||
### Database Queries
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD: Select only needed columns
|
||||
// PASS: GOOD: Select only needed columns
|
||||
const { data } = await supabase
|
||||
.from('markets')
|
||||
.select('id, name, status')
|
||||
.limit(10)
|
||||
|
||||
// ❌ BAD: Select everything
|
||||
// FAIL: BAD: Select everything
|
||||
const { data } = await supabase
|
||||
.from('markets')
|
||||
.select('*')
|
||||
@@ -461,12 +461,12 @@ test('calculates similarity correctly', () => {
|
||||
### Test Naming
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD: Descriptive test names
|
||||
// PASS: GOOD: Descriptive test names
|
||||
test('returns empty array when no markets match query', () => { })
|
||||
test('throws error when OpenAI API key is missing', () => { })
|
||||
test('falls back to substring search when Redis unavailable', () => { })
|
||||
|
||||
// ❌ BAD: Vague test names
|
||||
// FAIL: BAD: Vague test names
|
||||
test('works', () => { })
|
||||
test('test search', () => { })
|
||||
```
|
||||
@@ -477,12 +477,12 @@ Watch for these anti-patterns:
|
||||
|
||||
### 1. Long Functions
|
||||
```typescript
|
||||
// ❌ BAD: Function > 50 lines
|
||||
// FAIL: BAD: Function > 50 lines
|
||||
function processMarketData() {
|
||||
// 100 lines of code
|
||||
}
|
||||
|
||||
// ✅ GOOD: Split into smaller functions
|
||||
// PASS: GOOD: Split into smaller functions
|
||||
function processMarketData() {
|
||||
const validated = validateData()
|
||||
const transformed = transformData(validated)
|
||||
@@ -492,7 +492,7 @@ function processMarketData() {
|
||||
|
||||
### 2. Deep Nesting
|
||||
```typescript
|
||||
// ❌ BAD: 5+ levels of nesting
|
||||
// FAIL: BAD: 5+ levels of nesting
|
||||
if (user) {
|
||||
if (user.isAdmin) {
|
||||
if (market) {
|
||||
@@ -505,7 +505,7 @@ if (user) {
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ GOOD: Early returns
|
||||
// PASS: GOOD: Early returns
|
||||
if (!user) return
|
||||
if (!user.isAdmin) return
|
||||
if (!market) return
|
||||
@@ -517,11 +517,11 @@ if (!hasPermission) return
|
||||
|
||||
### 3. Magic Numbers
|
||||
```typescript
|
||||
// ❌ BAD: Unexplained numbers
|
||||
// FAIL: BAD: Unexplained numbers
|
||||
if (retryCount > 3) { }
|
||||
setTimeout(callback, 500)
|
||||
|
||||
// ✅ GOOD: Named constants
|
||||
// PASS: GOOD: Named constants
|
||||
const MAX_RETRIES = 3
|
||||
const DEBOUNCE_DELAY_MS = 500
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Modern frontend patterns for React, Next.js, and performant user interfaces.
|
||||
### Composition Over Inheritance
|
||||
|
||||
```typescript
|
||||
// ✅ GOOD: Component composition
|
||||
// PASS: GOOD: Component composition
|
||||
interface CardProps {
|
||||
children: React.ReactNode
|
||||
variant?: 'default' | 'outlined'
|
||||
@@ -296,17 +296,17 @@ export function useMarkets() {
|
||||
### Memoization
|
||||
|
||||
```typescript
|
||||
// ✅ useMemo for expensive computations
|
||||
// PASS: useMemo for expensive computations
|
||||
const sortedMarkets = useMemo(() => {
|
||||
return markets.sort((a, b) => b.volume - a.volume)
|
||||
}, [markets])
|
||||
|
||||
// ✅ useCallback for functions passed to children
|
||||
// PASS: useCallback for functions passed to children
|
||||
const handleSearch = useCallback((query: string) => {
|
||||
setSearchQuery(query)
|
||||
}, [])
|
||||
|
||||
// ✅ React.memo for pure components
|
||||
// PASS: React.memo for pure components
|
||||
export const MarketCard = React.memo<MarketCardProps>(({ market }) => {
|
||||
return (
|
||||
<div className="market-card">
|
||||
@@ -322,7 +322,7 @@ export const MarketCard = React.memo<MarketCardProps>(({ market }) => {
|
||||
```typescript
|
||||
import { lazy, Suspense } from 'react'
|
||||
|
||||
// ✅ Lazy load heavy components
|
||||
// PASS: Lazy load heavy components
|
||||
const HeavyChart = lazy(() => import('./HeavyChart'))
|
||||
const ThreeJsBackground = lazy(() => import('./ThreeJsBackground'))
|
||||
|
||||
@@ -517,7 +517,7 @@ export class ErrorBoundary extends React.Component<
|
||||
```typescript
|
||||
import { motion, AnimatePresence } from 'framer-motion'
|
||||
|
||||
// ✅ List animations
|
||||
// PASS: List animations
|
||||
export function AnimatedMarketList({ markets }: { markets: Market[] }) {
|
||||
return (
|
||||
<AnimatePresence>
|
||||
@@ -536,7 +536,7 @@ export function AnimatedMarketList({ markets }: { markets: Market[] }) {
|
||||
)
|
||||
}
|
||||
|
||||
// ✅ Modal animations
|
||||
// PASS: Modal animations
|
||||
export function Modal({ isOpen, onClose, children }: ModalProps) {
|
||||
return (
|
||||
<AnimatePresence>
|
||||
|
||||
@@ -192,7 +192,7 @@ func TestValidate(t *testing.T) {
|
||||
{"valid", "test@example.com", false},
|
||||
{"invalid", "not-an-email", true},
|
||||
}
|
||||
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := Validate(tt.input)
|
||||
|
||||
@@ -49,7 +49,7 @@ func TestValidateEmail(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := ValidateEmail(tt.email)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ValidateEmail(%q) error = %v, wantErr %v",
|
||||
t.Errorf("ValidateEmail(%q) error = %v, wantErr %v",
|
||||
tt.email, err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
@@ -95,19 +95,19 @@ Use `t.Cleanup()` for resource cleanup:
|
||||
```go
|
||||
func testDB(t *testing.T) *sql.DB {
|
||||
t.Helper()
|
||||
|
||||
|
||||
db, err := sql.Open("sqlite3", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open test db: %v", err)
|
||||
}
|
||||
|
||||
|
||||
// Cleanup runs after test completes
|
||||
t.Cleanup(func() {
|
||||
if err := db.Close(); err != nil {
|
||||
t.Errorf("failed to close db: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ go test -cover ./... | grep -E 'coverage: [0-7][0-9]\.[0-9]%' && exit 1
|
||||
```go
|
||||
func BenchmarkValidateEmail(b *testing.B) {
|
||||
email := "user@example.com"
|
||||
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
ValidateEmail(email)
|
||||
@@ -212,7 +212,7 @@ func TestUserService(t *testing.T) {
|
||||
"1": {ID: "1", Name: "Alice"},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
service := NewUserService(mock)
|
||||
// ... test logic
|
||||
}
|
||||
@@ -245,16 +245,16 @@ func TestWithPostgres(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
|
||||
// Setup test container
|
||||
ctx := context.Background()
|
||||
container, err := testcontainers.GenericContainer(ctx, ...)
|
||||
assertNoError(t, err)
|
||||
|
||||
|
||||
t.Cleanup(func() {
|
||||
container.Terminate(ctx)
|
||||
})
|
||||
|
||||
|
||||
// ... test logic
|
||||
}
|
||||
```
|
||||
@@ -290,10 +290,10 @@ package user
|
||||
func TestUserHandler(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/users/1", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
|
||||
handler := NewUserHandler(mockRepo)
|
||||
handler.ServeHTTP(rec, req)
|
||||
|
||||
|
||||
assertEqual(t, rec.Code, http.StatusOK)
|
||||
}
|
||||
```
|
||||
@@ -304,7 +304,7 @@ func TestUserHandler(t *testing.T) {
|
||||
func TestWithTimeout(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
|
||||
err := SlowOperation(ctx)
|
||||
if !errors.Is(err, context.DeadlineExceeded) {
|
||||
t.Errorf("expected timeout error, got %v", err)
|
||||
|
||||
@@ -30,7 +30,7 @@ class UserRepository:
|
||||
def find_by_id(self, id: str) -> dict | None:
|
||||
# implementation
|
||||
pass
|
||||
|
||||
|
||||
def save(self, entity: dict) -> dict:
|
||||
# implementation
|
||||
pass
|
||||
@@ -104,11 +104,11 @@ class FileProcessor:
|
||||
def __init__(self, filename: str):
|
||||
self.filename = filename
|
||||
self.file = None
|
||||
|
||||
|
||||
def __enter__(self):
|
||||
self.file = open(self.filename, 'r')
|
||||
return self.file
|
||||
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
if self.file:
|
||||
self.file.close()
|
||||
@@ -173,13 +173,13 @@ def slow_function():
|
||||
def singleton(cls):
|
||||
"""Decorator to make a class a singleton"""
|
||||
instances = {}
|
||||
|
||||
|
||||
@wraps(cls)
|
||||
def get_instance(*args, **kwargs):
|
||||
if cls not in instances:
|
||||
instances[cls] = cls(*args, **kwargs)
|
||||
return instances[cls]
|
||||
|
||||
|
||||
return get_instance
|
||||
|
||||
@singleton
|
||||
@@ -216,7 +216,7 @@ class AsyncDatabase:
|
||||
async def __aenter__(self):
|
||||
await self.connect()
|
||||
return self
|
||||
|
||||
|
||||
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||||
await self.disconnect()
|
||||
|
||||
@@ -238,7 +238,7 @@ class Repository(Generic[T]):
|
||||
"""Generic repository pattern"""
|
||||
def __init__(self, entity_type: type[T]):
|
||||
self.entity_type = entity_type
|
||||
|
||||
|
||||
def find_by_id(self, id: str) -> T | None:
|
||||
# implementation
|
||||
pass
|
||||
@@ -280,17 +280,17 @@ class UserService:
|
||||
self.repository = repository
|
||||
self.logger = logger
|
||||
self.cache = cache
|
||||
|
||||
|
||||
def get_user(self, user_id: str) -> User | None:
|
||||
if self.cache:
|
||||
cached = self.cache.get(user_id)
|
||||
if cached:
|
||||
return cached
|
||||
|
||||
|
||||
user = self.repository.find_by_id(user_id)
|
||||
if user and self.cache:
|
||||
self.cache.set(user_id, user)
|
||||
|
||||
|
||||
return user
|
||||
```
|
||||
|
||||
@@ -375,16 +375,16 @@ class User:
|
||||
def __init__(self, name: str):
|
||||
self._name = name
|
||||
self._email = None
|
||||
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Read-only property"""
|
||||
return self._name
|
||||
|
||||
|
||||
@property
|
||||
def email(self) -> str | None:
|
||||
return self._email
|
||||
|
||||
|
||||
@email.setter
|
||||
def email(self, value: str) -> None:
|
||||
if '@' not in value:
|
||||
|
||||
@@ -23,7 +23,7 @@ Use **pytest** as the testing framework for its powerful features and clean synt
|
||||
def test_user_creation():
|
||||
"""Test that a user can be created with valid data"""
|
||||
user = User(name="Alice", email="alice@example.com")
|
||||
|
||||
|
||||
assert user.name == "Alice"
|
||||
assert user.email == "alice@example.com"
|
||||
assert user.is_active is True
|
||||
@@ -52,12 +52,12 @@ def db_session():
|
||||
engine = create_engine("sqlite:///:memory:")
|
||||
Session = sessionmaker(bind=engine)
|
||||
session = Session()
|
||||
|
||||
|
||||
# Setup
|
||||
Base.metadata.create_all(engine)
|
||||
|
||||
|
||||
yield session
|
||||
|
||||
|
||||
# Teardown
|
||||
session.close()
|
||||
|
||||
@@ -65,7 +65,7 @@ def test_user_repository(db_session):
|
||||
"""Test using the db_session fixture"""
|
||||
repo = UserRepository(db_session)
|
||||
user = repo.create(name="Alice", email="alice@example.com")
|
||||
|
||||
|
||||
assert user.id is not None
|
||||
```
|
||||
|
||||
@@ -206,10 +206,10 @@ def test_user_service_with_mock():
|
||||
"""Test with mock repository"""
|
||||
mock_repo = Mock()
|
||||
mock_repo.find_by_id.return_value = User(id="1", name="Alice")
|
||||
|
||||
|
||||
service = UserService(mock_repo)
|
||||
user = service.get_user("1")
|
||||
|
||||
|
||||
assert user.name == "Alice"
|
||||
mock_repo.find_by_id.assert_called_once_with("1")
|
||||
|
||||
@@ -218,7 +218,7 @@ def test_send_notification(mock_email_service):
|
||||
"""Test with patched dependency"""
|
||||
service = NotificationService()
|
||||
service.send("user@example.com", "Hello")
|
||||
|
||||
|
||||
mock_email_service.send.assert_called_once()
|
||||
```
|
||||
|
||||
@@ -229,10 +229,10 @@ def test_with_mocker(mocker):
|
||||
"""Using pytest-mock plugin"""
|
||||
mock_repo = mocker.Mock()
|
||||
mock_repo.find_by_id.return_value = User(id="1", name="Alice")
|
||||
|
||||
|
||||
service = UserService(mock_repo)
|
||||
user = service.get_user("1")
|
||||
|
||||
|
||||
assert user.name == "Alice"
|
||||
```
|
||||
|
||||
@@ -357,7 +357,7 @@ def test_with_context():
|
||||
"""pytest provides detailed assertion introspection"""
|
||||
result = calculate_total([1, 2, 3])
|
||||
expected = 6
|
||||
|
||||
|
||||
# pytest shows: assert 5 == 6
|
||||
assert result == expected
|
||||
```
|
||||
@@ -378,7 +378,7 @@ import pytest
|
||||
def test_float_comparison():
|
||||
result = 0.1 + 0.2
|
||||
assert result == pytest.approx(0.3)
|
||||
|
||||
|
||||
# With tolerance
|
||||
assert result == pytest.approx(0.3, abs=1e-9)
|
||||
```
|
||||
@@ -402,7 +402,7 @@ def test_exception_details():
|
||||
"""Capture and inspect exception"""
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
validate_user(name="", age=-1)
|
||||
|
||||
|
||||
assert "name" in exc_info.value.errors
|
||||
assert "age" in exc_info.value.errors
|
||||
```
|
||||
|
||||
@@ -24,13 +24,13 @@ This skill ensures all code follows security best practices and identifies poten
|
||||
|
||||
### 1. Secrets Management
|
||||
|
||||
#### ❌ NEVER Do This
|
||||
#### FAIL: NEVER Do This
|
||||
```typescript
|
||||
const apiKey = "sk-proj-xxxxx" // Hardcoded secret
|
||||
const dbPassword = "password123" // In source code
|
||||
```
|
||||
|
||||
#### ✅ ALWAYS Do This
|
||||
#### PASS: ALWAYS Do This
|
||||
```typescript
|
||||
const apiKey = process.env.OPENAI_API_KEY
|
||||
const dbUrl = process.env.DATABASE_URL
|
||||
@@ -110,14 +110,14 @@ function validateFileUpload(file: File) {
|
||||
|
||||
### 3. SQL Injection Prevention
|
||||
|
||||
#### ❌ NEVER Concatenate SQL
|
||||
#### FAIL: NEVER Concatenate SQL
|
||||
```typescript
|
||||
// DANGEROUS - SQL Injection vulnerability
|
||||
const query = `SELECT * FROM users WHERE email = '${userEmail}'`
|
||||
await db.query(query)
|
||||
```
|
||||
|
||||
#### ✅ ALWAYS Use Parameterized Queries
|
||||
#### PASS: ALWAYS Use Parameterized Queries
|
||||
```typescript
|
||||
// Safe - parameterized query
|
||||
const { data } = await supabase
|
||||
@@ -142,10 +142,10 @@ await db.query(
|
||||
|
||||
#### JWT Token Handling
|
||||
```typescript
|
||||
// ❌ WRONG: localStorage (vulnerable to XSS)
|
||||
// FAIL: WRONG: localStorage (vulnerable to XSS)
|
||||
localStorage.setItem('token', token)
|
||||
|
||||
// ✅ CORRECT: httpOnly cookies
|
||||
// PASS: CORRECT: httpOnly cookies
|
||||
res.setHeader('Set-Cookie',
|
||||
`token=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`)
|
||||
```
|
||||
@@ -302,18 +302,18 @@ app.use('/api/search', searchLimiter)
|
||||
|
||||
#### Logging
|
||||
```typescript
|
||||
// ❌ WRONG: Logging sensitive data
|
||||
// FAIL: WRONG: Logging sensitive data
|
||||
console.log('User login:', { email, password })
|
||||
console.log('Payment:', { cardNumber, cvv })
|
||||
|
||||
// ✅ CORRECT: Redact sensitive data
|
||||
// PASS: CORRECT: Redact sensitive data
|
||||
console.log('User login:', { email, userId })
|
||||
console.log('Payment:', { last4: card.last4, userId })
|
||||
```
|
||||
|
||||
#### Error Messages
|
||||
```typescript
|
||||
// ❌ WRONG: Exposing internal details
|
||||
// FAIL: WRONG: Exposing internal details
|
||||
catch (error) {
|
||||
return NextResponse.json(
|
||||
{ error: error.message, stack: error.stack },
|
||||
@@ -321,7 +321,7 @@ catch (error) {
|
||||
)
|
||||
}
|
||||
|
||||
// ✅ CORRECT: Generic error messages
|
||||
// PASS: CORRECT: Generic error messages
|
||||
catch (error) {
|
||||
console.error('Internal error:', error)
|
||||
return NextResponse.json(
|
||||
|
||||
@@ -318,39 +318,39 @@ npm run test:coverage
|
||||
|
||||
## Common Testing Mistakes to Avoid
|
||||
|
||||
### ❌ WRONG: Testing Implementation Details
|
||||
### FAIL: WRONG: Testing Implementation Details
|
||||
```typescript
|
||||
// Don't test internal state
|
||||
expect(component.state.count).toBe(5)
|
||||
```
|
||||
|
||||
### ✅ CORRECT: Test User-Visible Behavior
|
||||
### PASS: CORRECT: Test User-Visible Behavior
|
||||
```typescript
|
||||
// Test what users see
|
||||
expect(screen.getByText('Count: 5')).toBeInTheDocument()
|
||||
```
|
||||
|
||||
### ❌ WRONG: Brittle Selectors
|
||||
### FAIL: WRONG: Brittle Selectors
|
||||
```typescript
|
||||
// Breaks easily
|
||||
await page.click('.css-class-xyz')
|
||||
```
|
||||
|
||||
### ✅ CORRECT: Semantic Selectors
|
||||
### PASS: CORRECT: Semantic Selectors
|
||||
```typescript
|
||||
// Resilient to changes
|
||||
await page.click('button:has-text("Submit")')
|
||||
await page.click('[data-testid="submit-button"]')
|
||||
```
|
||||
|
||||
### ❌ WRONG: No Test Isolation
|
||||
### FAIL: WRONG: No Test Isolation
|
||||
```typescript
|
||||
// Tests depend on each other
|
||||
test('creates user', () => { /* ... */ })
|
||||
test('updates same user', () => { /* depends on previous test */ })
|
||||
```
|
||||
|
||||
### ✅ CORRECT: Independent Tests
|
||||
### PASS: CORRECT: Independent Tests
|
||||
```typescript
|
||||
// Each test sets up its own data
|
||||
test('creates user', () => {
|
||||
|
||||
Reference in New Issue
Block a user