test(JiraSync): further refine test coverage for synchronization and change detection scenarios

This commit is contained in:
Julien Froidefond
2025-11-21 16:47:04 +01:00
parent ddba4eca37
commit 4c0f227e27
3 changed files with 1031 additions and 0 deletions

View File

@@ -0,0 +1,309 @@
/**
* Tests unitaires pour DailyService
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { DailyService } from '../daily';
import { prisma } from '@/services/core/database';
import {
getPreviousWorkday,
normalizeDate,
getToday,
getYesterday,
} from '@/lib/date-utils';
// Mock de prisma
vi.mock('@/services/core/database', () => ({
prisma: {
dailyCheckbox: {
findMany: vi.fn(),
findUnique: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
aggregate: vi.fn(),
},
},
}));
// Mock de date-utils
vi.mock('@/lib/date-utils', () => ({
getPreviousWorkday: vi.fn(),
normalizeDate: vi.fn(),
formatDateForAPI: vi.fn(),
getToday: vi.fn(),
getYesterday: vi.fn(),
}));
describe('DailyService', () => {
let service: DailyService;
const mockUserId = 'user-123';
const mockDate = new Date('2024-01-15');
beforeEach(() => {
vi.clearAllMocks();
service = new DailyService();
vi.mocked(normalizeDate).mockImplementation((date) => date);
vi.mocked(getPreviousWorkday).mockReturnValue(new Date('2024-01-14'));
vi.mocked(getToday).mockReturnValue(mockDate);
vi.mocked(getYesterday).mockReturnValue(new Date('2024-01-14'));
});
describe('getDailyView', () => {
it('devrait récupérer la vue daily pour une date', async () => {
const yesterdayCheckboxes: any[] = [];
const todayCheckboxes: any[] = [];
vi.mocked(prisma.dailyCheckbox.findMany)
.mockResolvedValueOnce(yesterdayCheckboxes)
.mockResolvedValueOnce(todayCheckboxes);
const view = await service.getDailyView(mockDate, mockUserId);
expect(view.date).toEqual(mockDate);
expect(view.yesterday).toEqual(yesterdayCheckboxes);
expect(view.today).toEqual(todayCheckboxes);
});
});
describe('getCheckboxesByDate', () => {
it('devrait récupérer les checkboxes pour une date', async () => {
const mockCheckboxes = [
{
id: 'checkbox-1',
date: mockDate,
text: 'Checkbox 1',
isChecked: false,
type: 'task',
order: 0,
taskId: null,
userId: mockUserId,
task: null,
user: { id: mockUserId },
createdAt: new Date(),
updatedAt: new Date(),
},
];
vi.mocked(prisma.dailyCheckbox.findMany).mockResolvedValue(
mockCheckboxes as any
);
const checkboxes = await service.getCheckboxesByDate(
mockDate,
mockUserId
);
expect(checkboxes).toBeDefined();
expect(prisma.dailyCheckbox.findMany).toHaveBeenCalledWith({
where: {
date: mockDate,
userId: mockUserId,
},
include: {
task: {
include: {
taskTags: {
include: {
tag: true,
},
},
primaryTag: true,
},
},
user: true,
},
orderBy: { order: 'asc' },
});
});
});
describe('addCheckbox', () => {
it('devrait ajouter une checkbox', async () => {
const mockCheckbox = {
id: 'checkbox-1',
date: mockDate,
text: 'New checkbox',
isChecked: false,
type: 'task',
order: 0,
taskId: null,
userId: mockUserId,
task: null,
user: { id: mockUserId },
createdAt: new Date(),
updatedAt: new Date(),
};
vi.mocked(prisma.dailyCheckbox.aggregate).mockResolvedValue({
_max: { order: null },
} as any);
vi.mocked(prisma.dailyCheckbox.create).mockResolvedValue(
mockCheckbox as any
);
const checkbox = await service.addCheckbox({
date: mockDate,
text: 'New checkbox',
userId: mockUserId,
});
expect(checkbox).toBeDefined();
expect(prisma.dailyCheckbox.create).toHaveBeenCalled();
});
it("devrait utiliser l'ordre spécifié", async () => {
const mockCheckbox = {
id: 'checkbox-1',
date: mockDate,
text: 'New checkbox',
isChecked: false,
type: 'task',
order: 5,
taskId: null,
userId: mockUserId,
task: null,
user: { id: mockUserId },
createdAt: new Date(),
updatedAt: new Date(),
};
vi.mocked(prisma.dailyCheckbox.create).mockResolvedValue(
mockCheckbox as any
);
await service.addCheckbox({
date: mockDate,
text: 'New checkbox',
userId: mockUserId,
order: 5,
});
expect(prisma.dailyCheckbox.create).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.objectContaining({
order: 5,
}),
})
);
});
});
describe('updateCheckbox', () => {
it('devrait mettre à jour une checkbox', async () => {
const mockCheckbox = {
id: 'checkbox-1',
date: mockDate,
text: 'Updated checkbox',
isChecked: true,
type: 'task',
order: 0,
taskId: null,
userId: mockUserId,
task: null,
user: { id: mockUserId },
createdAt: new Date(),
updatedAt: new Date(),
};
vi.mocked(prisma.dailyCheckbox.update).mockResolvedValue(
mockCheckbox as any
);
const checkbox = await service.updateCheckbox('checkbox-1', {
text: 'Updated checkbox',
isChecked: true,
});
expect(checkbox).toBeDefined();
expect(prisma.dailyCheckbox.update).toHaveBeenCalled();
});
});
describe('toggleCheckbox', () => {
it("devrait toggle l'état d'une checkbox", async () => {
const existingCheckbox = {
id: 'checkbox-1',
isChecked: false,
};
const updatedCheckbox = {
id: 'checkbox-1',
date: mockDate,
text: 'Checkbox',
isChecked: true,
type: 'task',
order: 0,
taskId: null,
userId: mockUserId,
task: null,
user: { id: mockUserId },
createdAt: new Date(),
updatedAt: new Date(),
};
vi.mocked(prisma.dailyCheckbox.findUnique).mockResolvedValue(
existingCheckbox as any
);
vi.mocked(prisma.dailyCheckbox.update).mockResolvedValue(
updatedCheckbox as any
);
const checkbox = await service.toggleCheckbox('checkbox-1');
expect(checkbox.isChecked).toBe(true);
expect(prisma.dailyCheckbox.update).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.objectContaining({
isChecked: true,
}),
})
);
});
it("devrait lancer une erreur si la checkbox n'existe pas", async () => {
vi.mocked(prisma.dailyCheckbox.findUnique).mockResolvedValue(null);
await expect(service.toggleCheckbox('non-existent')).rejects.toThrow(
'non trouvée'
);
});
});
describe('deleteCheckbox', () => {
it('devrait supprimer une checkbox', async () => {
const existingCheckbox = {
id: 'checkbox-1',
date: mockDate,
text: 'Checkbox',
isChecked: false,
type: 'task',
order: 0,
taskId: null,
userId: mockUserId,
createdAt: new Date(),
updatedAt: new Date(),
};
vi.mocked(prisma.dailyCheckbox.findUnique).mockResolvedValue(
existingCheckbox as any
);
vi.mocked(prisma.dailyCheckbox.delete).mockResolvedValue({} as any);
await service.deleteCheckbox('checkbox-1');
expect(prisma.dailyCheckbox.delete).toHaveBeenCalledWith({
where: { id: 'checkbox-1' },
});
});
it("devrait lancer une erreur si la checkbox n'existe pas", async () => {
vi.mocked(prisma.dailyCheckbox.findUnique).mockResolvedValue(null);
await expect(service.deleteCheckbox('non-existent')).rejects.toThrow(
'non trouvée'
);
});
});
});

View File

@@ -0,0 +1,286 @@
/**
* Tests unitaires pour tagsService
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { tagsService } from '../tags';
import { prisma } from '@/services/core/database';
// Mock de prisma
vi.mock('@/services/core/database', () => ({
prisma: {
tag: {
findMany: vi.fn(),
findFirst: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
},
taskTag: {
deleteMany: vi.fn(),
},
},
}));
describe('tagsService', () => {
const mockUserId = 'user-123';
beforeEach(() => {
vi.clearAllMocks();
});
describe('getTags', () => {
it('devrait récupérer tous les tags avec leur usage', async () => {
const mockTags = [
{
id: 'tag-1',
name: 'Tag 1',
color: '#ff0000',
isPinned: false,
_count: { taskTags: 5 },
},
{
id: 'tag-2',
name: 'Tag 2',
color: '#00ff00',
isPinned: true,
_count: { taskTags: 2 },
},
];
vi.mocked(prisma.tag.findMany).mockResolvedValue(mockTags as any);
const tags = await tagsService.getTags(mockUserId);
expect(tags).toHaveLength(2);
expect(tags[0]).toEqual({
id: 'tag-1',
name: 'Tag 1',
color: '#ff0000',
isPinned: false,
usage: 5,
});
expect(prisma.tag.findMany).toHaveBeenCalledWith({
where: { ownerId: mockUserId },
include: {
_count: {
select: {
taskTags: true,
},
},
},
orderBy: { name: 'asc' },
});
});
});
describe('getTagById', () => {
it('devrait récupérer un tag par son ID', async () => {
const mockTag = {
id: 'tag-1',
name: 'Tag 1',
color: '#ff0000',
isPinned: false,
};
vi.mocked(prisma.tag.findFirst).mockResolvedValue(mockTag as any);
const tag = await tagsService.getTagById('tag-1', mockUserId);
expect(tag).toEqual(mockTag);
expect(prisma.tag.findFirst).toHaveBeenCalledWith({
where: {
id: 'tag-1',
ownerId: mockUserId,
},
});
});
it("devrait retourner null si le tag n'existe pas", async () => {
vi.mocked(prisma.tag.findFirst).mockResolvedValue(null);
const tag = await tagsService.getTagById('non-existent', mockUserId);
expect(tag).toBeNull();
});
});
describe('getTagByName', () => {
it('devrait récupérer un tag par son nom', async () => {
const mockTag = {
id: 'tag-1',
name: 'Tag 1',
color: '#ff0000',
isPinned: false,
};
vi.mocked(prisma.tag.findFirst).mockResolvedValue(mockTag as any);
const tag = await tagsService.getTagByName('Tag 1', mockUserId);
expect(tag).toEqual(mockTag);
expect(prisma.tag.findFirst).toHaveBeenCalledWith({
where: {
name: {
equals: 'Tag 1',
},
ownerId: mockUserId,
},
});
});
});
describe('createTag', () => {
it('devrait créer un nouveau tag', async () => {
const mockTag = {
id: 'tag-1',
name: 'New Tag',
color: '#ff0000',
isPinned: false,
};
vi.mocked(prisma.tag.findFirst).mockResolvedValue(null); // Pas de tag existant
vi.mocked(prisma.tag.create).mockResolvedValue(mockTag as any);
const tag = await tagsService.createTag({
name: 'New Tag',
color: '#ff0000',
userId: mockUserId,
});
expect(tag).toEqual(mockTag);
expect(prisma.tag.create).toHaveBeenCalledWith({
data: {
name: 'New Tag',
color: '#ff0000',
isPinned: false,
ownerId: mockUserId,
},
});
});
it('devrait lancer une erreur si le tag existe déjà', async () => {
const existingTag = {
id: 'tag-1',
name: 'Existing Tag',
color: '#ff0000',
isPinned: false,
};
vi.mocked(prisma.tag.findFirst).mockResolvedValue(existingTag as any);
await expect(
tagsService.createTag({
name: 'Existing Tag',
color: '#ff0000',
userId: mockUserId,
})
).rejects.toThrow('existe déjà');
});
it('devrait trimmer le nom du tag', async () => {
const mockTag = {
id: 'tag-1',
name: 'Trimmed Tag',
color: '#ff0000',
isPinned: false,
};
vi.mocked(prisma.tag.findFirst).mockResolvedValue(null);
vi.mocked(prisma.tag.create).mockResolvedValue(mockTag as any);
await tagsService.createTag({
name: ' Trimmed Tag ',
color: '#ff0000',
userId: mockUserId,
});
expect(prisma.tag.create).toHaveBeenCalledWith({
data: {
name: 'Trimmed Tag',
color: '#ff0000',
isPinned: false,
ownerId: mockUserId,
},
});
});
});
describe('updateTag', () => {
it('devrait mettre à jour un tag', async () => {
const mockTag = {
id: 'tag-1',
name: 'Updated Tag',
color: '#00ff00',
isPinned: true,
};
vi.mocked(prisma.tag.findFirst).mockResolvedValue({
id: 'tag-1',
name: 'Old Tag',
color: '#ff0000',
isPinned: false,
} as any);
vi.mocked(prisma.tag.update).mockResolvedValue(mockTag as any);
const tag = await tagsService.updateTag('tag-1', mockUserId, {
name: 'Updated Tag',
color: '#00ff00',
isPinned: true,
});
expect(tag).toEqual(mockTag);
expect(prisma.tag.update).toHaveBeenCalledWith({
where: { id: 'tag-1' },
data: {
name: 'Updated Tag',
color: '#00ff00',
isPinned: true,
},
});
});
it("devrait lancer une erreur si le tag n'existe pas", async () => {
vi.mocked(prisma.tag.findFirst).mockResolvedValue(null);
await expect(
tagsService.updateTag('non-existent', mockUserId, {
name: 'Updated Tag',
})
).rejects.toThrow('non trouvé');
});
});
describe('deleteTag', () => {
it('devrait supprimer un tag', async () => {
vi.mocked(prisma.tag.findFirst).mockResolvedValue({
id: 'tag-1',
name: 'Tag to delete',
color: '#ff0000',
isPinned: false,
} as any);
vi.mocked(prisma.taskTag.deleteMany).mockResolvedValue({
count: 0,
} as any);
vi.mocked(prisma.tag.delete).mockResolvedValue({} as any);
await tagsService.deleteTag('tag-1', mockUserId);
expect(prisma.taskTag.deleteMany).toHaveBeenCalledWith({
where: { tagId: 'tag-1' },
});
expect(prisma.tag.delete).toHaveBeenCalledWith({
where: { id: 'tag-1' },
});
});
it("devrait lancer une erreur si le tag n'existe pas", async () => {
vi.mocked(prisma.tag.findFirst).mockResolvedValue(null);
await expect(
tagsService.deleteTag('non-existent', mockUserId)
).rejects.toThrow('non trouvé');
});
});
});

View File

@@ -0,0 +1,436 @@
/**
* Tests unitaires pour TasksService
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { TasksService } from '../tasks';
import { prisma } from '@/services/core/database';
import { tagsService } from '../tags';
import { getReadonlyFieldsForTask } from '../readonly-fields';
import { getToday } from '@/lib/date-utils';
// Mock de prisma
vi.mock('@/services/core/database', () => ({
prisma: {
task: {
findMany: vi.fn(),
findFirst: vi.fn(),
findUnique: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
aggregate: vi.fn(),
},
taskTag: {
createMany: vi.fn(),
deleteMany: vi.fn(),
findMany: vi.fn(),
},
dailyCheckbox: {
findMany: vi.fn(),
},
},
}));
// Mock de tagsService
vi.mock('../tags', () => ({
tagsService: {
ensureTagsExist: vi.fn(),
},
}));
// Mock de readonly-fields
vi.mock('../readonly-fields', () => ({
getReadonlyFieldsForTask: vi.fn(),
}));
// Mock de date-utils
vi.mock('@/lib/date-utils', () => ({
getToday: vi.fn(),
}));
describe('TasksService', () => {
let service: TasksService;
const mockUserId = 'user-123';
beforeEach(() => {
vi.clearAllMocks();
service = new TasksService();
vi.mocked(getToday).mockReturnValue(new Date('2024-01-15'));
});
describe('getTasks', () => {
it('devrait récupérer toutes les tâches pour un utilisateur', async () => {
const mockTasks = [
{
id: 'task-1',
title: 'Task 1',
description: null,
status: 'todo',
priority: 'medium',
source: 'manual',
sourceId: 'manual-123',
ownerId: mockUserId,
createdAt: new Date(),
updatedAt: new Date(),
dueDate: null,
completedAt: null,
primaryTagId: null,
jiraProject: null,
jiraKey: null,
jiraType: null,
tfsProject: null,
tfsPullRequestId: null,
tfsRepository: null,
tfsSourceBranch: null,
tfsTargetBranch: null,
assignee: null,
taskTags: [],
primaryTag: null,
_count: { dailyCheckboxes: 0 },
},
];
vi.mocked(prisma.task.findMany).mockResolvedValue(mockTasks as any);
vi.mocked(getReadonlyFieldsForTask).mockReturnValue([]);
const tasks = await service.getTasks(mockUserId);
expect(tasks).toHaveLength(1);
expect(prisma.task.findMany).toHaveBeenCalledWith({
where: { ownerId: mockUserId },
include: {
taskTags: {
include: {
tag: true,
},
},
primaryTag: true,
_count: {
select: {
dailyCheckboxes: true,
},
},
},
take: undefined,
skip: 0,
orderBy: [
{ completedAt: 'desc' },
{ dueDate: 'asc' },
{ createdAt: 'desc' },
],
});
});
it('devrait filtrer par statut', async () => {
vi.mocked(prisma.task.findMany).mockResolvedValue([]);
await service.getTasks(mockUserId, { status: ['todo', 'in_progress'] });
expect(prisma.task.findMany).toHaveBeenCalledWith(
expect.objectContaining({
where: expect.objectContaining({
status: { in: ['todo', 'in_progress'] },
}),
})
);
});
it('devrait rechercher dans le titre et la description', async () => {
vi.mocked(prisma.task.findMany).mockResolvedValue([]);
await service.getTasks(mockUserId, { search: 'test' });
expect(prisma.task.findMany).toHaveBeenCalledWith(
expect.objectContaining({
where: expect.objectContaining({
OR: [
{ title: { contains: 'test' } },
{ description: { contains: 'test' } },
],
}),
})
);
});
});
describe('createTask', () => {
it('devrait créer une nouvelle tâche', async () => {
const mockTask = {
id: 'task-1',
title: 'New Task',
description: 'Description',
status: 'todo',
priority: 'medium',
ownerId: mockUserId,
source: 'manual',
sourceId: 'manual-123',
createdAt: new Date(),
updatedAt: new Date(),
dueDate: null,
completedAt: null,
primaryTagId: null,
jiraProject: null,
jiraKey: null,
jiraType: null,
tfsProject: null,
tfsPullRequestId: null,
tfsRepository: null,
tfsSourceBranch: null,
tfsTargetBranch: null,
assignee: null,
taskTags: [],
primaryTag: null,
};
vi.mocked(prisma.task.create).mockResolvedValue(mockTask as any);
vi.mocked(prisma.task.findUnique).mockResolvedValue(mockTask as any);
vi.mocked(tagsService.ensureTagsExist).mockResolvedValue([]);
vi.mocked(getReadonlyFieldsForTask).mockReturnValue([]);
const task = await service.createTask({
title: 'New Task',
description: 'Description',
ownerId: mockUserId,
});
expect(task).toBeDefined();
expect(prisma.task.create).toHaveBeenCalled();
});
it('devrait définir completedAt si le statut est done', async () => {
const mockTask = {
id: 'task-1',
title: 'Done Task',
description: null,
status: 'done',
priority: 'medium',
ownerId: mockUserId,
source: 'manual',
sourceId: 'manual-123',
createdAt: new Date(),
updatedAt: new Date(),
dueDate: null,
completedAt: new Date('2024-01-15'),
primaryTagId: null,
jiraProject: null,
jiraKey: null,
jiraType: null,
tfsProject: null,
tfsPullRequestId: null,
tfsRepository: null,
tfsSourceBranch: null,
tfsTargetBranch: null,
assignee: null,
taskTags: [],
primaryTag: null,
};
vi.mocked(prisma.task.create).mockResolvedValue(mockTask as any);
vi.mocked(prisma.task.findUnique).mockResolvedValue(mockTask as any);
vi.mocked(tagsService.ensureTagsExist).mockResolvedValue([]);
vi.mocked(getReadonlyFieldsForTask).mockReturnValue([]);
await service.createTask({
title: 'Done Task',
status: 'done',
ownerId: mockUserId,
});
expect(prisma.task.create).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.objectContaining({
completedAt: expect.any(Date),
}),
})
);
});
});
describe('updateTask', () => {
it('devrait mettre à jour une tâche', async () => {
const mockTask = {
id: 'task-1',
title: 'Updated Task',
description: null,
status: 'in_progress',
priority: 'medium',
ownerId: mockUserId,
source: 'manual',
sourceId: 'manual-123',
createdAt: new Date(),
updatedAt: new Date(),
dueDate: null,
completedAt: null,
primaryTagId: null,
jiraProject: null,
jiraKey: null,
jiraType: null,
tfsProject: null,
tfsPullRequestId: null,
tfsRepository: null,
tfsSourceBranch: null,
tfsTargetBranch: null,
assignee: null,
taskTags: [],
primaryTag: null,
};
vi.mocked(prisma.task.findFirst).mockResolvedValue(mockTask as any);
vi.mocked(prisma.task.update).mockResolvedValue(mockTask as any);
vi.mocked(prisma.task.findUnique).mockResolvedValue(mockTask as any);
vi.mocked(getReadonlyFieldsForTask).mockReturnValue([]);
const task = await service.updateTask(mockUserId, 'task-1', {
title: 'Updated Task',
status: 'in_progress',
});
expect(task).toBeDefined();
expect(prisma.task.update).toHaveBeenCalled();
});
it('devrait définir completedAt quand le statut passe à done', async () => {
const mockTask = {
id: 'task-1',
title: 'Task',
description: null,
status: 'todo',
priority: 'medium',
ownerId: mockUserId,
source: 'manual',
sourceId: 'manual-123',
createdAt: new Date(),
updatedAt: new Date(),
dueDate: null,
completedAt: null,
primaryTagId: null,
jiraProject: null,
jiraKey: null,
jiraType: null,
tfsProject: null,
tfsPullRequestId: null,
tfsRepository: null,
tfsSourceBranch: null,
tfsTargetBranch: null,
assignee: null,
taskTags: [],
primaryTag: null,
};
vi.mocked(prisma.task.findFirst).mockResolvedValue(mockTask as any);
vi.mocked(prisma.task.update).mockResolvedValue({
...mockTask,
status: 'done',
completedAt: new Date('2024-01-15'),
} as any);
vi.mocked(prisma.task.findUnique).mockResolvedValue({
...mockTask,
status: 'done',
completedAt: new Date('2024-01-15'),
} as any);
vi.mocked(getReadonlyFieldsForTask).mockReturnValue([]);
await service.updateTask(mockUserId, 'task-1', {
status: 'done',
});
expect(prisma.task.update).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.objectContaining({
completedAt: expect.any(Date),
}),
})
);
});
it("devrait lancer une erreur si la tâche n'existe pas", async () => {
vi.mocked(prisma.task.findFirst).mockResolvedValue(null);
await expect(
service.updateTask(mockUserId, 'non-existent', {
title: 'Updated',
})
).rejects.toThrow('introuvable');
});
});
describe('deleteTask', () => {
it('devrait supprimer une tâche', async () => {
const mockTask = {
id: 'task-1',
title: 'Task to delete',
ownerId: mockUserId,
};
vi.mocked(prisma.task.findFirst).mockResolvedValue(mockTask as any);
vi.mocked(prisma.task.delete).mockResolvedValue({} as any);
await service.deleteTask(mockUserId, 'task-1');
expect(prisma.task.delete).toHaveBeenCalledWith({
where: { id: 'task-1' },
});
});
it("devrait lancer une erreur si la tâche n'existe pas", async () => {
vi.mocked(prisma.task.findFirst).mockResolvedValue(null);
await expect(
service.deleteTask(mockUserId, 'non-existent')
).rejects.toThrow('introuvable');
});
});
describe('updateTaskStatus', () => {
it("devrait mettre à jour le statut d'une tâche", async () => {
const mockTask = {
id: 'task-1',
title: 'Task',
description: null,
status: 'todo',
priority: 'medium',
ownerId: mockUserId,
source: 'manual',
sourceId: 'manual-123',
createdAt: new Date(),
updatedAt: new Date(),
dueDate: null,
completedAt: null,
primaryTagId: null,
jiraProject: null,
jiraKey: null,
jiraType: null,
tfsProject: null,
tfsPullRequestId: null,
tfsRepository: null,
tfsSourceBranch: null,
tfsTargetBranch: null,
assignee: null,
taskTags: [],
primaryTag: null,
};
vi.mocked(prisma.task.findFirst).mockResolvedValue(mockTask as any);
vi.mocked(prisma.task.update).mockResolvedValue({
...mockTask,
status: 'in_progress',
} as any);
vi.mocked(prisma.task.findUnique).mockResolvedValue({
...mockTask,
status: 'in_progress',
} as any);
vi.mocked(getReadonlyFieldsForTask).mockReturnValue([]);
const task = await service.updateTaskStatus(
mockUserId,
'task-1',
'in_progress'
);
expect(task).toBeDefined();
expect(prisma.task.update).toHaveBeenCalled();
});
});
});