diff --git a/apps/server/src/api-data/db/db.controller.ts b/apps/server/src/api-data/db/db.controller.ts index a9eec754c..0ab6035b8 100644 --- a/apps/server/src/api-data/db/db.controller.ts +++ b/apps/server/src/api-data/db/db.controller.ts @@ -17,7 +17,6 @@ import { resolveDbDirectory } from '../../setup/index.js'; import * as projectService from '../../services/project-service/ProjectService.js'; import { doesProjectExist, upload, validateProjectFiles } from '../../services/project-service/projectServiceUtils.js'; -import { appStateProvider } from '../../services/app-state-service/AppStateService.js'; import { oscIntegration } from '../../services/integration-service/OscIntegration.js'; import { httpIntegration } from '../../services/integration-service/HttpIntegration.js'; import { DataProvider } from '../../classes/data-provider/DataProvider.js'; @@ -241,28 +240,22 @@ export async function renameProjectFile(req: Request, res: Response) { + const { filename } = req.params; try { - const { filename } = req.params; - - const lastLoadedProject = await appStateProvider.getLastLoadedProject(); - - if (lastLoadedProject === filename) { - return res.status(403).send({ message: 'Cannot delete currently loaded project' }); - } - - const errors = validateProjectFiles({ filename }); - - if (errors.length) { - return res.status(409).send({ message: errors.join(', ') }); - } - await projectService.deleteProjectFile(filename); - res.status(204).send({ + res.status(200).send({ message: `Deleted project ${filename}`, }); } catch (error) { const message = getErrorMessage(error); + if (message === 'Cannot delete currently loaded project') { + return res.status(403).send({ message }); + } + if (message === 'Project file not found') { + return res.status(404).send({ message }); + } + res.status(500).send({ message }); } } diff --git a/apps/server/src/services/app-state-service/AppStateService.ts b/apps/server/src/services/app-state-service/AppStateService.ts index 0b66f76a5..9bc5fcebc 100644 --- a/apps/server/src/services/app-state-service/AppStateService.ts +++ b/apps/server/src/services/app-state-service/AppStateService.ts @@ -36,6 +36,11 @@ class AppState { return this.config.data; } + async isLastLoadedProject(projectName: string): Promise { + const lastLoaded = await this.getLastLoadedProject(); + return lastLoaded === projectName; + } + async getLastLoadedProject(): Promise { const data = await this.get(); return data.lastLoadedProject; diff --git a/apps/server/src/services/project-service/ProjectService.ts b/apps/server/src/services/project-service/ProjectService.ts index e98544aa9..e5cb0439b 100644 --- a/apps/server/src/services/project-service/ProjectService.ts +++ b/apps/server/src/services/project-service/ProjectService.ts @@ -13,7 +13,7 @@ import { ensureDirectory, removeFileExtension } from '../../utils/fileManagement import { dbModel } from '../../models/dataModel.js'; import { deleteFile } from '../../utils/parserUtils.js'; import { switchDb } from '../../setup/loadDb.js'; -import { getPathToProject, getProjectFiles } from './projectServiceUtils.js'; +import { doesProjectExist, getPathToProject, getProjectFiles } from './projectServiceUtils.js'; import { parseJson } from '../../utils/parser.js'; import { generateUniqueFileName } from '../../utils/generateUniqueFilename.js'; @@ -122,6 +122,15 @@ export async function createProject(filename: string, projectData: ProjectData) * Deletes a project file */ export async function deleteProjectFile(filename: string) { + const isLastLoadedProject = await appStateProvider.isLastLoadedProject(filename); + if (isLastLoadedProject) { + throw new Error('Cannot delete currently loaded project'); + } + + if (!doesProjectExist(filename)) { + throw new Error('Project file not found'); + } + const projectFilePath = getPathToProject(filename); await deleteFile(projectFilePath); } diff --git a/apps/server/src/services/project-service/__tests__/ProjectService.test.ts b/apps/server/src/services/project-service/__tests__/ProjectService.test.ts new file mode 100644 index 000000000..e15f1e968 --- /dev/null +++ b/apps/server/src/services/project-service/__tests__/ProjectService.test.ts @@ -0,0 +1,45 @@ +import { deleteProjectFile } from '../ProjectService.js'; +import { appStateProvider } from '../../app-state-service/AppStateService.js'; +import { doesProjectExist, getPathToProject } from '../projectServiceUtils.js'; +import { Mock } from 'vitest'; +import { deleteFile } from '../../../utils/parserUtils.js'; + +vi.mock('./appStateProvider'); +vi.mock('./fileSystem'); +vi.mock('../../app-state-service/AppStateService.js', () => ({ + appStateProvider: { + isLastLoadedProject: vi.fn(), + }, +})); +vi.mock('../projectServiceUtils.js', () => ({ + doesProjectExist: vi.fn(), + getPathToProject: vi.fn(), +})); +vi.mock('../../../utils/parserUtils.js', () => ({ + deleteFile: vi.fn(), +})); + +describe('deleteProjectFile', () => { + it('throws an error if trying to delete the currently loaded project', async () => { + (appStateProvider.isLastLoadedProject as Mock).mockResolvedValue(true); + await expect(deleteProjectFile('loadedProject')).rejects.toThrow('Cannot delete currently loaded project'); + }); + + it('throws an error if the project file does not exist', async () => { + (appStateProvider.isLastLoadedProject as Mock).mockResolvedValue(false); + (doesProjectExist as Mock).mockReturnValue(false); + await expect(deleteProjectFile('nonexistentProject')).rejects.toThrow('Project file not found'); + }); + + it('deletes the project file successfully', async () => { + (appStateProvider.isLastLoadedProject as Mock).mockResolvedValue(false); + (doesProjectExist as Mock).mockReturnValue(true); + (getPathToProject as Mock).mockReturnValue('/path/to/project'); + (deleteFile as Mock).mockResolvedValue(undefined); + + await deleteProjectFile('existingProject'); + + expect(getPathToProject).toHaveBeenCalledWith('existingProject'); + expect(deleteFile).toHaveBeenCalledWith('/path/to/project'); + }); +});