.gemini/skills/testing-patterns/SKILL.md
Testing patterns for LivestockAI. Use when writing unit tests, property-based tests with fast-check, or integration tests with the test database.
npx skillsauth add captjay98/gemini-livestockai testing-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.
You are a QA specialist for LivestockAI, expert in Vitest, fast-check, and integration testing.
# IMPORTANT: Use "bun run test" not "bun test"
bun run test # Unit tests (vitest)
bun run test:integration # Integration tests (requires DATABASE_URL_TEST)
bun run test:all # All tests
bun run test:coverage # Coverage report
Use for mathematical invariants and business logic:
import { describe, it, expect } from 'vitest'
import * as fc from 'fast-check'
describe('calculateFCR', () => {
it('should always be positive when inputs are positive', () => {
fc.assert(
fc.property(
fc.double({ min: 0.01, max: 10000, noNaN: true }),
fc.double({ min: 0.01, max: 10000, noNaN: true }),
(feed, gain) => {
const fcr = calculateFCR(feed, gain)
expect(fcr).toBeGreaterThan(0)
},
),
)
})
it('should return 0 when weight gain is 0 or negative', () => {
fc.assert(
fc.property(
fc.double({ min: 0.01, max: 10000, noNaN: true }),
fc.double({ max: 0, noNaN: true }),
(feed, gain) => {
expect(calculateFCR(feed, gain)).toBe(0)
},
),
)
})
})
// Always use noNaN: true for doubles
fc.double({ min: 0, max: 1000, noNaN: true })
// Always use noInvalidDate: true for dates
fc.date({
min: new Date('2020-01-01'),
max: new Date('2030-01-01'),
noInvalidDate: true,
})
// Constrain longitude to avoid date line issues
fc.double({ min: -170, max: 170, noNaN: true })
import { afterAll, afterEach, beforeEach, describe, expect, it } from 'vitest'
import {
closeTestDb,
getTestDb,
resetTestDb,
seedTestUser,
seedTestFarm,
truncateAllTables,
} from '../../helpers/db-integration'
describe('Batch Integration Tests', () => {
beforeEach(async () => {
await truncateAllTables() // Clean slate
})
afterEach(() => {
resetTestDb() // Clear transaction state (NOT closeTestDb!)
})
afterAll(async () => {
await closeTestDb() // Only here!
})
it('should create batch', async () => {
// Use unique identifiers
const { userId } = await seedTestUser({
email: `test-${Date.now()}@example.com`,
})
const { farmId } = await seedTestFarm(userId)
const db = getTestDb()
// ... test code
})
})
truncateAllTables() in beforeEach() - Reset database stateresetTestDb() in afterEach() - Clear transaction statecloseTestDb() in afterAll() ONLY - Don't destroy connection earlyimport { describe, it, expect } from 'vitest'
describe('validateBatchData', () => {
it('should return null for valid data', () => {
const result = validateBatchData({
batchName: 'Test Batch',
initialQuantity: 100,
costPerUnit: 50,
})
expect(result).toBeNull()
})
it('should return error for negative quantity', () => {
const result = validateBatchData({
batchName: 'Test',
initialQuantity: -1,
costPerUnit: 50,
})
expect(result).toBe('Quantity must be positive')
})
})
tests/
├── features/
│ ├── batches/
│ │ ├── batches.test.ts # Unit tests
│ │ └── batches.property.test.ts # Property tests
│ └── ...
├── integration/
│ ├── batches.integration.test.ts # DB tests
│ └── ...
└── helpers/
├── db-integration.ts # Test DB helpers
└── db-mock.ts # Mock helpers
import { vi } from 'vitest'
// Mock server function
vi.mock('~/features/batches/server', () => ({
getBatchesFn: vi.fn().mockResolvedValue([]),
}))
// Mock database
vi.mock('~/lib/db', () => ({
getDb: vi.fn().mockResolvedValue(mockDb),
}))
data-ai
Input validation patterns with Zod in LivestockAI server functions
testing
Unit testing patterns with Vitest in LivestockAI
tools
Server → Service → Repository pattern for feature organization
data-ai
Server-side rendering and server functions with TanStack Start in LivestockAI