You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

126 lines
4.9 KiB

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { supabase } from '../supabase.js'
import * as profileApi from './profile.js'
const nextResults = []
function createChain() {
const getResult = () => nextResults.shift() ?? { data: null, error: null }
const chain = {
from: vi.fn(function () { return this }),
select: vi.fn(function () { return this }),
eq: vi.fn(function () { return this }),
ilike: vi.fn(function () { return this }),
limit: vi.fn(function () { return this }),
single: vi.fn(() => Promise.resolve(getResult())),
update: vi.fn(function () { return this }),
then(resolve) { return Promise.resolve(getResult()).then(resolve) },
catch(fn) { return Promise.resolve(getResult()).catch(fn) },
}
return chain
}
const mockStorageFrom = {
getPublicUrl: vi.fn(() => ({ data: { publicUrl: 'https://example.com/avatars/path' } })),
upload: vi.fn(() => Promise.resolve({ error: null })),
}
vi.mock('../supabase.js', () => ({
supabase: {
from: vi.fn(() => createChain()),
storage: { from: vi.fn(() => mockStorageFrom) },
},
}))
describe('profile API', () => {
beforeEach(() => {
vi.clearAllMocks()
nextResults.length = 0
})
describe('getEmailForUsername', () => {
it('returns null for empty or whitespace username', async () => {
expect(await profileApi.getEmailForUsername(supabase, '')).toBe(null)
expect(await profileApi.getEmailForUsername(supabase, ' ')).toBe(null)
expect(await profileApi.getEmailForUsername(supabase, null)).toBe(null)
})
it('returns email when profile found', async () => {
nextResults.push({ data: [{ email: 'user@example.com' }], error: null })
const email = await profileApi.getEmailForUsername(supabase, 'johndoe')
expect(email).toBe('user@example.com')
})
it('returns null when error or no data', async () => {
nextResults.push({ data: [], error: null })
expect(await profileApi.getEmailForUsername(supabase, 'x')).toBe(null)
nextResults.push({ data: null, error: { message: 'err' } })
expect(await profileApi.getEmailForUsername(supabase, 'y')).toBe(null)
nextResults.push({ data: [{}], error: null })
expect(await profileApi.getEmailForUsername(supabase, 'z')).toBe(null)
})
})
describe('getProfile', () => {
it('returns profile when found', async () => {
const p = { id: 'u1', display_name: 'Alice', email: 'a@b.com', avatar_url: null }
nextResults.push({ data: p, error: null })
const result = await profileApi.getProfile(supabase, 'u1')
expect(result).toEqual(p)
})
it('returns null when not found (PGRST116)', async () => {
nextResults.push({ data: null, error: { code: 'PGRST116' } })
const result = await profileApi.getProfile(supabase, 'u1')
expect(result).toBe(null)
})
it('throws when other error', async () => {
nextResults.push({ data: null, error: new Error('Network error') })
await expect(profileApi.getProfile(supabase, 'u1')).rejects.toThrow('Network error')
})
})
describe('updateProfile', () => {
it('updates display_name and returns data', async () => {
const updated = { id: 'u1', display_name: 'New Name', email: 'a@b.com', avatar_url: null }
nextResults.push({ data: updated, error: null })
const result = await profileApi.updateProfile(supabase, 'u1', { display_name: 'New Name' })
expect(result).toEqual(updated)
})
it('trims display_name and allows avatar_url', async () => {
nextResults.push({ data: { id: 'u1' }, error: null })
await profileApi.updateProfile(supabase, 'u1', { display_name: ' Trim ', avatar_url: 'path/to/av.jpg' })
expect(nextResults.length).toBe(0)
})
it('throws on error', async () => {
nextResults.push({ data: null, error: new Error('DB error') })
await expect(profileApi.updateProfile(supabase, 'u1', { display_name: 'X' })).rejects.toThrow('DB error')
})
})
describe('getAvatarPublicUrl', () => {
it('returns publicUrl from storage', () => {
const url = profileApi.getAvatarPublicUrl('user1/avatar.jpg')
expect(url).toBe('https://example.com/avatars/path')
expect(mockStorageFrom.getPublicUrl).toHaveBeenCalledWith('user1/avatar.jpg')
})
})
describe('uploadAvatar', () => {
it('uploads file and returns public URL', async () => {
const file = new File(['x'], 'photo.jpg', { type: 'image/jpeg' })
const url = await profileApi.uploadAvatar('u1', file)
expect(mockStorageFrom.upload).toHaveBeenCalledWith('u1/avatar.jpg', file, { upsert: true, contentType: 'image/jpeg' })
expect(url).toBe('https://example.com/avatars/path')
})
it('throws on upload error', async () => {
mockStorageFrom.upload.mockResolvedValueOnce({ error: new Error('Storage full') })
const file = new File(['x'], 'a.png', { type: 'image/png' })
await expect(profileApi.uploadAvatar('u1', file)).rejects.toThrow('Storage full')
})
})
})

Powered by TurnKey Linux.