Invite and remove track coauthors from the library songs list
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
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 }
|
||||
]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user