ThatAIguyA world of ideas

THE CONSCIOUSNESS ATLAS
Read. Experiment. Follow the evidence.

ConsciousnessLearn · Explore · Joseph Anady

Janady13/llms-txt-hub / apps/web/app/api/user/check-username/__tests__/route.test.ts

Source identity, revision, and recovered contents.

GitHub sourceSource text recovered

Source identity

FieldRecorded value
Source typeGitHub source
Original recordJanady13/llms-txt-hub / apps/web/app/api/user/check-username/__tests__/route.test.ts
DateNot specified in source
RepositoryJanady13/llms-txt-hub
Revisione0f4bf978d4df168369c7601d9c5f5ea89e84377
Branchmain
Path or archive memberapps/web/app/api/user/check-username/__tests__/route.test.ts
Original SHA-25639cd1f8f4a39ab5d1ccb35a91853c09b595af687041d3724c7c1a9ed160a3459
Git blob SHA2444e618066e50f107b8ff56a9cd077d09f4a14f
Reading-copy SHA-25670da966d21904bce444a3ef0008f8036421bdc6bf3c08a196a49dc041691587e

Original source ↗

Recovered source transcript

Credential-bearing and unrelated personal details were removed from this reading copy. The source identity and original checksum remain recorded.

/** * Tests for check-username API route * * Tests username validation, availability checking, authentication, and rate limiting. */ // Mock dependencies BEFORE imports jest.mock('@thedaviddias/auth') jest.mock('@thedaviddias/logging', () => ({ logger: { error: jest.fn() } })) // Mock Clerk with proper client structure jest.mock('@clerk/backend', () => { const mockUsers = { getUser: jest.fn(), getUserList: jest.fn() } return { createClerkClient: jest.fn(() => ({ users: mockUsers })), __mockUsers: mockUsers // Export for test access } }) import { auth } from '@thedaviddias/auth' import { MALICIOUS_INPUTS } from '@/app/api/__tests__/test-helpers' // Import route AFTER mocks are set up import { POST } from '@/app/api/user/check-username/route' import { clearRateLimiting } from '@/lib/security-utils' // Mock environment variable for Clerk process.env.CLERK_SECRET_KEY = '[credential removed]' /** * Helper to clear the route's internal rate limiting * * @returns void */ const _clearRouteRateLimit = () => { // Access the rate limit map from the module jest.resetModules() } describe('Check Username API Route', () => { const mockAuth = auth as jest.MockedFunction<typeof auth> // Get the mock users from the mocked module const mockClerkModule = jest.requireMock('@clerk/backend') const mockClerkUsers = mockClerkModule.__mockUsers beforeEach(() => { jest.clearAllMocks() clearRateLimiting() // Clear rate limiting between tests // Setup Clerk mocks mockClerkUsers.getUser.mockResolvedValue({ id: 'test-user-id', username: 'currentuser', email: 'test@example.com' }) mockClerkUsers.getUserList.mockResolvedValue({ data: [], totalCount: 0 }) // Setup default mocks mockAuth.mockResolvedValue({ user: { id: 'test-user-id', email: 'test@example.com', user_metadata: { user_name: null, full_name: null, avatar_url: null } } }) }) describe('Authentication', () => { it('returns 401 when user is not authenticated', async () => { mockAuth.mockResolvedValueOnce(null) const request = new Request('http://localhost/api/user/check-username', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'testuser' }) }) const response = await POST(request) const data = await response.json() expect(response.status).toBe(401) expect(data.error).toBe('Unauthorized') }) it('returns 401 when user session lacks ID', async () => { mockAuth.mockResolvedValueOnce({ user: { id: '', email: 'test@example.com', user_metadata: { user_name: null, full_name: null, avatar_url: null } } }) const request = new Request('http://localhost/api/user/check-username', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'testuser' }) }) const response = await POST(request) const data = await response.json() expect(response.status).toBe(401) expect(data.error).toBe('Unauthorized') }) }) describe('Request Validation', () => { it('returns 400 when username is missing', async () => { const request = new Request('http://localhost/api/user/check-username', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({}) }) const response = await POST(request) const data = await response.json() expect(response.status).toBe(400) expect(data.error).toBe('Username is required') }) it('returns 400 when username is not a string', async () => { const request = new Request('http://localhost/api/user/check-username', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 123 }) }) const response = await POST(request) const data = await response.json() expect(response.status).toBe(400) expect(data.error).toBe('Username is required') }) it('returns error for username shorter than 3 characters', async () => { const request = new Request('http://localhost/api/user/check-username', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'ab' }) }) const response = await POST(request) const data = await response.json() expect(response.status).toBe(200) expect(data.available).toBe(false) expect(data.error).toBe('Username must be at least 3 characters') }) it('returns error for username longer than 30 characters', async () => { const request = new Request('http://localhost/api/user/check-username', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'a'.repeat(31) }) }) const response = await POST(request) const data = await response.json() expect(response.status).toBe(200) expect(data.available).toBe(false) expect(data.error).toBe('Username must be 30 characters or less') }) it('returns error for username with invalid characters', async () => { const invalidUsernames = ['user@name', 'user name', 'user.name', 'user!name', 'user#name'] for (const username of invalidUsernames) { const request = new Request('http://localhost/api/user/check-username', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username }) }) const response = await POST(request) const data = await response.json() expect(response.status).toBe(200) expect(data.available).toBe(false) expect(data.error).toBe( 'Username can only contain letters, numbers, underscores, and hyphens' ) } }) it('accepts valid username formats', async () => { const validUsernames = ['username', 'user_name', 'user-name', 'User123', 'USER_NAME_123'] for (const username of validUsernames) { const request = new Request('http://localhost/api/user/check-username', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username }) }) const response = await POST(request) const data = await response.json() if (response.status !== 200) { console.error('Test failed for username:', username) console.error('Response data:', data) } expect(response.status).toBe(200) expect(data.available).toBe(true) } }) }) describe('Username Availability', () => { it('returns available true for unused username', async () => { mockClerkUsers.getUserList.mockResolvedValueOnce({ data: [] }) const request = new Request('http://localhost/api/user/check-username', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'newusername' }) }) const response = await POST(request) const data = await response.json() expect(response.status).toBe(200) expect(data.available).toBe(true) expect(mockClerkUsers.getUserList).toHaveBeenCalledWith({ username: ['newusername'], limit: 1 }) }) it('returns available false for taken username', async () => { mockClerkUsers.getUserList.mockResolvedValueOnce({ data: [{ id: 'other-user', username: 'takenusername' }] }) const request = new Request('http://localhost/api/user/check-username', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'takenusername' }) }) const response = await POST(request) const data = await response.json() expect(response.status).toBe(200) expect(data.available).toBe(false) }) it('returns available true when checking own username', async () => { mockClerkUsers.getUser.mockResolvedValueOnce({ id: 'test-user-id', username: 'myusername' }) const request = new Request('http://localhost/api/user/check-username', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'myusername' }) }) const response = await POST(request) const data = await response.json() expect(response.status).toBe(200) expect(data.available).toBe(true) // Should not query getUserList for own username expect(mockClerkUsers.getUserList).not.toHaveBeenCalled() }) it('handles case-insensitive username checking', async () => { mockClerkUsers.getUserList.mockResolvedValueOnce({ data: [] }) const request = new Request('http://localhost/api/user/check-username', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'UserName' }) }) const response = await POST(request) const _data = await response.json() expect(response.status).toBe(200) // Should check lowercase version expect(mockClerkUsers.getUserList).toHaveBeenCalledWith({ username: ['username'], limit: 1 }) }) }) describe('Security', () => { it('sanitizes username input to prevent XSS', async () => { const request = new Request('http://localhost/api/user/check-username', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: '<script>alert(1)</script>' }) }) const response = await POST(request) const data = await response.json() expect(response.status).toBe(400) expect(data.error).toBe('Invalid characters in username') }) it('rejects malicious input patterns', async () => { for (const maliciousInput of MALICIOUS_INPUTS.xss) { const request = new Request('http://localhost/api/user/check-username', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: maliciousInput }) }) const response = await POST(request) expect(response.status).toBeGreaterThanOrEqual(200) expect(response.status).toBeLessThan(500) } }) }) describe('Rate Limiting', () => { it('enforces rate limiting after max requests', async () => { // Use a unique user ID for this test to avoid conflicts mockAuth.mockResolvedValue({ user: { id: 'rate-limit-test-user', email: 'rate-limit@example.com', user_metadata: { user_name: null, full_name: null, avatar_url: null } } }) // Make 20 requests (the limit) for (let i = 0; i < 20; i++) { const request = new Request('http://localhost/api/user/check-username', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: `username${i}` }) }) const response = await POST(request) if (response.status !== 200) { console.error(`Request ${i + 1} failed with status ${response.status}`) } expect(response.status).toBe(200) } // 21st request should be rate limited const rateLimitedRequest = new Request('http://localhost/api/user/check-username', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'anotheruser' }) }) const response = await POST(rateLimitedRequest as any) const data = await response.json() expect(response.status).toBe(429) expect(data.error).toBe('Too many requests. Please try again later.') }) it('applies rate limiting per user', async () => { // First user makes requests mockAuth.mockResolvedValue({ user: { id: 'user-1', email: 'user1@example.com', user_metadata: { user_name: null, full_name: null, avatar_url: null } } }) const request1 = new Request('http://localhost/api/user/check-username', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'username1' }) }) const response1 = await POST(request1) expect(response1.status).toBe(200) // Different user can also make requests mockAuth.mockResolvedValue({ user: { id: 'user-2', email: 'user2@example.com', user_metadata: { user_name: null, full_name: null, avatar_url: null } } }) const request2 = new Request('http://localhost/api/user/check-username', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'username2' }) }) const response2 = await POST(request2) expect(response2.status).toBe(200) }) }) describe('Error Handling', () => { it('handles Clerk API errors gracefully', async () => { // Use unique user ID to avoid rate limiting mockAuth.mockResolvedValue({ user: { id: 'error-test-user-1', email: 'error1@example.com', user_metadata: { user_name: null, full_name: null, avatar_url: null } } }) mockClerkUsers.getUserList.mockRejectedValueOnce(new Error('Clerk API error')) const request = new Request('http://localhost/api/user/check-username', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'testuser' }) }) const response = await POST(request) const data = await response.json() expect(response.status).toBe(200) expect(data.available).toBe(false) expect(data.error).toBe('Unable to check username availability') }) it('handles malformed JSON gracefully', async () => { // Use unique user ID to avoid rate limiting mockAuth.mockResolvedValue({ user: { id: 'error-test-user-2', email: 'error2@example.com', user_metadata: { user_name: null, full_name: null, avatar_url: null } } }) const request = new Request('http://localhost/api/user/check-username', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: 'invalid json' }) const response = await POST(request) const data = await response.json() expect(response.status).toBe(400) expect(data.error).toBe('Invalid request body') }) it('handles unexpected errors', async () => { mockAuth.mockRejectedValueOnce(new Error('Unexpected error')) const request = new Request('http://localhost/api/user/check-username', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'testuser' }) }) const response = await POST(request) const data = await response.json() expect(response.status).toBe(500) expect(data.error).toBe('Failed to check username') }) }) })

Search equations, builds, manuscript text, and recovered sources.