skills/ballee/test-patterns/SKILL.md
Testing patterns for Ballee using Vitest E2E tests with dual-client architecture, RLS validation, and authentication patterns. Use when writing tests, validating RLS policies, or debugging test failures.
npx skillsauth add javeedishaq/ai-workflow-orchestrator skills/ballee/test-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
import { createAdminClient, createAuthenticatedClient } from '@/tests/utils/supabase';
describe('Feature Tests', () => {
let adminClient: SupabaseClient; // Bypasses RLS - for seeding
let userClient: SupabaseClient; // Respects RLS - for testing
beforeAll(async () => {
adminClient = createAdminClient();
userClient = await createAuthenticatedClient('[email protected]');
});
});
| Client | Use For |
|--------|---------|
| adminClient | Seeding data, cleanup, verifying DB state |
| userClient | Testing user-facing operations, RLS validation |
it('should allow access to own client data', async () => {
// Seed with admin
const { data: item } = await adminClient
.from('items')
.insert({ client_id: userClientId, name: 'Test' })
.select()
.single();
// Verify user can access
const { data, error } = await userClient
.from('items')
.select()
.eq('id', item.id)
.single();
expect(error).toBeNull();
expect(data.id).toBe(item.id);
});
it('should deny access to other client data', async () => {
// Seed with different client
const { data: otherItem } = await adminClient
.from('items')
.insert({ client_id: otherClientId, name: 'Other' })
.select()
.single();
// Verify user CANNOT access
const { data, error } = await userClient
.from('items')
.select()
.eq('id', otherItem.id)
.single();
expect(data).toBeNull();
});
it('super admin should access all data', async () => {
const superAdminClient = await createAuthenticatedClient('[email protected]');
const { data, error } = await superAdminClient
.from('items')
.select()
.eq('id', otherClientItem.id)
.single();
expect(error).toBeNull();
expect(data).not.toBeNull();
});
apps/web/__tests__/
├── e2e/
│ ├── events.test.ts # E2E feature tests
│ └── auth.test.ts
├── unit/
│ ├── services/ # Service unit tests
│ └── utils/
└── utils/
└── supabase.ts # Test client utilities
describe('EventService', () => {
it('should create event and return Result.success', async () => {
const service = new EventService(userClient);
const result = await service.create({ name: 'Test Event' });
expect(result.success).toBe(true);
expect(result.data.name).toBe('Test Event');
});
it('should return Result.error on failure', async () => {
const service = new EventService(userClient);
const result = await service.create({ name: '' }); // Invalid
expect(result.success).toBe(false);
expect(result.error).toBeDefined();
});
});
it('should require authentication', async () => {
// Call without auth context
const result = await createEventAction(formData);
expect(result.error).toContain('Unauthorized');
});
pnpm test # Run all tests
pnpm test:e2e # Run E2E tests only
pnpm test:unit # Run unit tests only
pnpm test --coverage # With coverage report
| Issue | Fix | |-------|-----| | RLS blocking test data | Use adminClient for seeding | | Test pollution | Clean up in afterEach/afterAll | | Flaky auth tests | Use fresh authenticated client per test | | Missing test data | Seed required dependencies first |
afterAll(async () => {
// Clean up test data (use admin to bypass RLS)
await adminClient.from('items').delete().eq('name', 'Test%');
});
tools
# Test Patterns Testing patterns for reliable, maintainable, and fast tests. > **Template Usage:** Customize for your test framework (Vitest, Jest, Playwright, etc.) and assertion library. ## Test Structure ```typescript // user.test.ts import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { userService } from '@/services/user.service'; import { createTestUser, cleanupTestData } from '@/tests/helpers'; describe('UserService', () => { let testUserId: string; befor
tools
# State Management Patterns Client-side state management patterns for modern applications. > **Template Usage:** Customize for your state library (React Query, Zustand, Jotai, Redux, etc.). ## State Categories | Type | Description | Solution | |------|-------------|----------| | **Server State** | Data from API/database | React Query, SWR | | **Client State** | UI state, user preferences | Zustand, Jotai, useState | | **Form State** | Form inputs, validation | React Hook Form, Formik | | **U
development
# Service Patterns Service layer patterns for clean architecture with proper error handling, logging, and type safety. > **Template Usage:** Customize for your ORM (Prisma, Drizzle, TypeORM, etc.) and logging solution. ## Result Type Pattern Never throw exceptions from services. Always return a Result type. ```typescript // lib/result.ts export type Result<T, E = Error> = | { success: true; data: T } | { success: false; error: E }; export function ok<T>(data: T): Result<T, never> { r
testing
# Row-Level Security Patterns Database security patterns for multi-tenant and user-scoped data. > **Template Usage:** Customize for your database (PostgreSQL, Supabase, etc.) and auth system. ## RLS Fundamentals ### Enable RLS on Tables ```sql -- Enable RLS (required before policies take effect) ALTER TABLE users ENABLE ROW LEVEL SECURITY; ALTER TABLE posts ENABLE ROW LEVEL SECURITY; ALTER TABLE comments ENABLE ROW LEVEL SECURITY; -- Force RLS for table owners too (recommended) ALTER TABLE