diff --git a/package.json b/package.json index 4a0d3cc9..d5c80bd6 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "dotenv": "17.3.1", "express": "5.2.1", "express-async-handler": "1.2.0", - "fast-xml-parser": "5.3.9", + "fast-xml-parser": "5.4.1", "hash-wasm": "4.12.0", "mime-types": "3.0.2", "open": "11.0.0", @@ -72,7 +72,7 @@ "@types/cli-progress": "3.11.6", "@types/express": "5.0.6", "@types/mime-types": "3.0.1", - "@types/node": "25.3.0", + "@types/node": "25.3.1", "@types/range-parser": "1.2.7", "@vitest/coverage-istanbul": "4.0.18", "@vitest/spy": "4.0.18", diff --git a/src/commands/create-folder.ts b/src/commands/create-folder.ts index cb5679db..f32b9ab3 100644 --- a/src/commands/create-folder.ts +++ b/src/commands/create-folder.ts @@ -38,7 +38,7 @@ export default class CreateFolder extends Command { const folderName = await this.getFolderName(flags['name'], nonInteractive); const folderUuidFromFlag = await this.getFolderUuid(flags['id'], nonInteractive); - const folderUuid = await CLIUtils.fallbackToRootFolderIdIfEmpty(folderUuidFromFlag, userCredentials); + const folderUuid = await CLIUtils.fallbackToRootFolderIdIfEmpty(folderUuidFromFlag); CLIUtils.doing('Creating folder...', flags['json']); const [createNewFolder, requestCanceler] = await DriveFolderService.instance.createFolder({ diff --git a/src/commands/list.ts b/src/commands/list.ts index b0428472..43541610 100644 --- a/src/commands/list.ts +++ b/src/commands/list.ts @@ -36,7 +36,7 @@ export default class List extends Command { if (!userCredentials) throw new MissingCredentialsError(); const folderUuidFromFlag = await this.getFolderUuid(flags['id'], nonInteractive); - const folderUuid = await CLIUtils.fallbackToRootFolderIdIfEmpty(folderUuidFromFlag, userCredentials); + const folderUuid = await CLIUtils.fallbackToRootFolderIdIfEmpty(folderUuidFromFlag); const { folders, files } = await DriveFolderService.instance.getFolderContent(folderUuid); diff --git a/src/commands/move-file.ts b/src/commands/move-file.ts index b6a01f7e..62a9626f 100644 --- a/src/commands/move-file.ts +++ b/src/commands/move-file.ts @@ -41,10 +41,7 @@ export default class MoveFile extends Command { nonInteractive, reporter: this.log.bind(this), }); - const destinationFolderUuid = await CLIUtils.fallbackToRootFolderIdIfEmpty( - destinationFolderUuidFromFlag, - userCredentials, - ); + const destinationFolderUuid = await CLIUtils.fallbackToRootFolderIdIfEmpty(destinationFolderUuidFromFlag); const newFile = await DriveFileService.instance.moveFile(fileUuid, { destinationFolder: destinationFolderUuid }); const message = `File moved successfully to: ${destinationFolderUuid}`; diff --git a/src/commands/move-folder.ts b/src/commands/move-folder.ts index d0b9de07..c73706b4 100644 --- a/src/commands/move-folder.ts +++ b/src/commands/move-folder.ts @@ -41,10 +41,7 @@ export default class MoveFolder extends Command { nonInteractive, reporter: this.log.bind(this), }); - const destinationFolderUuid = await CLIUtils.fallbackToRootFolderIdIfEmpty( - destinationFolderUuidFromFlag, - userCredentials, - ); + const destinationFolderUuid = await CLIUtils.fallbackToRootFolderIdIfEmpty(destinationFolderUuidFromFlag); const newFolder = await DriveFolderService.instance.moveFolder(folderUuid, { destinationFolder: destinationFolderUuid, diff --git a/src/commands/trash-restore-file.ts b/src/commands/trash-restore-file.ts index b6945956..0b622aa3 100644 --- a/src/commands/trash-restore-file.ts +++ b/src/commands/trash-restore-file.ts @@ -42,10 +42,7 @@ export default class TrashRestoreFile extends Command { nonInteractive, reporter: this.log.bind(this), }); - const destinationFolderUuid = await CLIUtils.fallbackToRootFolderIdIfEmpty( - destinationFolderUuidFromFlag, - userCredentials, - ); + const destinationFolderUuid = await CLIUtils.fallbackToRootFolderIdIfEmpty(destinationFolderUuidFromFlag); const file = await DriveFileService.instance.moveFile(fileUuid, { destinationFolder: destinationFolderUuid }); const message = `File restored successfully to: ${destinationFolderUuid}`; diff --git a/src/commands/trash-restore-folder.ts b/src/commands/trash-restore-folder.ts index 532549cc..ab5a0aa0 100644 --- a/src/commands/trash-restore-folder.ts +++ b/src/commands/trash-restore-folder.ts @@ -42,10 +42,7 @@ export default class TrashRestoreFolder extends Command { nonInteractive, reporter: this.log.bind(this), }); - const destinationFolderUuid = await CLIUtils.fallbackToRootFolderIdIfEmpty( - destinationFolderUuidFromFlag, - userCredentials, - ); + const destinationFolderUuid = await CLIUtils.fallbackToRootFolderIdIfEmpty(destinationFolderUuidFromFlag); const folder = await DriveFolderService.instance.moveFolder(folderUuid, { destinationFolder: destinationFolderUuid, diff --git a/src/commands/upload-file.ts b/src/commands/upload-file.ts index d671e985..441eeb5d 100644 --- a/src/commands/upload-file.ts +++ b/src/commands/upload-file.ts @@ -55,10 +55,7 @@ export default class UploadFile extends Command { nonInteractive, reporter: this.log.bind(this), }); - const destinationFolderUuid = await CLIUtils.fallbackToRootFolderIdIfEmpty( - destinationFolderUuidFromFlag, - userCredentials, - ); + const destinationFolderUuid = await CLIUtils.fallbackToRootFolderIdIfEmpty(destinationFolderUuidFromFlag); const timings = { networkUpload: 0, @@ -138,7 +135,7 @@ export default class UploadFile extends Command { const thumbnailTimer = CLIUtils.timer(); if (fileSize > 0 && isThumbnailable && bufferStream) { - void ThumbnailService.instance.tryUploadThumbnail({ + await ThumbnailService.instance.tryUploadThumbnail({ bufferStream, fileType, bucket, diff --git a/src/commands/upload-folder.ts b/src/commands/upload-folder.ts index a7061374..de1ef530 100644 --- a/src/commands/upload-folder.ts +++ b/src/commands/upload-folder.ts @@ -40,10 +40,7 @@ export default class UploadFolder extends Command { nonInteractive: flags['non-interactive'], reporter: this.log.bind(this), }); - const destinationFolderUuid = await CLIUtils.fallbackToRootFolderIdIfEmpty( - destinationFolderUuidFromFlag, - userCredentials, - ); + const destinationFolderUuid = await CLIUtils.fallbackToRootFolderIdIfEmpty(destinationFolderUuidFromFlag); const progressBar = CLIUtils.progress( { diff --git a/src/commands/workspaces-unset.ts b/src/commands/workspaces-unset.ts index 285e67b9..468383f4 100644 --- a/src/commands/workspaces-unset.ts +++ b/src/commands/workspaces-unset.ts @@ -8,7 +8,7 @@ export default class WorkspacesUnset extends Command { static readonly args = {}; static readonly description = 'Unset the active workspace context for the current user session. ' + - 'Once a workspace is unset, all subsequent commands (list, upload, download, etc.) ' + + 'Once a workspace is unset, WebDAV and all of the subsequent CLI commands ' + 'will operate within the personal drive space until it is changed or set again.'; static readonly aliases = ['workspaces:unset']; static readonly examples = ['<%= config.bin %> <%= command.id %>']; diff --git a/src/commands/workspaces-use.ts b/src/commands/workspaces-use.ts index a6a5fed8..66de3091 100644 --- a/src/commands/workspaces-use.ts +++ b/src/commands/workspaces-use.ts @@ -12,7 +12,7 @@ export default class WorkspacesUse extends Command { static readonly args = {}; static readonly description = 'Set the active workspace context for the current user session. ' + - 'Once a workspace is selected, all subsequent commands (list, upload, download, etc.) ' + + 'Once a workspace is selected, WebDAV and all of the subsequent CLI commands ' + 'will operate within that workspace until it is changed or unset.'; static readonly aliases = ['workspaces:use']; static readonly examples = ['<%= config.bin %> <%= command.id %>']; @@ -75,7 +75,7 @@ export default class WorkspacesUse extends Command { }); const message = - `Workspace ${workspaceUuid} selected successfully. Now all drive commands (list, upload, download, etc.) ` + + `Workspace ${workspaceUuid} selected successfully. Now WebDAV and all of the CLI commands ` + 'will operate within this workspace until it is changed or unset.'; CLIUtils.success(this.log.bind(this), message); diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index 7d8716d8..c9092c07 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -173,6 +173,14 @@ export class AuthService { return loginCreds.workspace; }; + public getCurrentRootFolder = async (): Promise => { + const loginCreds = await ConfigService.instance.readUser(); + if (!loginCreds?.token || !loginCreds?.user?.mnemonic) { + throw new MissingCredentialsError(); + } + return loginCreds.workspace?.workspaceData?.workspaceUser?.rootFolderId ?? loginCreds.user.rootFolderId; + }; + /** * Logs the user out of the application by invoking the logout method * from the authentication client. This will terminate the user's session diff --git a/src/services/config.service.ts b/src/services/config.service.ts index 835fafbe..1d625267 100644 --- a/src/services/config.service.ts +++ b/src/services/config.service.ts @@ -22,13 +22,14 @@ export class ConfigService { /** * Gets the value from an environment key * @param key The environment key to retrieve - * @throws {Error} If key is not found in process.env + * @param throwIfNotFound If true, throws an error if the key is not found + * @throws {Error} If key is not found in process.env and throwIfNotFound is true * @returns The value from the environment variable **/ - public get = (key: keyof ConfigKeys): string => { + public get = (key: keyof ConfigKeys, throwIfNotFound = true): string => { const value = process.env[key]; - if (!value) throw new Error(`Config key ${key} was not found in process.env`); - return value; + if (!value && throwIfNotFound) throw new Error(`Config key ${key} was not found in process.env`); + return value ?? ''; }; /** diff --git a/src/services/database/database.service.ts b/src/services/database/database.service.ts index e7976d02..43325ac3 100644 --- a/src/services/database/database.service.ts +++ b/src/services/database/database.service.ts @@ -2,12 +2,13 @@ import { DataSource } from 'typeorm'; import { DriveFileModel } from './drive-file/drive-file.model'; import { DriveFolderModel } from './drive-folder/drive-folder.model'; import { DRIVE_SQLITE_FILE } from '../../constants/configs'; +import { ConfigService } from '../config.service'; export class DatabaseService { public static readonly instance = new DatabaseService(); public dataSource = new DataSource( - process.env.NODE_ENV === 'test' + ConfigService.instance.get('NODE_ENV', false) === 'test' ? { type: 'sqljs', autoSave: false, diff --git a/src/services/database/drive-file/drive-file.attributes.ts b/src/services/database/drive-file/drive-file.attributes.ts index 256e0b67..c06719de 100644 --- a/src/services/database/drive-file/drive-file.attributes.ts +++ b/src/services/database/drive-file/drive-file.attributes.ts @@ -1,8 +1,8 @@ export interface DriveFileAttributes { uuid: string; name: string; - type?: string | null; - fileId?: string | null; + type: string | null; + fileId: string | null; folderUuid: string; bucket: string; createdAt: Date; diff --git a/src/services/database/drive-file/drive-file.domain.ts b/src/services/database/drive-file/drive-file.domain.ts index 36f85cd8..a1ceaa51 100644 --- a/src/services/database/drive-file/drive-file.domain.ts +++ b/src/services/database/drive-file/drive-file.domain.ts @@ -3,9 +3,9 @@ import { DriveFileAttributes } from './drive-file.attributes'; export class DriveFile implements DriveFileAttributes { name: string; - type?: string | null; + type: string | null; uuid: string; - fileId?: string | null; + fileId: string | null; folderUuid: string; bucket: string; createdAt: Date; diff --git a/src/services/database/drive-file/drive-file.model.ts b/src/services/database/drive-file/drive-file.model.ts index 68b3bd83..5c3b25e3 100644 --- a/src/services/database/drive-file/drive-file.model.ts +++ b/src/services/database/drive-file/drive-file.model.ts @@ -10,10 +10,10 @@ export class DriveFileModel implements DriveFileAttributes { declare name: string; @Column({ nullable: true, type: 'varchar' }) - declare type?: string | null; + declare type: string | null; @Column({ nullable: true, type: 'varchar' }) - declare fileId?: string | null; + declare fileId: string | null; @Column({ nullable: false, type: 'varchar' }) declare folderUuid: string; diff --git a/src/services/database/drive-file/drive-file.repository.ts b/src/services/database/drive-file/drive-file.repository.ts index b81cdbcc..6768e190 100644 --- a/src/services/database/drive-file/drive-file.repository.ts +++ b/src/services/database/drive-file/drive-file.repository.ts @@ -1,26 +1,26 @@ +import { IsNull } from 'typeorm'; +import { DatabaseUtils } from '../../../utils/database.utils'; import { ErrorUtils } from '../../../utils/errors.utils'; import { DatabaseService } from '../database.service'; import { DriveFile } from './drive-file.domain'; import { DriveFileModel } from './drive-file.model'; -const BATCH_SIZE = 100; - export class FileRepository { public static readonly instance = new FileRepository(); - private fileRepository = DatabaseService.instance.dataSource.getRepository(DriveFileModel); + private readonly fileRepository = DatabaseService.instance.dataSource.getRepository(DriveFileModel); - public createOrUpdate = async (files: DriveFileModel[]) => { + public createOrUpdate = async (files: DriveFileModel[]): Promise => { try { - for (let i = 0; i < files.length; i += BATCH_SIZE) { - const chunk = files.slice(i, i + BATCH_SIZE); + for (let i = 0; i < files.length; i += DatabaseUtils.CREATE_BATCH_SIZE) { + const chunk = files.slice(i, i + DatabaseUtils.CREATE_BATCH_SIZE); await this.fileRepository.upsert(chunk, { conflictPaths: ['uuid'] }); } return files.map((file) => DriveFile.build(file)); } catch (error) { - ErrorUtils.report(error, { files }); + ErrorUtils.report(error, { createOrUpdate: files }); } }; @@ -28,7 +28,7 @@ export class FileRepository { try { return await this.fileRepository.update({ uuid }, update); } catch (error) { - ErrorUtils.report(error, { uuid }); + ErrorUtils.report(error, { updateByUuid: uuid }); } }; @@ -36,7 +36,32 @@ export class FileRepository { try { return await this.fileRepository.delete(uuids); } catch (error) { - ErrorUtils.report(error, { uuids }); + ErrorUtils.report(error, { delete: uuids }); + } + }; + + public deleteByParentUuid = async (parentUuid: string) => { + try { + return await this.fileRepository.delete({ folderUuid: parentUuid }); + } catch (error) { + ErrorUtils.report(error, { deleteByParentUuid: parentUuid }); + } + }; + + public getByParentUuidNameAndType = async ( + parentUuid: string, + name: string, + type: string | null, + ): Promise => { + try { + const typeCondition = type ?? IsNull(); + const file = await this.fileRepository.findOneBy({ folderUuid: parentUuid, name, type: typeCondition }); + if (!file) { + return; + } + return DriveFile.build(file); + } catch (error) { + ErrorUtils.report(error, { getByParentuuidAndName: { parentUuid, name, type } }); } }; } diff --git a/src/services/database/drive-folder/drive-folder.repository.ts b/src/services/database/drive-folder/drive-folder.repository.ts index be9764fe..7f83a351 100644 --- a/src/services/database/drive-folder/drive-folder.repository.ts +++ b/src/services/database/drive-folder/drive-folder.repository.ts @@ -2,13 +2,12 @@ import { ErrorUtils } from '../../../utils/errors.utils'; import { DatabaseService } from '../database.service'; import { DriveFolder } from './drive-folder.domain'; import { DriveFolderModel } from './drive-folder.model'; - -const BATCH_SIZE = 100; +import { DatabaseUtils } from '../../../utils/database.utils'; export class FolderRepository { public static readonly instance = new FolderRepository(); - private folderRepository = DatabaseService.instance.dataSource.getRepository(DriveFolderModel); + private readonly folderRepository = DatabaseService.instance.dataSource.getRepository(DriveFolderModel); public getByUuid = async (uuid: string): Promise => { try { @@ -34,12 +33,45 @@ export class FolderRepository { } }; + public getByParentUuidAndName = async (parentUuid: string, name: string): Promise => { + try { + const folder = await this.folderRepository.findOneBy({ parentUuid, name }); + if (!folder) { + return; + } + return DriveFolder.build(folder); + } catch (error) { + ErrorUtils.report(error, { getByParentuuidAndName: { parentUuid, name } }); + } + }; + + public getByPath = async (path: string, parentUuid: string): Promise => { + try { + const onFound = async (uuid: string) => { + const folder = await this.folderRepository.findOneBy({ uuid }); + if (!folder) { + return; + } + return DriveFolder.build(folder); + }; + + return DatabaseUtils.getFolderByPathGeneric({ + path, + parentUuid, + onFound, + getByParentAndName: this.getByParentUuidAndName.bind(this), + }); + } catch (error) { + ErrorUtils.report(error, { getByPath: path }); + } + }; + public createOrUpdate = async (files: DriveFolderModel[]) => { if (files.length === 0) return; try { - for (let i = 0; i < files.length; i += BATCH_SIZE) { - const chunk = files.slice(i, i + BATCH_SIZE); + for (let i = 0; i < files.length; i += DatabaseUtils.CREATE_BATCH_SIZE) { + const chunk = files.slice(i, i + DatabaseUtils.CREATE_BATCH_SIZE); await this.folderRepository.upsert(chunk, { conflictPaths: ['uuid'] }); } diff --git a/src/services/drive/drive-file.service.ts b/src/services/drive/drive-file.service.ts index 824814f4..d63f0c17 100644 --- a/src/services/drive/drive-file.service.ts +++ b/src/services/drive/drive-file.service.ts @@ -4,6 +4,10 @@ import { DriveFileItem } from '../../types/drive.types'; import { DriveUtils } from '../../utils/drive.utils'; import { AuthService } from '../auth.service'; import { FileRepository } from '../database/drive-file/drive-file.repository'; +import { DriveFolderService } from './drive-folder.service'; +import { NotFoundError } from '../../utils/errors.utils'; +import { PathUtils } from '../../utils/path.utils'; +import { logger } from '../../utils/logger.utils'; export class DriveFileService { static readonly instance = new DriveFileService(); @@ -19,14 +23,14 @@ export class DriveFileService { bucket: driveFile.bucket, createdAt: new Date(driveFile.createdAt), updatedAt: new Date(driveFile.updatedAt), - fileId: driveFile.fileId, - type: driveFile.type, + fileId: driveFile.fileId ?? null, + type: driveFile.type ?? null, status: driveFile.status as DriveFileItem['status'], folderUuid: driveFile.folderUuid, creationTime: new Date(driveFile.creationTime ?? driveFile.createdAt), modificationTime: new Date(driveFile.modificationTime ?? driveFile.updatedAt), }; - FileRepository.instance.createOrUpdate([driveFileItem]); + await FileRepository.instance.createOrUpdate([driveFileItem]); return driveFileItem; }; @@ -65,7 +69,7 @@ export class DriveFileService { const fileMetadata = await getFileMetadata; const driveFileItem = DriveUtils.driveFileMetaToItem(fileMetadata); - FileRepository.instance.createOrUpdate([driveFileItem]); + await FileRepository.instance.createOrUpdate([driveFileItem]); return driveFileItem; }; @@ -75,7 +79,7 @@ export class DriveFileService { const fileMeta = await storageClient.moveFileByUuid(uuid, payload); const driveFileItem = DriveUtils.driveFileMetaToItem(fileMeta); - FileRepository.instance.createOrUpdate([driveFileItem]); + await FileRepository.instance.createOrUpdate([driveFileItem]); return fileMeta; }; @@ -86,13 +90,42 @@ export class DriveFileService { ): Promise => { const storageClient = SdkManager.instance.getStorage(); await storageClient.updateFileMetaByUUID(fileUuid, payload); - FileRepository.instance.updateByUuid(fileUuid, { name: payload.plainName, type: payload.type }); + await FileRepository.instance.updateByUuid(fileUuid, { name: payload.plainName, type: payload.type }); + }; + + public getByParentUuidAndName = async ( + parentUuid: string, + name: string, + type: string | null, + ): Promise => { + const subFiles = await DriveFolderService.instance.getFolderSubfiles(parentUuid); + const fileMeta = subFiles.find( + (file) => (file.plainName === name || file.name === name) && (file.type ?? null) === type, + ); + if (!fileMeta) { + throw new NotFoundError('File not found'); + } + return DriveUtils.driveFileMetaToItem(fileMeta); }; public getFileMetadataByPath = async (path: string): Promise => { - const storageClient = SdkManager.instance.getStorage(); - const fileMetadata = await storageClient.getFileByPath(encodeURIComponent(path)); - return DriveUtils.driveFileMetaToItem(fileMetadata); + const { fileName, fileType, folderPath } = PathUtils.getPathFileData(path); + + const parentFolder = await DriveFolderService.instance.getFolderMetadataByPath(folderPath); + + const localFileDB = await FileRepository.instance.getByParentUuidNameAndType(parentFolder.uuid, fileName, fileType); + if (localFileDB) { + try { + const file = await this.getFileMetadata(localFileDB.uuid); + if (file) { + return file; + } + } catch { + logger.error('File not found when getting file by path on local DB', { path }); + } + } + + return this.getByParentUuidAndName(parentFolder.uuid, fileName, fileType); }; public createThumbnail = (payload: StorageTypes.CreateThumbnailEntryPayload): Promise => { diff --git a/src/services/drive/drive-folder.service.ts b/src/services/drive/drive-folder.service.ts index b5a591e4..e0b2237f 100644 --- a/src/services/drive/drive-folder.service.ts +++ b/src/services/drive/drive-folder.service.ts @@ -10,6 +10,9 @@ import { FolderRepository } from '../database/drive-folder/drive-folder.reposito import { DriveFolder } from '../database/drive-folder/drive-folder.domain'; import { FileRepository } from '../database/drive-file/drive-file.repository'; import { DriveFile } from '../database/drive-file/drive-file.domain'; +import { NotFoundError } from '../../utils/errors.utils'; +import { DatabaseUtils } from '../../utils/database.utils'; +import { logger } from '../../utils/logger.utils'; export class DriveFolderService { static readonly instance = new DriveFolderService(); @@ -18,7 +21,7 @@ export class DriveFolderService { const storageClient = SdkManager.instance.getStorage(); const folderMeta = await storageClient.getFolderMeta(uuid); const folderItem = DriveUtils.driveFolderMetaToItem(folderMeta); - FolderRepository.instance.createOrUpdate([folderItem]); + await FolderRepository.instance.createOrUpdate([folderItem]); return folderItem; }; @@ -26,16 +29,28 @@ export class DriveFolderService { const storageClient = SdkManager.instance.getStorage(); const folderMeta = await storageClient.getFolderMetaById(id); const folderItem = DriveUtils.driveFolderMetaToItem(folderMeta); - FolderRepository.instance.createOrUpdate([folderItem]); + await FolderRepository.instance.createOrUpdate([folderItem]); return folderItem; }; public getFolderContent = async (folderUuid: string) => { + const folders = await this.getFolderSubfolders(folderUuid); + const files = await this.getFolderSubfiles(folderUuid); + return { folders, files }; + }; + + public getFolderSubfolders = async (folderUuid: string): Promise => { const currentWorkspace = await AuthService.instance.getCurrentWorkspace(); const currentWorkspaceCreds = currentWorkspace?.workspaceCredentials; const folders = await this.getAllSubfolders(currentWorkspaceCreds, folderUuid, 0); + return folders; + }; + + public getFolderSubfiles = async (folderUuid: string): Promise => { + const currentWorkspace = await AuthService.instance.getCurrentWorkspace(); + const currentWorkspaceCreds = currentWorkspace?.workspaceCredentials; const files = await this.getAllSubfiles(currentWorkspaceCreds, folderUuid, 0); - return { folders, files }; + return files; }; private readonly getAllSubfolders = async ( @@ -68,7 +83,7 @@ export class DriveFolderService { folders = (await personalFolderContentPromise).folders; } - FolderRepository.instance.createOrUpdate( + await FolderRepository.instance.createOrUpdate( folders.map( (folder) => new DriveFolder({ @@ -115,27 +130,27 @@ export class DriveFolderService { files = (await folderContentPromise).files; } - FileRepository.instance.createOrUpdate( - files.map( - (file) => - new DriveFile({ - uuid: file.uuid, - name: file.plainName, - type: file.type, - folderUuid: file.folderUuid, - status: FileStatus.EXISTS, - bucket: file.bucket, - size: Number(file.size ?? 0), - fileId: file.fileId, - createdAt: new Date(file.createdAt), - updatedAt: new Date(file.updatedAt), - creationTime: new Date(file.creationTime ?? file.createdAt), - modificationTime: new Date(file.modificationTime ?? file.updatedAt), - }), - ), - ); - if (files.length > 0) { + await FileRepository.instance.deleteByParentUuid(folderUuid); + await FileRepository.instance.createOrUpdate( + files.map( + (file) => + new DriveFile({ + uuid: file.uuid, + name: file.plainName, + type: file.type, + folderUuid: file.folderUuid, + status: FileStatus.EXISTS, + bucket: file.bucket, + size: Number(file.size ?? 0), + fileId: file.fileId, + createdAt: new Date(file.createdAt), + updatedAt: new Date(file.updatedAt), + creationTime: new Date(file.creationTime ?? file.createdAt), + modificationTime: new Date(file.modificationTime ?? file.updatedAt), + }), + ), + ); return files.concat(await this.getAllSubfiles(currentWorkspace, folderUuid, offset + files.length)); } else { return files; @@ -149,7 +164,7 @@ export class DriveFolderService { const storageClient = SdkManager.instance.getStorage(); const folderMeta = await storageClient.moveFolderByUuid(uuid, payload); const folderItem = DriveUtils.driveFolderMetaToItem(folderMeta); - FolderRepository.instance.createOrUpdate([folderItem]); + await FolderRepository.instance.createOrUpdate([folderItem]); return folderMeta; }; @@ -181,19 +196,62 @@ export class DriveFolderService { public renameFolder = async (payload: { folderUuid: string; name: string }): Promise => { const storageClient = SdkManager.instance.getStorage(); await storageClient.updateFolderNameWithUUID(payload); - FolderRepository.instance.updateByUuid(payload.folderUuid, { name: payload.name }); + await FolderRepository.instance.updateByUuid(payload.folderUuid, { name: payload.name }); }; - public getFolderMetadataByPath = async (path: string): Promise => { - const storageClient = SdkManager.instance.getStorage(); - const folderMeta = await storageClient.getFolderByPath(encodeURIComponent(path)); - return DriveUtils.driveFolderMetaToItem({ - ...folderMeta, - createdAt: folderMeta.createdAt ?? folderMeta.created_at, - updatedAt: folderMeta.updatedAt ?? folderMeta.updated_at, - plainName: folderMeta.plainName ?? folderMeta.plain_name, - parentId: folderMeta.parentId ?? folderMeta.parent_id, - parentUuid: folderMeta.parentUuid ?? folderMeta.parent_uuid, + public getByParentUuidAndName = async (parentUuid: string, name: string): Promise => { + const subFolders = await this.getFolderSubfolders(parentUuid); + const folderMeta = subFolders.find((folder) => folder.plainName === name || folder.name === name); + if (!folderMeta) { + throw new NotFoundError('Folder not found'); + } + + return { + itemType: 'folder', + name: folderMeta.plainName, + uuid: folderMeta.uuid, + parentUuid: folderMeta.parentUuid, + status: FileStatus.EXISTS, + createdAt: new Date(folderMeta.createdAt), + updatedAt: new Date(folderMeta.updatedAt), + creationTime: new Date(folderMeta.creationTime), + modificationTime: new Date(folderMeta.modificationTime), + bucket: folderMeta.bucket, + }; + }; + + public getByPath = async (path: string, parentUuid: string): Promise => { + const onFound = async (uuid: string) => { + const folder = await this.getFolderMetaByUuid(uuid); + return folder; + }; + + const folder = await DatabaseUtils.getFolderByPathGeneric({ + path, + parentUuid, + onFound, + getByParentAndName: this.getByParentUuidAndName.bind(this), }); + if (!folder) { + throw new NotFoundError('Folder not found'); + } + return folder; + }; + + public getFolderMetadataByPath = async (path: string): Promise => { + const rootFolderUuid = await AuthService.instance.getCurrentRootFolder(); + const localFolderDB = await FolderRepository.instance.getByPath(path, rootFolderUuid); + + if (localFolderDB) { + try { + const folder = await this.getFolderMetaByUuid(localFolderDB.uuid); + if (folder) { + return folder; + } + } catch { + logger.error('Folder not found when getting folder by path on local DB', { path, rootFolderUuid }); + } + } + return this.getByPath(path, rootFolderUuid); }; } diff --git a/src/services/network/upload/upload-file.service.ts b/src/services/network/upload/upload-file.service.ts index c895f63f..d33f4321 100644 --- a/src/services/network/upload/upload-file.service.ts +++ b/src/services/network/upload/upload-file.service.ts @@ -125,7 +125,7 @@ export class UploadFileService { const thumbnailTimer = CLIUtils.timer(); if (thumbnailStream && fileSize > 0) { - void ThumbnailService.instance.tryUploadThumbnail({ + await ThumbnailService.instance.tryUploadThumbnail({ bufferStream: thumbnailStream, fileType, bucket, diff --git a/src/services/sdk-manager.service.ts b/src/services/sdk-manager.service.ts index 4339db2a..6c90d3fe 100644 --- a/src/services/sdk-manager.service.ts +++ b/src/services/sdk-manager.service.ts @@ -8,6 +8,8 @@ import { Workspaces } from '@internxt/sdk/dist/workspaces'; export type SdkManagerApiSecurity = ApiSecurity; +const MAX_RETRIES = 3; + /** * Manages all the sdk submodules initialization * based on the current apiSecurity details @@ -21,6 +23,9 @@ export class SdkManager { * @param apiSecurity Security properties to be setted **/ public static readonly init = (apiSecurity: SdkManagerApiSecurity) => { + apiSecurity.retryOptions = { + maxRetries: MAX_RETRIES, + }; SdkManager.apiSecurity = apiSecurity; }; diff --git a/src/types/config.types.ts b/src/types/config.types.ts index 4bad4cbe..ed1ecb39 100644 --- a/src/types/config.types.ts +++ b/src/types/config.types.ts @@ -1,4 +1,5 @@ export interface ConfigKeys { + readonly NODE_ENV: string; readonly DRIVE_WEB_URL: string; readonly DRIVE_NEW_API_URL: string; readonly APP_CRYPTO_SECRET: string; diff --git a/src/types/drive.types.ts b/src/types/drive.types.ts index 3025790c..ddb27b55 100644 --- a/src/types/drive.types.ts +++ b/src/types/drive.types.ts @@ -7,8 +7,8 @@ export type DriveFileItem = Pick & { diff --git a/src/utils/cli.utils.ts b/src/utils/cli.utils.ts index 43de1a2b..13659370 100644 --- a/src/utils/cli.utils.ts +++ b/src/utils/cli.utils.ts @@ -297,10 +297,9 @@ export class CLIUtils { return { networkFacade, bucket, mnemonic }; }; - static readonly fallbackToRootFolderIdIfEmpty = async (folderId: string, userCredentials: LoginCredentials) => { + static readonly fallbackToRootFolderIdIfEmpty = async (folderId: string) => { if (folderId.trim().length === 0) { - const currentWorkspace = await AuthService.instance.getCurrentWorkspace(); - return currentWorkspace?.workspaceData.workspaceUser.rootFolderId ?? userCredentials.user.rootFolderId; + return await AuthService.instance.getCurrentRootFolder(); } else { return folderId; } diff --git a/src/utils/database.utils.ts b/src/utils/database.utils.ts new file mode 100644 index 00000000..cb676024 --- /dev/null +++ b/src/utils/database.utils.ts @@ -0,0 +1,42 @@ +export class DatabaseUtils { + public static readonly CREATE_BATCH_SIZE = 100; + + public static readonly getFolderByPathGeneric = async ({ + path, + parentUuid, + onFound, + getByParentAndName, + }: { + path: string; + parentUuid: string; + onFound: (uuid: string) => Promise; + getByParentAndName: (parentUuid: string, name: string) => Promise<{ uuid: string } | null | undefined>; + }): Promise => { + // Remove leading/trailing slashes + path = path.replace(/^\//, '').replace(/\/$/, ''); + + // Base case: If the path is empty, return the folder's found uuid + if (path.trim().length === 0) { + return onFound(parentUuid); + } + + // Get the next folder name and the remaining path + const slashIndex = path.indexOf('/'); + const currentFolder = slashIndex === -1 ? path : path.substring(0, slashIndex); + const nextPath = slashIndex === -1 ? '' : path.substring(slashIndex + 1); + + const folder = await getByParentAndName(parentUuid, currentFolder); + + // If no intermediate folder is found, return onNotFound callback + if (!folder) { + return; + } + + return this.getFolderByPathGeneric({ + path: nextPath, + parentUuid: folder.uuid, + onFound, + getByParentAndName, + }); + }; +} diff --git a/src/utils/path.utils.ts b/src/utils/path.utils.ts new file mode 100644 index 00000000..9802f7c6 --- /dev/null +++ b/src/utils/path.utils.ts @@ -0,0 +1,13 @@ +import path from 'node:path'; + +export class PathUtils { + static getPathFileData(filePath: string): { folderPath: string; fileName: string; fileType: string | null } { + const folderPath = path.dirname(filePath); + const fileExt = path.extname(filePath); + const fileName = path.basename(filePath, fileExt); + + const fileExtWithoutDot = fileExt.replace('.', '').trim(); + const fileType = fileExtWithoutDot.length > 0 ? fileExtWithoutDot : null; + return { folderPath, fileName, fileType }; + } +} diff --git a/src/webdav/handlers/PUT.handler.ts b/src/webdav/handlers/PUT.handler.ts index 8e3a155e..6f8dda34 100644 --- a/src/webdav/handlers/PUT.handler.ts +++ b/src/webdav/handlers/PUT.handler.ts @@ -125,7 +125,7 @@ export class PUTRequestHandler implements WebDavMethodHandler { const thumbnailTimer = CLIUtils.timer(); if (contentLength > 0 && isThumbnailable && bufferStream) { - void ThumbnailService.instance.tryUploadThumbnail({ + await ThumbnailService.instance.tryUploadThumbnail({ fileUuid: file.uuid, bufferStream, fileType, diff --git a/test/commands/login.test.ts b/test/commands/login.test.ts index a4d1407c..c75736d1 100644 --- a/test/commands/login.test.ts +++ b/test/commands/login.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { ConfigService } from '../../src/services/config.service'; import { UserCredentialsFixture, UserLoginFixture } from '../fixtures/login.fixture'; import { fail } from 'node:assert'; @@ -7,10 +7,6 @@ import { AuthService } from '../../src/services/auth.service'; import { CLIUtils, NoFlagProvidedError } from '../../src/utils/cli.utils'; describe('Login Command', () => { - beforeEach(() => { - vi.restoreAllMocks(); - }); - it('When user logs in with non-interactive and no email, then it throws error', async () => { const getValueFromFlagsSpy = vi .spyOn(CLIUtils, 'getValueFromFlag') diff --git a/test/commands/logout.test.ts b/test/commands/logout.test.ts index af0b42bd..66d70017 100644 --- a/test/commands/logout.test.ts +++ b/test/commands/logout.test.ts @@ -1,14 +1,10 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { ConfigService } from '../../src/services/config.service'; import { UserCredentialsFixture } from '../fixtures/login.fixture'; import Logout from '../../src/commands/logout'; import { AuthService } from '../../src/services/auth.service'; describe('Logout Command', () => { - beforeEach(() => { - vi.restoreAllMocks(); - }); - it('When user is logged out, then it returns false', async () => { const readUserSpy = vi.spyOn(ConfigService.instance, 'readUser').mockResolvedValue(undefined); const networkLogout = vi.spyOn(AuthService.instance, 'logout').mockRejectedValue(new Error()); diff --git a/test/commands/upload-folder.test.ts b/test/commands/upload-folder.test.ts index 2cb94223..54c4c98f 100644 --- a/test/commands/upload-folder.test.ts +++ b/test/commands/upload-folder.test.ts @@ -23,7 +23,6 @@ describe('Upload Folder Command', () => { }; beforeEach(() => { - vi.restoreAllMocks(); configReadUserSpy = vi.spyOn(ConfigService.instance, 'readUser').mockResolvedValue({ user: UserFixture, token: 'mock-token', diff --git a/test/commands/whoami.test.ts b/test/commands/whoami.test.ts index 1e6b1fb3..f9d31fcd 100644 --- a/test/commands/whoami.test.ts +++ b/test/commands/whoami.test.ts @@ -1,14 +1,10 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { ConfigService } from '../../src/services/config.service'; import { UserCredentialsFixture } from '../fixtures/login.fixture'; import Whoami from '../../src/commands/whoami'; import { ValidationService } from '../../src/services/validation.service'; describe('Whoami Command', () => { - beforeEach(() => { - vi.restoreAllMocks(); - }); - it('When user is logged out, then it returns false', async () => { const readUserSpy = vi.spyOn(ConfigService.instance, 'readUser').mockResolvedValue(undefined); const clearUserSpy = vi.spyOn(ConfigService.instance, 'clearUser').mockRejectedValue(new Error()); diff --git a/test/fixtures/drive.fixture.ts b/test/fixtures/drive.fixture.ts index 6b96510a..3875ad6d 100644 --- a/test/fixtures/drive.fixture.ts +++ b/test/fixtures/drive.fixture.ts @@ -32,6 +32,8 @@ export const newFolderItem = (attributes?: Partial): DriveFolde updatedAt: getRandomDate(), status: 'EXISTS', parentUuid: randomUUID(), + creationTime: getRandomDate(), + modificationTime: getRandomDate(), }; return { ...folder, ...attributes }; }; @@ -172,6 +174,8 @@ export const newDriveFolder = (attributes?: Partial): Dri createdAt: getRandomDate(), updatedAt: getRandomDate(), status: FileStatus.EXISTS, + creationTime: getRandomDate(), + modificationTime: getRandomDate(), }; return new DriveFolder({ ...folder, ...attributes }); }; diff --git a/test/services/auth.service.test.ts b/test/services/auth.service.test.ts index e6f320ef..5f95b0c1 100644 --- a/test/services/auth.service.test.ts +++ b/test/services/auth.service.test.ts @@ -19,8 +19,6 @@ import { paths } from '@internxt/sdk/dist/schema'; describe('Auth service', () => { beforeEach(() => { - vi.restoreAllMocks(); - vi.spyOn(ConfigService.instance, 'readUser').mockResolvedValue(UserCredentialsFixture); vi.spyOn(ConfigService.instance, 'saveUser').mockResolvedValue(undefined); }); diff --git a/test/services/config.service.test.ts b/test/services/config.service.test.ts index 170c3039..65851bb9 100644 --- a/test/services/config.service.test.ts +++ b/test/services/config.service.test.ts @@ -22,7 +22,6 @@ const env = Object.assign({}, process.env); describe('Config service', () => { beforeEach(() => { process.env = env; - vi.restoreAllMocks(); }); it('When an env property is requested, then the get method return its value', async () => { diff --git a/test/services/crypto.service.test.ts b/test/services/crypto.service.test.ts index fc083d4b..cf66355c 100644 --- a/test/services/crypto.service.test.ts +++ b/test/services/crypto.service.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import crypto from 'node:crypto'; import { ConfigService } from '../../src/services/config.service'; import { CryptoService } from '../../src/services/crypto.service'; @@ -7,10 +7,6 @@ import { Keys } from '@internxt/sdk'; import { KeysService } from '../../src/services/keys.service'; describe('Crypto service', () => { - beforeEach(() => { - vi.restoreAllMocks(); - }); - it('When text is encrypted using crypto secret env, then it can be decrypted back', () => { const envEndpoint: { key: keyof ConfigKeys; value: string } = { key: 'APP_CRYPTO_SECRET', diff --git a/test/services/database/database.service.test.ts b/test/services/database/database.service.test.ts new file mode 100644 index 00000000..d9e89eab --- /dev/null +++ b/test/services/database/database.service.test.ts @@ -0,0 +1,56 @@ +import { describe, it, expect, vi } from 'vitest'; +import { DatabaseService } from '../../../src/services/database/database.service'; +import { DriveFileModel } from '../../../src/services/database/drive-file/drive-file.model'; +import { DriveFolderModel } from '../../../src/services/database/drive-folder/drive-folder.model'; +import { ConfigService } from '../../../src/services/config.service'; +import { DRIVE_SQLITE_FILE } from '../../../src/constants/configs'; + +describe('DatabaseService', () => { + describe('dataSource configuration', () => { + it('should configure sqljs when NODE_ENV is test', () => { + const configServiceInstancespyOn = vi.spyOn(ConfigService.instance, 'get'); + configServiceInstancespyOn.mockReturnValueOnce('test'); + + const service = new DatabaseService(); + + expect(service.dataSource.options.type).toBe('sqljs'); + expect(service.dataSource.options.synchronize).toBe(true); + expect(service.dataSource.options.entities).toEqual([DriveFileModel, DriveFolderModel]); + expect(configServiceInstancespyOn).toHaveBeenCalledWith('NODE_ENV', false); + }); + + it('should configure better-sqlite3 when NODE_ENV is not test', () => { + const configServiceInstancespyOn = vi.spyOn(ConfigService.instance, 'get'); + configServiceInstancespyOn.mockReturnValueOnce('production'); + + const service = new DatabaseService(); + + expect(service.dataSource.options.type).toBe('better-sqlite3'); + expect(service.dataSource.options.database).toBe(DRIVE_SQLITE_FILE); + expect(service.dataSource.options.synchronize).toBe(true); + expect(service.dataSource.options.entities).toEqual([DriveFileModel, DriveFolderModel]); + expect(configServiceInstancespyOn).toHaveBeenCalledWith('NODE_ENV', false); + }); + }); + + describe('Integration scenarios', () => { + it('should handle initialize, clear, and destroy sequence', async () => { + const configServiceInstancespyOn = vi.spyOn(ConfigService.instance, 'get'); + configServiceInstancespyOn.mockReturnValueOnce('test'); + + const service = new DatabaseService(); + + const initializeSpy = vi.spyOn(service.dataSource, 'initialize').mockResolvedValue(service.dataSource); + const clearSpy = vi.spyOn(service.dataSource, 'synchronize').mockResolvedValue(undefined); + const destroySpy = vi.spyOn(service.dataSource, 'destroy').mockResolvedValue(undefined); + + await service.initialize(); + await service.clear(); + await service.destroy(); + + expect(initializeSpy).toHaveBeenCalledTimes(1); + expect(clearSpy).toHaveBeenCalledTimes(1); + expect(destroySpy).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/test/services/drive/drive-file.service.test.ts b/test/services/drive/drive-file.service.test.ts index 557e4a51..c4bec013 100644 --- a/test/services/drive/drive-file.service.test.ts +++ b/test/services/drive/drive-file.service.test.ts @@ -12,8 +12,6 @@ describe('Drive file Service', () => { const sut = DriveFileService.instance; beforeEach(() => { - vi.restoreAllMocks(); - vi.spyOn(ConfigService.instance, 'readUser').mockResolvedValue(UserCredentialsFixture); vi.spyOn(ConfigService.instance, 'saveUser').mockResolvedValue(undefined); }); diff --git a/test/services/drive/drive-folder.service.test.ts b/test/services/drive/drive-folder.service.test.ts index 78d57110..7f99c639 100644 --- a/test/services/drive/drive-folder.service.test.ts +++ b/test/services/drive/drive-folder.service.test.ts @@ -13,8 +13,6 @@ describe('Drive folder Service', () => { const sut = DriveFolderService.instance; beforeEach(() => { - vi.restoreAllMocks(); - vi.spyOn(ConfigService.instance, 'readUser').mockResolvedValue(UserCredentialsFixture); vi.spyOn(ConfigService.instance, 'saveUser').mockResolvedValue(undefined); }); diff --git a/test/services/keys.service.test.ts b/test/services/keys.service.test.ts index 6e3f20a1..954cc18c 100644 --- a/test/services/keys.service.test.ts +++ b/test/services/keys.service.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import crypto from 'node:crypto'; import * as openpgp from 'openpgp'; import { KeysService } from '../../src/services/keys.service'; @@ -13,10 +13,6 @@ describe('Keys service', () => { salt: crypto.randomBytes(64).toString('hex'), }; - beforeEach(() => { - vi.restoreAllMocks(); - }); - it('When message is encrypted with private key & password, then it can be decrypted using same data', async () => { const plainPrivateKey = crypto.randomBytes(16).toString('hex'); const password = crypto.randomBytes(8).toString('hex'); diff --git a/test/services/local-filesystem/local-filesystem.service.test.ts b/test/services/local-filesystem/local-filesystem.service.test.ts index 9e03cd3f..509cef4a 100644 --- a/test/services/local-filesystem/local-filesystem.service.test.ts +++ b/test/services/local-filesystem/local-filesystem.service.test.ts @@ -32,7 +32,6 @@ describe('Local Filesystem Service', () => { }) as unknown as Dirent; beforeEach(() => { - vi.clearAllMocks(); service = LocalFilesystemService.instance; mockReaddir.mockResolvedValue([]); }); diff --git a/test/services/network/download.service.test.ts b/test/services/network/download.service.test.ts index 8a74caf7..b39e0e2d 100644 --- a/test/services/network/download.service.test.ts +++ b/test/services/network/download.service.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { DownloadService } from '../../../src/services/network/download.service'; import { Readable } from 'node:stream'; import axios from 'axios'; @@ -6,10 +6,6 @@ import axios from 'axios'; describe('Download Service', () => { const sut = DownloadService.instance; - beforeEach(() => { - vi.restoreAllMocks(); - }); - it('When a file is downloaded, should return a ReadableStream', async () => { const fileContent = Buffer.from('file-content'); const readableContent = new Readable({ diff --git a/test/services/network/network-facade.service.test.ts b/test/services/network/network-facade.service.test.ts index 1aef275f..4e09266b 100644 --- a/test/services/network/network-facade.service.test.ts +++ b/test/services/network/network-facade.service.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { NetworkFacade } from '../../../src/services/network/network-facade.service'; import { SdkManager } from '../../../src/services/sdk-manager.service'; import path from 'node:path'; @@ -12,10 +12,6 @@ import { ConfigService } from '../../../src/services/config.service'; import { UserFixture } from '../../fixtures/auth.fixture'; describe('Network Facade Service', () => { - beforeEach(() => { - vi.restoreAllMocks(); - }); - const getNetworkMock = () => { return SdkManager.instance.getNetwork({ user: 'user', diff --git a/test/services/network/upload/upload-facade.service.test.ts b/test/services/network/upload/upload-facade.service.test.ts index 76a86d0b..020e61b8 100644 --- a/test/services/network/upload/upload-facade.service.test.ts +++ b/test/services/network/upload/upload-facade.service.test.ts @@ -21,7 +21,6 @@ describe('UploadFacade', () => { const folderMap = new Map([[folderName, 'folder-uuid-123']]); beforeEach(() => { - vi.clearAllMocks(); sut = UploadFacade.instance; vi.spyOn(LocalFilesystemService.instance, 'scanLocalDirectory').mockResolvedValue({ folders: [createFileSystemNodeFixture({ type: 'folder', name: folderName, relativePath: folderName })], diff --git a/test/services/network/upload/upload-file.service.test.ts b/test/services/network/upload/upload-file.service.test.ts index adda9f37..5828e312 100644 --- a/test/services/network/upload/upload-file.service.test.ts +++ b/test/services/network/upload/upload-file.service.test.ts @@ -36,7 +36,6 @@ describe('UploadFileService', () => { } as unknown as NetworkFacade; beforeEach(() => { - vi.clearAllMocks(); sut = UploadFileService.instance; vi.mocked(stat).mockResolvedValue(createMockStats(1024) as Awaited>); vi.mocked(createReadStream).mockReturnValue(createMockReadStream() as ReturnType); diff --git a/test/services/network/upload/upload-folder.service.test.ts b/test/services/network/upload/upload-folder.service.test.ts index b070c6c8..6f91b85b 100644 --- a/test/services/network/upload/upload-folder.service.test.ts +++ b/test/services/network/upload/upload-folder.service.test.ts @@ -10,7 +10,6 @@ describe('UploadFolderService', () => { let sut: UploadFolderService; beforeEach(() => { - vi.clearAllMocks(); sut = UploadFolderService.instance; vi.spyOn(DriveFolderService.instance, 'createFolder').mockReturnValue([ Promise.resolve({ uuid: 'mock-folder-uuid' }), diff --git a/test/services/sdkmanager.service.test.ts b/test/services/sdkmanager.service.test.ts index 809640d5..aefbb4ef 100644 --- a/test/services/sdkmanager.service.test.ts +++ b/test/services/sdkmanager.service.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import crypto from 'node:crypto'; import { Auth, Drive, Network } from '@internxt/sdk'; import { Trash } from '@internxt/sdk/dist/drive'; @@ -16,10 +16,6 @@ describe('SDKManager service', () => { clientVersion: crypto.randomBytes(16).toString('hex'), }; - beforeEach(() => { - vi.restoreAllMocks(); - }); - it('When SDKManager ApiSecurityFixture is requested, then it is returned from static property', () => { const ApiSecurityFixture: SdkManagerApiSecurity = { token: crypto.randomBytes(16).toString('hex'), diff --git a/test/services/thumbnail.service.test.ts b/test/services/thumbnail.service.test.ts index d3be973a..b509559b 100644 --- a/test/services/thumbnail.service.test.ts +++ b/test/services/thumbnail.service.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { BufferStream } from '../../src/utils/stream.utils'; import { ThumbnailService } from '../../src/services/thumbnail.service'; import path from 'node:path'; @@ -7,10 +7,6 @@ import { Readable } from 'node:stream'; describe('Thumbnail Service tests', () => { const testFilePath = path.join(process.cwd(), 'test/fixtures/test-content.fixture.txt'); - beforeEach(() => { - vi.restoreAllMocks(); - }); - describe('createFileStreamWithBuffer', () => { it('should create BufferStream and pipe stream when file type is thumbnailable', () => { const result = ThumbnailService.instance.createFileStreamWithBuffer({ path: testFilePath, fileType: 'png' }); diff --git a/test/services/usage.service.test.ts b/test/services/usage.service.test.ts index 4a306fe9..230e1473 100644 --- a/test/services/usage.service.test.ts +++ b/test/services/usage.service.test.ts @@ -1,14 +1,10 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { randomInt, randomUUID } from 'node:crypto'; import { Storage } from '@internxt/sdk/dist/drive'; import { UsageService } from '../../src/services/usage.service'; import { SdkManager } from '../../src/services/sdk-manager.service'; describe('Usage Service', () => { - beforeEach(() => { - vi.restoreAllMocks(); - }); - it('When getting user usage, it should return the total usage', async () => { const drive = randomInt(2000000000); const backups = randomInt(2000000000); diff --git a/test/services/validation.service.test.ts b/test/services/validation.service.test.ts index 3be79192..ce4bf38e 100644 --- a/test/services/validation.service.test.ts +++ b/test/services/validation.service.test.ts @@ -1,14 +1,10 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { auth } from '@internxt/lib'; import { randomInt, randomUUID } from 'node:crypto'; import { UserFixture } from '../fixtures/auth.fixture'; import { ValidationService } from '../../src/services/validation.service'; describe('Validation Service', () => { - beforeEach(() => { - vi.restoreAllMocks(); - }); - it('When email is not valid, then validation service validates it as expected', () => { vi.spyOn(auth, 'isValidEmail').mockReturnValue(false); const isValidEmail = ValidationService.instance.validateEmail(UserFixture.email); diff --git a/test/utils/cli.utils.test.ts b/test/utils/cli.utils.test.ts index ff344f62..705a60ad 100644 --- a/test/utils/cli.utils.test.ts +++ b/test/utils/cli.utils.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect, vi, beforeEach, afterEach, MockInstance } from 'vitest'; +import { describe, it, expect, vi, beforeEach, MockInstance } from 'vitest'; import { ux } from '@oclif/core'; import { CLIUtils } from '../../src/utils/cli.utils'; import { Direction } from 'node:readline'; @@ -48,7 +48,6 @@ describe('CliUtils', () => { const mockAppDetails = {} as ReturnType; beforeEach(() => { - vi.clearAllMocks(); process.stdout.write = vi.fn(); process.stdout.clearLine = vi.fn(); stdoutWrite = vi.spyOn(process.stdout, 'write').mockImplementation(() => true); @@ -65,10 +64,6 @@ describe('CliUtils', () => { vi.spyOn(ConfigService.instance, 'get').mockReturnValue(BRIDGE_URL); }); - afterEach(() => { - vi.restoreAllMocks(); - }); - describe('clearPreviousLine', () => { it('should move cursor up and clear line when jsonFlag is false', () => { CLIUtils.clearPreviousLine(false); diff --git a/test/utils/crypto.utils.test.ts b/test/utils/crypto.utils.test.ts index fd9eb07f..e5570ab5 100644 --- a/test/utils/crypto.utils.test.ts +++ b/test/utils/crypto.utils.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import crypto from 'node:crypto'; import { CryptoUtils } from '../../src/utils/crypto.utils'; import { ConfigService } from '../../src/services/config.service'; @@ -11,10 +11,6 @@ describe('Crypto utils', () => { salt: crypto.randomBytes(64).toString('hex'), }; - beforeEach(() => { - vi.restoreAllMocks(); - }); - it('When Magic IV or Magic Salt are missing should throw an error', () => { try { CryptoUtils.getAesInit(); diff --git a/test/utils/errors.utils.test.ts b/test/utils/errors.utils.test.ts index 2fa5140c..44175372 100644 --- a/test/utils/errors.utils.test.ts +++ b/test/utils/errors.utils.test.ts @@ -1,12 +1,8 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { ErrorUtils } from '../../src/utils/errors.utils'; import { logger } from '../../src/utils/logger.utils'; describe('Errors Utils', () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - it('When reporting an error, should log with the expected message and properties', () => { const error = new Error('Test Error'); const props = { key: 'value' }; diff --git a/test/utils/network.utils.test.ts b/test/utils/network.utils.test.ts index 5e0d8da4..e5c86d02 100644 --- a/test/utils/network.utils.test.ts +++ b/test/utils/network.utils.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { randomBytes, randomInt, X509Certificate } from 'node:crypto'; import selfsigned, { GenerateResult } from 'selfsigned'; import { readFile, stat, writeFile } from 'node:fs/promises'; @@ -30,10 +30,6 @@ vi.mock('node:crypto', async () => { const mock509Certificate = vi.mocked(X509Certificate); describe('Network utils', () => { - beforeEach(() => { - vi.restoreAllMocks(); - }); - it('When obtaining auth credentials, should return the password as a SHA256 hash', async () => { const result = NetworkUtils.getAuthFromCredentials({ user: 'test', diff --git a/test/utils/pm2.utils.test.ts b/test/utils/pm2.utils.test.ts index 9717ed3f..8001c280 100644 --- a/test/utils/pm2.utils.test.ts +++ b/test/utils/pm2.utils.test.ts @@ -1,13 +1,9 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import pm2 from 'pm2'; import { PM2Utils } from '../../src/utils/pm2.utils'; import { fail } from 'node:assert'; describe('PM2 utils', () => { - beforeEach(() => { - vi.restoreAllMocks(); - }); - it('When connecting, should connect to PM2 daemon', async () => { // @ts-expect-error - The error callback does not include an error const connectStub = vi.spyOn(pm2, 'connect').mockImplementation((callback) => callback()); diff --git a/test/utils/stream.utils.test.ts b/test/utils/stream.utils.test.ts index 1e21dfcf..03f5a352 100644 --- a/test/utils/stream.utils.test.ts +++ b/test/utils/stream.utils.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { StreamUtils } from '../../src/utils/stream.utils'; import { createReadStream, readFileSync, WriteStream } from 'node:fs'; import path from 'node:path'; @@ -6,10 +6,6 @@ import path from 'node:path'; describe('Stream utils', () => { const fileWithContent = path.join(process.cwd(), 'test/fixtures/test-content.fixture.txt'); - beforeEach(() => { - vi.restoreAllMocks(); - }); - it('When a ReadStream is given, should return a ReadableStream', async () => { const content = readFileSync(fileWithContent, 'utf-8'); diff --git a/test/utils/thumbnail.utils.test.ts b/test/utils/thumbnail.utils.test.ts index d8a21311..b8566d57 100644 --- a/test/utils/thumbnail.utils.test.ts +++ b/test/utils/thumbnail.utils.test.ts @@ -1,11 +1,7 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { ThumbnailUtils } from '../../src/utils/thumbnail.utils'; describe('Thumbnail Utils tests', () => { - beforeEach(() => { - vi.restoreAllMocks(); - }); - describe('isFileThumbnailable', () => { it('should return true for valid image extensions', () => { expect(ThumbnailUtils.isFileThumbnailable('jpg')).toBe(true); diff --git a/test/utils/webdav.utils.test.ts b/test/utils/webdav.utils.test.ts index 66ae31ec..a22de306 100644 --- a/test/utils/webdav.utils.test.ts +++ b/test/utils/webdav.utils.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { WebDavUtils } from '../../src/utils/webdav.utils'; import { WebDavRequestedResource } from '../../src/types/webdav.types'; import { newFileItem, newFolderItem } from '../fixtures/drive.fixture'; @@ -7,10 +7,6 @@ import { DriveFileService } from '../../src/services/drive/drive-file.service'; import AppError from '@internxt/sdk/dist/shared/types/errors'; describe('Webdav utils', () => { - beforeEach(() => { - vi.restoreAllMocks(); - }); - describe('joinURL', () => { it('When a list of path components are given, then it should generate a correct href', () => { const href = WebDavUtils.joinURL('/path', 'to', 'file'); diff --git a/test/webdav/handlers/DELETE.handler.test.ts b/test/webdav/handlers/DELETE.handler.test.ts index 7b4d207d..aa27dc3d 100644 --- a/test/webdav/handlers/DELETE.handler.test.ts +++ b/test/webdav/handlers/DELETE.handler.test.ts @@ -17,8 +17,6 @@ import { UserCredentialsFixture } from '../../fixtures/login.fixture'; describe('DELETE request handler', () => { beforeEach(() => { - vi.restoreAllMocks(); - vi.spyOn(AuthService.instance, 'getAuthDetails').mockResolvedValue(UserCredentialsFixture); }); diff --git a/test/webdav/handlers/GET.handler.test.ts b/test/webdav/handlers/GET.handler.test.ts index 8f9a1ec8..025a8a4d 100644 --- a/test/webdav/handlers/GET.handler.test.ts +++ b/test/webdav/handlers/GET.handler.test.ts @@ -28,8 +28,6 @@ describe('GET request handler', () => { const networkOptions: NetworkOptions = getNetworkOptionsMock({ networkFacade }); beforeEach(() => { - vi.restoreAllMocks(); - vi.spyOn(CLIUtils, 'prepareNetwork').mockResolvedValue(networkOptions); sut = new GETRequestHandler(); diff --git a/test/webdav/handlers/HEAD.handler.test.ts b/test/webdav/handlers/HEAD.handler.test.ts index 471d9af8..3d681ba5 100644 --- a/test/webdav/handlers/HEAD.handler.test.ts +++ b/test/webdav/handlers/HEAD.handler.test.ts @@ -14,9 +14,9 @@ import { randomInt } from 'crypto'; describe('HEAD request handler', () => { let sut: HEADRequestHandler; + beforeEach(() => { sut = new HEADRequestHandler(); - vi.restoreAllMocks(); }); it('When a folder is requested, it should reply with a 200', async () => { diff --git a/test/webdav/handlers/MKCOL.handler.test.ts b/test/webdav/handlers/MKCOL.handler.test.ts index cf5e1613..35e9d541 100644 --- a/test/webdav/handlers/MKCOL.handler.test.ts +++ b/test/webdav/handlers/MKCOL.handler.test.ts @@ -17,7 +17,6 @@ describe('MKCOL request handler', () => { let sut: MKCOLRequestHandler; beforeEach(() => { - vi.restoreAllMocks(); sut = new MKCOLRequestHandler(); vi.spyOn(AuthService.instance, 'getAuthDetails').mockResolvedValue(UserCredentialsFixture); diff --git a/test/webdav/handlers/OPTIONS.handler.test.ts b/test/webdav/handlers/OPTIONS.handler.test.ts index 17e51d4c..2fd47875 100644 --- a/test/webdav/handlers/OPTIONS.handler.test.ts +++ b/test/webdav/handlers/OPTIONS.handler.test.ts @@ -1,13 +1,9 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { OPTIONSRequestHandler } from '../../../src/webdav/handlers/OPTIONS.handler'; import { UserSettingsFixture } from '../../fixtures/auth.fixture'; import { createWebDavRequestFixture, createWebDavResponseFixture } from '../../fixtures/webdav.fixture'; describe('OPTIONS request handler', () => { - beforeEach(() => { - vi.restoreAllMocks(); - }); - it('When the root folder is requested, it should return all of the server allowed methods', async () => { const requestHandler = new OPTIONSRequestHandler(); diff --git a/test/webdav/handlers/PROPFIND.handler.test.ts b/test/webdav/handlers/PROPFIND.handler.test.ts index 6be0ce82..cae9c701 100644 --- a/test/webdav/handlers/PROPFIND.handler.test.ts +++ b/test/webdav/handlers/PROPFIND.handler.test.ts @@ -32,7 +32,6 @@ describe('PROPFIND request handler', () => { beforeEach(() => { sut = new PROPFINDRequestHandler(); - vi.restoreAllMocks(); }); it('When the root folder exists and there is no content, then it should return the correct XML', async () => { @@ -52,7 +51,7 @@ describe('PROPFIND request handler', () => { }); const folderFixture = newFolderItem({ - id: Number.parseInt(UserSettingsFixture.rootFolderId), + uuid: UserSettingsFixture.rootFolderId, }); const usageFixture = crypto.randomInt(2000000000); const spaceLimitFixture = crypto.randomInt(2000000000); @@ -98,7 +97,7 @@ describe('PROPFIND request handler', () => { }); const folderFixture = newFolderItem({ - id: Number.parseInt(UserSettingsFixture.rootFolderId), + uuid: UserSettingsFixture.rootFolderId, }); const paginatedFolder1 = newPaginatedFolder({ plainName: 'folder_1', diff --git a/test/webdav/handlers/PUT.handler.test.ts b/test/webdav/handlers/PUT.handler.test.ts index 160e1e2b..0d769835 100644 --- a/test/webdav/handlers/PUT.handler.test.ts +++ b/test/webdav/handlers/PUT.handler.test.ts @@ -23,8 +23,6 @@ describe('PUT request handler', () => { let sut: PUTRequestHandler; beforeEach(() => { - vi.restoreAllMocks(); - networkFacade = getNetworkFacadeMock(); vi.spyOn(CLIUtils, 'prepareNetwork').mockResolvedValue(getNetworkOptionsMock({ networkFacade })); @@ -39,7 +37,6 @@ describe('PUT request handler', () => { }); const folderFixture = newFolderItem({ name: requestedParentFolderResource.name }); const fileFixture = newDriveFile({ - folderId: folderFixture.id, folderUuid: folderFixture.uuid, size: 0, fileId: undefined, @@ -92,7 +89,7 @@ describe('PUT request handler', () => { folderName: '', }); const folderFixture = newFolderItem({ name: requestedParentFolderResource.name }); - const fileFixture = newDriveFile({ folderId: folderFixture.id, folderUuid: folderFixture.uuid }); + const fileFixture = newDriveFile({ folderUuid: folderFixture.uuid }); const request = createWebDavRequestFixture({ method: 'PUT', @@ -146,7 +143,7 @@ describe('PUT request handler', () => { folderName: '', }); const folderFixture = newFolderItem({ name: requestedParentFolderResource.name }); - const fileFixture = newDriveFile({ folderId: folderFixture.id, folderUuid: folderFixture.uuid }); + const fileFixture = newDriveFile({ folderUuid: folderFixture.uuid }); const request = createWebDavRequestFixture({ method: 'PUT', @@ -202,7 +199,7 @@ describe('PUT request handler', () => { folderName: '', }); const folderFixture = newFolderItem({ name: requestedParentFolderResource.name }); - const fileFixture = newDriveFile({ folderId: folderFixture.id, folderUuid: folderFixture.uuid }); + const fileFixture = newDriveFile({ folderUuid: folderFixture.uuid }); const request = createWebDavRequestFixture({ method: 'PUT', diff --git a/test/webdav/middlewares/auth.middleware.test.ts b/test/webdav/middlewares/auth.middleware.test.ts index 613eff86..b2f475d6 100644 --- a/test/webdav/middlewares/auth.middleware.test.ts +++ b/test/webdav/middlewares/auth.middleware.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { AuthMiddleware } from '../../../src/webdav/middewares/auth.middleware'; import { createWebDavRequestFixture, createWebDavResponseFixture } from '../../fixtures/webdav.fixture'; import { UserCredentialsFixture } from '../../fixtures/login.fixture'; @@ -7,10 +7,6 @@ import { MissingCredentialsError } from '../../../src/types/command.types'; import { XMLUtils } from '../../../src/utils/xml.utils'; describe('Auth middleware', () => { - beforeEach(() => { - vi.restoreAllMocks(); - }); - it('When the user is not authenticated, then it should return 401', async () => { const req = createWebDavRequestFixture({}); const res = createWebDavResponseFixture({ diff --git a/test/webdav/middlewares/errors.middleware.test.ts b/test/webdav/middlewares/errors.middleware.test.ts index 2edc5d37..41924107 100644 --- a/test/webdav/middlewares/errors.middleware.test.ts +++ b/test/webdav/middlewares/errors.middleware.test.ts @@ -1,14 +1,10 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { ErrorHandlingMiddleware } from '../../../src/webdav/middewares/errors.middleware'; import { createWebDavRequestFixture, createWebDavResponseFixture } from '../../fixtures/webdav.fixture'; import { BadRequestError, NotFoundError, NotImplementedError } from '../../../src/utils/errors.utils'; import { XMLUtils } from '../../../src/utils/xml.utils'; describe('Error handling middleware', () => { - beforeEach(() => { - vi.restoreAllMocks(); - }); - it('When a not found error is received, should respond with a 404', () => { const errorMessage = 'Item not found'; const error = new NotFoundError('Item not found'); diff --git a/test/webdav/middlewares/mkcol.middleware.test.ts b/test/webdav/middlewares/mkcol.middleware.test.ts index 282ad3ab..721b6f89 100644 --- a/test/webdav/middlewares/mkcol.middleware.test.ts +++ b/test/webdav/middlewares/mkcol.middleware.test.ts @@ -1,14 +1,10 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { createWebDavRequestFixture, createWebDavResponseFixture } from '../../fixtures/webdav.fixture'; import { MkcolMiddleware } from '../../../src/webdav/middewares/mkcol.middleware'; import { fail } from 'node:assert'; import { UnsupportedMediaTypeError } from '../../../src/utils/errors.utils'; describe('MKCOL middleware', () => { - beforeEach(() => { - vi.restoreAllMocks(); - }); - it('When MKCOL content is application/xml, then it should call next', () => { const req = createWebDavRequestFixture({ method: 'MKCOL', diff --git a/test/webdav/middlewares/request-logger.middleware.test.ts b/test/webdav/middlewares/request-logger.middleware.test.ts index a3a58063..b7e0dbf0 100644 --- a/test/webdav/middlewares/request-logger.middleware.test.ts +++ b/test/webdav/middlewares/request-logger.middleware.test.ts @@ -1,13 +1,9 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { webdavLogger } from '../../../src/utils/logger.utils'; import { RequestLoggerMiddleware } from '../../../src/webdav/middewares/request-logger.middleware'; import { createWebDavRequestFixture, createWebDavResponseFixture } from '../../fixtures/webdav.fixture'; describe('Request logger middleware', () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - it('When a request is received, should log only the specified methods', () => { const req = createWebDavRequestFixture({ method: 'PROPFIND', diff --git a/test/webdav/services/webdav-folder.service.test.ts b/test/webdav/services/webdav-folder.service.test.ts index 648a14d4..09a99dac 100644 --- a/test/webdav/services/webdav-folder.service.test.ts +++ b/test/webdav/services/webdav-folder.service.test.ts @@ -32,7 +32,6 @@ describe('WebDavFolderService', () => { }; beforeEach(() => { - vi.restoreAllMocks(); driveFolderService = DriveFolderService.instance; configService = ConfigService.instance; sut = WebDavFolderService.instance; diff --git a/test/webdav/webdav-server.test.ts b/test/webdav/webdav-server.test.ts index 806ad6cf..6c749bbb 100644 --- a/test/webdav/webdav-server.test.ts +++ b/test/webdav/webdav-server.test.ts @@ -1,31 +1,22 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import express from 'express'; import { randomBytes, randomInt } from 'node:crypto'; import http from 'http'; import https from 'https'; import { ConfigService } from '../../src/services/config.service'; -import { DriveFolderService } from '../../src/services/drive/drive-folder.service'; import { WebDavServer } from '../../src/webdav/webdav-server'; -import { DriveFileService } from '../../src/services/drive/drive-file.service'; -import { DownloadService } from '../../src/services/network/download.service'; -import { AuthService } from '../../src/services/auth.service'; -import { CryptoService } from '../../src/services/crypto.service'; import { NetworkUtils } from '../../src/utils/network.utils'; -import { TrashService } from '../../src/services/drive/trash.service'; import { WebdavConfig } from '../../src/types/command.types'; import { UserCredentialsFixture } from '../fixtures/login.fixture'; describe('WebDav server', () => { - beforeEach(() => { - vi.restoreAllMocks(); - }); - it('When the WebDav server is started with https, it should generate self-signed certificates', async () => { const webdavConfig: WebdavConfig = { host: '127.0.0.1', port: randomInt(65535).toString(), protocol: 'https', timeoutMinutes: randomInt(900), + createFullPath: true, }; const sslSelfSigned = { private: randomBytes(8).toString('hex'), @@ -50,16 +41,7 @@ describe('WebDav server', () => { }); const app = express(); - const server = new WebDavServer( - app, - ConfigService.instance, - DriveFileService.instance, - DriveFolderService.instance, - DownloadService.instance, - AuthService.instance, - CryptoService.instance, - TrashService.instance, - ); + const server = new WebDavServer(app); await server.start(); expect(createHTTPSServerStub).toHaveBeenCalledOnce(); @@ -73,6 +55,7 @@ describe('WebDav server', () => { port: randomInt(65535).toString(), protocol: 'http', timeoutMinutes: randomInt(900), + createFullPath: true, }; vi.spyOn(ConfigService.instance, 'readWebdavConfig').mockResolvedValue(webdavConfig); @@ -87,16 +70,7 @@ describe('WebDav server', () => { }); const app = express(); - const server = new WebDavServer( - app, - ConfigService.instance, - DriveFileService.instance, - DriveFolderService.instance, - DownloadService.instance, - AuthService.instance, - CryptoService.instance, - TrashService.instance, - ); + const server = new WebDavServer(app); await server.start(); expect(createHTTPServerStub).toHaveBeenCalledOnce(); diff --git a/vitest.config.mjs b/vitest.config.mjs index dc973f24..aced18cf 100644 --- a/vitest.config.mjs +++ b/vitest.config.mjs @@ -16,6 +16,8 @@ export default defineConfig({ ...coverageConfigDefaults.exclude ], }, + restoreMocks: true, + clearMocks: true, setupFiles: ['dotenv/config', './test/vitest.setup.ts'] } }); diff --git a/yarn.lock b/yarn.lock index 3b3e3c7d..738e71a8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2763,10 +2763,10 @@ dependencies: undici-types "~7.16.0" -"@types/node@25.3.0": - version "25.3.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-25.3.0.tgz#749b1bd4058e51b72e22bd41e9eab6ebd0180470" - integrity sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A== +"@types/node@25.3.1": + version "25.3.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-25.3.1.tgz#82f3f6e30ac3b48560a092d9224a975b5c24e38d" + integrity sha512-hj9YIJimBCipHVfHKRMnvmHg+wfhKc0o4mTtXh9pKBjC8TLJzz0nzGmLi5UJsYAUgSvXFHgb0V2oY10DUFtImw== dependencies: undici-types "~7.18.0" @@ -4459,6 +4459,11 @@ fast-wrap-ansi@^0.2.0: dependencies: fast-string-width "^3.0.2" +fast-xml-builder@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-xml-builder/-/fast-xml-builder-1.0.0.tgz#a485d7e8381f1db983cf006f849d1066e2935241" + integrity sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ== + fast-xml-parser@5.3.6: version "5.3.6" resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-5.3.6.tgz#85a69117ca156b1b3c52e426495b6de266cb6a4b" @@ -4466,11 +4471,12 @@ fast-xml-parser@5.3.6: dependencies: strnum "^2.1.2" -fast-xml-parser@5.3.9: - version "5.3.9" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-5.3.9.tgz#8777df0e215845534b023b2830e75d0068a9fbcf" - integrity sha512-zU0KUuO9U+fLGduTDdxQ6qsQLIxRg4EK5AMduwBNGNCSfCGRSbNS7OpH343NFQlLDg1jxoH68JSbOPAGksIGvg== +fast-xml-parser@5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-5.4.1.tgz#0c81b8ecfb3021e5ad83aa3df904af19a05bc601" + integrity sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A== dependencies: + fast-xml-builder "^1.0.0" strnum "^2.1.2" fastest-levenshtein@^1.0.7: