Initial release v1.0.0
Open-source marketplace for AI Agent skills. Features: - Next.js 15 web app with i18n (en/fa) - CLI tool for skill installation (npx skillhub) - GitHub crawler/indexer with multi-strategy discovery - Security scanning for all indexed skills - Self-hostable with Docker Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
214
packages/db/src/test-utils.ts
Normal file
214
packages/db/src/test-utils.ts
Normal file
@@ -0,0 +1,214 @@
|
||||
/**
|
||||
* Test utilities for @skillhub/db package
|
||||
*
|
||||
* Factory functions and helpers for creating test data
|
||||
*/
|
||||
|
||||
import type { skills, categories, users, ratings, favorites, installations } from './schema.js';
|
||||
|
||||
type SkillInsert = typeof skills.$inferInsert;
|
||||
type CategoryInsert = typeof categories.$inferInsert;
|
||||
type UserInsert = typeof users.$inferInsert;
|
||||
type RatingInsert = typeof ratings.$inferInsert;
|
||||
type FavoriteInsert = typeof favorites.$inferInsert;
|
||||
type InstallationInsert = typeof installations.$inferInsert;
|
||||
|
||||
/**
|
||||
* Create a test skill with sensible defaults
|
||||
*/
|
||||
export function createTestSkill(overrides: Partial<SkillInsert> = {}): SkillInsert {
|
||||
const id = overrides.id || 'test-owner/test-repo/test-skill';
|
||||
return {
|
||||
id,
|
||||
name: 'test-skill',
|
||||
description: 'A test skill for unit testing',
|
||||
githubOwner: 'test-owner',
|
||||
githubRepo: 'test-repo',
|
||||
skillPath: 'skills/test-skill',
|
||||
branch: 'main',
|
||||
version: '1.0.0',
|
||||
license: 'MIT',
|
||||
author: 'Test Author',
|
||||
compatibility: {
|
||||
platforms: ['claude', 'codex'],
|
||||
},
|
||||
githubStars: 100,
|
||||
githubForks: 10,
|
||||
downloadCount: 50,
|
||||
viewCount: 200,
|
||||
rating: 4,
|
||||
ratingCount: 5,
|
||||
ratingSum: 20,
|
||||
securityScore: 85,
|
||||
isVerified: false,
|
||||
isFeatured: false,
|
||||
rawContent: '# Test Skill\n\nThis is a test skill.',
|
||||
contentHash: 'abc123',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
indexedAt: new Date(),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a test category with sensible defaults
|
||||
*/
|
||||
export function createTestCategory(overrides: Partial<CategoryInsert> = {}): CategoryInsert {
|
||||
const id = overrides.id || `cat-${Date.now()}`;
|
||||
return {
|
||||
id,
|
||||
name: 'Test Category',
|
||||
slug: overrides.slug || `test-category-${Date.now()}`,
|
||||
description: 'A test category',
|
||||
icon: 'folder',
|
||||
color: '#3B82F6',
|
||||
sortOrder: 0,
|
||||
skillCount: 0,
|
||||
createdAt: new Date(),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a test user with sensible defaults
|
||||
*/
|
||||
export function createTestUser(overrides: Partial<UserInsert> = {}): UserInsert {
|
||||
const id = overrides.id || `user-${Date.now()}`;
|
||||
return {
|
||||
id,
|
||||
githubId: overrides.githubId || `gh-${Date.now()}`,
|
||||
username: 'testuser',
|
||||
displayName: 'Test User',
|
||||
email: 'test@example.com',
|
||||
avatarUrl: 'https://example.com/avatar.png',
|
||||
bio: 'A test user',
|
||||
isAdmin: false,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
lastLoginAt: new Date(),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a test rating with sensible defaults
|
||||
*/
|
||||
export function createTestRating(overrides: Partial<RatingInsert> = {}): RatingInsert {
|
||||
return {
|
||||
id: overrides.id || `rating-${Date.now()}`,
|
||||
skillId: 'test-owner/test-repo/test-skill',
|
||||
userId: 'user-1',
|
||||
rating: 4,
|
||||
review: 'Great skill!',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a test favorite with sensible defaults
|
||||
*/
|
||||
export function createTestFavorite(overrides: Partial<FavoriteInsert> = {}): FavoriteInsert {
|
||||
return {
|
||||
userId: 'user-1',
|
||||
skillId: 'test-owner/test-repo/test-skill',
|
||||
createdAt: new Date(),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a test installation with sensible defaults
|
||||
*/
|
||||
export function createTestInstallation(overrides: Partial<InstallationInsert> = {}): InstallationInsert {
|
||||
return {
|
||||
id: overrides.id || `install-${Date.now()}`,
|
||||
skillId: 'test-owner/test-repo/test-skill',
|
||||
platform: 'claude',
|
||||
method: 'cli',
|
||||
createdAt: new Date(),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock database type for testing
|
||||
* This provides a mock implementation of the database client
|
||||
*/
|
||||
export interface MockDb {
|
||||
select: ReturnType<typeof vi.fn>;
|
||||
insert: ReturnType<typeof vi.fn>;
|
||||
update: ReturnType<typeof vi.fn>;
|
||||
delete: ReturnType<typeof vi.fn>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a chainable mock for database operations
|
||||
*/
|
||||
export function createMockDbChain(finalResult: unknown = []) {
|
||||
const chain: Record<string, ReturnType<typeof vi.fn>> = {};
|
||||
|
||||
const createChainedMock = (): ReturnType<typeof vi.fn> => {
|
||||
const mock = vi.fn().mockImplementation(() => {
|
||||
return new Proxy({}, {
|
||||
get: (_target, prop: string) => {
|
||||
if (prop === 'then') {
|
||||
return (resolve: (value: unknown) => void) => resolve(finalResult);
|
||||
}
|
||||
if (!chain[prop]) {
|
||||
chain[prop] = createChainedMock();
|
||||
}
|
||||
return chain[prop];
|
||||
},
|
||||
});
|
||||
});
|
||||
return mock;
|
||||
};
|
||||
|
||||
return {
|
||||
select: createChainedMock(),
|
||||
insert: createChainedMock(),
|
||||
update: createChainedMock(),
|
||||
delete: createChainedMock(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a simple mock database for unit tests
|
||||
*/
|
||||
export function createMockDb() {
|
||||
const mockResult: unknown[] = [];
|
||||
|
||||
const createChained = () => {
|
||||
const chained = {
|
||||
from: vi.fn().mockReturnThis(),
|
||||
where: vi.fn().mockReturnThis(),
|
||||
limit: vi.fn().mockReturnThis(),
|
||||
offset: vi.fn().mockReturnThis(),
|
||||
orderBy: vi.fn().mockReturnThis(),
|
||||
innerJoin: vi.fn().mockReturnThis(),
|
||||
leftJoin: vi.fn().mockReturnThis(),
|
||||
groupBy: vi.fn().mockReturnThis(),
|
||||
values: vi.fn().mockReturnThis(),
|
||||
set: vi.fn().mockReturnThis(),
|
||||
onConflictDoUpdate: vi.fn().mockReturnThis(),
|
||||
onConflictDoNothing: vi.fn().mockReturnThis(),
|
||||
returning: vi.fn().mockResolvedValue(mockResult),
|
||||
then: (resolve: (v: unknown) => void) => Promise.resolve(mockResult).then(resolve),
|
||||
};
|
||||
return chained;
|
||||
};
|
||||
|
||||
return {
|
||||
select: vi.fn().mockReturnValue(createChained()),
|
||||
insert: vi.fn().mockReturnValue(createChained()),
|
||||
update: vi.fn().mockReturnValue(createChained()),
|
||||
delete: vi.fn().mockReturnValue(createChained()),
|
||||
_setMockResult: (result: unknown[]) => {
|
||||
mockResult.length = 0;
|
||||
mockResult.push(...result);
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user