Files
Musify/web-player/src/lib/server/coauthors.test.ts
T

144 lines
4.4 KiB
TypeScript

import { isActionFailure } from '@sveltejs/kit';
import { describe, expect, it, vi } from 'vitest';
import type { ApiClient } from '$lib/server/api';
import {
inviteCoauthors,
loadInvitationCount,
parseUserIds,
removeCoauthor,
respondToInvitation,
searchUsers
} from '$lib/server/coauthors';
const asApi = (fake: unknown) => fake as ApiClient;
function failed(status: number) {
return { error: { status }, response: { status } };
}
describe('parseUserIds', () => {
it('trims, drops blanks and removes duplicates', () => {
expect(parseUserIds([' a ', '', 'b', 'a'])).toEqual(['a', 'b']);
});
});
describe('inviteCoauthors', () => {
it('rejects an empty selection without calling the API', async () => {
const POST = vi.fn();
const result = await inviteCoauthors(asApi({ POST }), 't1', ['', ' ']);
expect(isActionFailure(result)).toBe(true);
expect(POST).not.toHaveBeenCalled();
});
it('rejects more users than the API accepts', async () => {
const POST = vi.fn();
const ids = Array.from({ length: 21 }, (_, i) => String(i));
const result = await inviteCoauthors(asApi({ POST }), 't1', ids);
expect(isActionFailure(result)).toBe(true);
expect(POST).not.toHaveBeenCalled();
});
it('returns the full invite list of the track', async () => {
const POST = vi.fn(async () => ({
data: [
{ userId: 'u1', name: 'Ana', status: 'Pending' },
{ userId: 'u2', name: 'Leo', status: 'Accepted' }
]
}));
const result = await inviteCoauthors(asApi({ POST }), 't1', ['u1', 'u1']);
expect(POST).toHaveBeenCalledWith('/tracks/{id}/coauthors', {
params: { path: { id: 't1' } },
body: { userIds: ['u1'] }
});
expect(result).toEqual({
invites: [
{ userId: 'u1', name: 'Ana', status: 'Pending' },
{ userId: 'u2', name: 'Leo', status: 'Accepted' }
]
});
});
it.each([
[400, 'ti mismo'],
[403, 'autor'],
[409, 'ya tiene']
])('explains a %i response', async (status, text) => {
const POST = vi.fn(async () => failed(status));
const result = await inviteCoauthors(asApi({ POST }), 't1', ['u1']);
expect(isActionFailure(result)).toBe(true);
if (!isActionFailure(result)) return;
expect(result.status).toBe(status);
expect(result.data).toMatchObject({ message: expect.stringContaining(text) });
});
});
describe('removeCoauthor', () => {
it('returns the removed user', async () => {
const DELETE = vi.fn(async () => ({}));
expect(await removeCoauthor(asApi({ DELETE }), 't1', 'u1')).toEqual({ removed: 'u1' });
expect(DELETE).toHaveBeenCalledWith('/tracks/{id}/coauthors/{userId}', {
params: { path: { id: 't1', userId: 'u1' } }
});
});
it('fails when the API refuses', async () => {
const DELETE = vi.fn(async () => failed(403));
expect(isActionFailure(await removeCoauthor(asApi({ DELETE }), 't1', 'u1'))).toBe(true);
});
});
describe('respondToInvitation', () => {
it('calls the endpoint matching the decision', async () => {
const POST = vi.fn(async () => ({}));
await respondToInvitation(asApi({ POST }), 'i1', 'accept');
await respondToInvitation(asApi({ POST }), 'i1', 'reject');
expect(POST).toHaveBeenNthCalledWith(1, '/coauthor-invitations/{id}/accept', {
params: { path: { id: 'i1' } }
});
expect(POST).toHaveBeenNthCalledWith(2, '/coauthor-invitations/{id}/reject', {
params: { path: { id: 'i1' } }
});
});
it('reports an already answered invitation', async () => {
const POST = vi.fn(async () => failed(409));
const result = await respondToInvitation(asApi({ POST }), 'i1', 'accept');
expect(isActionFailure(result)).toBe(true);
if (isActionFailure(result)) expect(result.status).toBe(409);
});
});
describe('loadInvitationCount', () => {
it('reads the total of pending invitations', async () => {
const GET = vi.fn(async () => ({ data: { items: [], totalItemCount: '3' } }));
expect(await loadInvitationCount(asApi({ GET }))).toBe(3);
});
it('falls back to zero when the request fails', async () => {
const GET = vi.fn(async () => failed(500));
expect(await loadInvitationCount(asApi({ GET }))).toBe(0);
});
});
describe('searchUsers', () => {
it('keeps only what the invite UI needs', async () => {
const GET = vi.fn(async () => ({
data: {
items: [
{
id: 'u1',
name: 'Ana',
firstName: 'A',
secondName: null,
profilePictureUrl: null,
isFollowedByViewer: true
}
]
}
}));
expect(await searchUsers(asApi({ GET }), 'an')).toEqual([
{ id: 'u1', name: 'Ana', profilePictureUrl: null }
]);
});
});