Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:23:07 +08:00
commit 8b69ed05af
12 changed files with 812 additions and 0 deletions

280
commands/db-test.md Normal file
View File

@@ -0,0 +1,280 @@
---
description: Database testing with test data setup, transaction rollback, and schema validation
shortcut: dbt
---
# Database Test Manager
Comprehensive database testing utilities including test data generation, transaction management, schema validation, migration testing, and database cleanup.
## What You Do
1. **Test Data Management**
- Generate realistic test data with factories
- Create database fixtures and seeds
- Set up test database state
2. **Transaction Management**
- Wrap tests in transactions with automatic rollback
- Implement database cleanup strategies
- Handle nested transactions
3. **Schema Validation**
- Verify database schema matches models
- Test migrations up and down
- Validate constraints and indexes
4. **Database Testing Patterns**
- Test database queries and performance
- Verify data integrity constraints
- Test stored procedures and triggers
## Usage Pattern
When invoked, you should:
1. Analyze database schema and models
2. Generate test data factories
3. Set up database testing infrastructure
4. Create transaction wrappers for tests
5. Implement database assertions
6. Provide cleanup and teardown strategies
## Output Format
```markdown
## Database Test Suite
### Database: [Type]
**ORM:** [Prisma / TypeORM / SQLAlchemy / ActiveRecord]
**Test Framework:** [Jest / Pytest / RSpec]
### Test Data Factories
\`\`\`javascript
// factories/userFactory.js
import { faker } from '@faker-js/faker';
export const userFactory = {
build: (overrides = {}) => ({
id: faker.string.uuid(),
email: faker.internet.email(),
name: faker.person.fullName(),
createdAt: new Date(),
...overrides
}),
create: async (overrides = {}) => {
const data = userFactory.build(overrides);
return await prisma.user.create({ data });
},
createMany: async (count, overrides = {}) => {
const users = Array.from({ length: count }, () =>
userFactory.build(overrides)
);
return await prisma.user.createMany({ data: users });
}
};
\`\`\`
### Transaction Wrapper
\`\`\`javascript
// testHelpers/dbHelper.js
export const withTransaction = (testFn) => {
return async () => {
return await prisma.$transaction(async (tx) => {
try {
await testFn(tx);
} finally {
// Transaction will rollback automatically
throw new Error('ROLLBACK');
}
}).catch(err => {
if (err.message !== 'ROLLBACK') throw err;
});
};
};
// Usage in tests
describe('User Service', () => {
it('should create user', withTransaction(async (tx) => {
const user = await userFactory.create();
expect(user.email).toBeDefined();
// Rolls back automatically after test
}));
});
\`\`\`
### Database Assertions
\`\`\`javascript
// Custom matchers
expect.extend({
async toExistInDatabase(tableName, conditions) {
const record = await prisma[tableName].findFirst({
where: conditions
});
return {
pass: record !== null,
message: () =>
`Expected ${tableName} with ${JSON.stringify(conditions)} ` +
`to ${record ? '' : 'not '}exist in database`
};
},
async toHaveCount(tableName, expectedCount) {
const count = await prisma[tableName].count();
return {
pass: count === expectedCount,
message: () =>
`Expected ${tableName} to have ${expectedCount} records, ` +
`but found ${count}`
};
}
});
// Usage
await expect('user').toExistInDatabase({ email: '[email protected]' });
await expect('user').toHaveCount(5);
\`\`\`
### Migration Testing
\`\`\`javascript
describe('Database Migrations', () => {
it('should run migrations up and down', async () => {
// Run all migrations
await runMigrations('up');
// Verify schema
const tables = await getTables();
expect(tables).toContain('users');
expect(tables).toContain('posts');
// Rollback migrations
await runMigrations('down');
// Verify tables removed
const tablesAfter = await getTables();
expect(tablesAfter).not.toContain('users');
});
it('should enforce constraints', async () => {
const user = await userFactory.create();
// Test foreign key constraint
await expect(
prisma.post.create({
data: {
title: 'Test',
userId: 'non-existent-id'
}
})
).rejects.toThrow('Foreign key constraint');
// Test unique constraint
await expect(
userFactory.create({ email: user.email })
).rejects.toThrow('Unique constraint');
});
});
\`\`\`
### Query Performance Testing
\`\`\`javascript
describe('Query Performance', () => {
beforeAll(async () => {
// Seed large dataset
await userFactory.createMany(10000);
});
it('should query efficiently with indexes', async () => {
const start = performance.now();
await prisma.user.findMany({
where: { email: { contains: '@example.com' } }
});
const duration = performance.now() - start;
expect(duration).toBeLessThan(100); // 100ms threshold
});
it('should use proper indexes', async () => {
const explain = await prisma.$queryRaw`
EXPLAIN ANALYZE
SELECT * FROM users WHERE email LIKE '%@example.com%'
`;
expect(explain).toContain('Index Scan');
expect(explain).not.toContain('Seq Scan'); // No full table scan
});
});
\`\`\`
### Database Cleanup
\`\`\`javascript
// testSetup.js
beforeEach(async () => {
// Clear all tables in reverse dependency order
await prisma.comment.deleteMany();
await prisma.post.deleteMany();
await prisma.user.deleteMany();
});
afterAll(async () => {
await prisma.$disconnect();
});
\`\`\`
### Test Configuration
\`\`\`javascript
// jest.config.js
module.exports = {
testEnvironment: 'node',
setupFilesAfterEnv: ['./tests/setup.js'],
globalSetup: './tests/globalSetup.js',
globalTeardown: './tests/globalTeardown.js'
};
// globalSetup.js
module.exports = async () => {
// Create test database
execSync('createdb myapp_test');
// Run migrations
process.env.DATABASE_URL = 'postgresql://localhost/myapp_test';
execSync('npx prisma migrate deploy');
};
\`\`\`
### Next Steps
- [ ] Implement test data factories
- [ ] Set up transaction rollback
- [ ] Add database assertions
- [ ] Test migrations
- [ ] Configure test database
```
## Supported Databases
- PostgreSQL
- MySQL
- SQLite
- MongoDB
- SQL Server
## ORMs/Query Builders
- Prisma
- TypeORM
- Sequelize
- SQLAlchemy
- ActiveRecord