mirror of
https://github.com/unraid/api.git
synced 2025-12-31 13:39:52 -06:00
feat: allow deletion and creation of files with patches
This commit is contained in:
@@ -92,11 +92,12 @@ export abstract class FileModification {
|
||||
if (!patchContents.trim()) {
|
||||
throw new Error('Patch contents are empty');
|
||||
}
|
||||
const currentContent = await readFile(this.filePath, 'utf8');
|
||||
const currentContent = await readFile(this.filePath, 'utf8').catch(() => '');
|
||||
const parsedPatch = parsePatch(patchContents)[0];
|
||||
if (!parsedPatch?.hunks.length) {
|
||||
throw new Error('Invalid Patch Format: No hunks found');
|
||||
}
|
||||
|
||||
const results = applyPatch(currentContent, parsedPatch);
|
||||
if (results === false) {
|
||||
throw new Error(`Failed to apply patch to ${this.filePath}`);
|
||||
@@ -178,7 +179,12 @@ export abstract class FileModification {
|
||||
throw new Error(`Failed to rollback patch from ${this.filePath}`);
|
||||
}
|
||||
|
||||
await writeFile(this.filePath, results);
|
||||
if (results === '') {
|
||||
// Delete the file if the patch results in an empty string
|
||||
await unlink(this.filePath);
|
||||
} else {
|
||||
await writeFile(this.filePath, results);
|
||||
}
|
||||
|
||||
// Clean up the patch file after successful rollback
|
||||
try {
|
||||
|
||||
@@ -74,7 +74,7 @@ const downloadOrRetrieveOriginalFile = async (filePath: string, fileUrl: string)
|
||||
);
|
||||
}
|
||||
}
|
||||
return await readFile(filePath, 'utf-8');
|
||||
return await readFile(filePath, 'utf-8').catch(() => '');
|
||||
};
|
||||
|
||||
async function testModification(testCase: ModificationTestCase, patcher: FileModification) {
|
||||
@@ -101,7 +101,7 @@ async function testModification(testCase: ModificationTestCase, patcher: FileMod
|
||||
|
||||
// Rollback and verify original state
|
||||
await patcher.rollback();
|
||||
const revertedContent = await readFile(filePath, 'utf-8');
|
||||
const revertedContent = await readFile(filePath, 'utf-8').catch(() => '');
|
||||
await expect(revertedContent).toMatch(originalContent);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
|
||||
import { createPatch } from 'diff';
|
||||
import { execa } from 'execa';
|
||||
|
||||
import { fileExists } from '@app/core/utils/files/file-exists';
|
||||
import {
|
||||
FileModification,
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
ShouldApplyWithReason,
|
||||
} from '@app/unraid-api/unraid-file-modifier/file-modification';
|
||||
import { UnraidFileModificationService } from '@app/unraid-api/unraid-file-modifier/unraid-file-modifier.service';
|
||||
import { fileExistsSync } from '@app/core/utils/files/file-exists';
|
||||
|
||||
const FIXTURE_PATH = join(__dirname, 'modifications', '__test__', '__fixtures__', 'text-patch-file.txt');
|
||||
const ORIGINAL_CONTENT = 'original';
|
||||
@@ -80,6 +81,25 @@ describe.sequential('FileModificationService', () => {
|
||||
await expect(service.applyModification(mod)).resolves.toBe(undefined);
|
||||
});
|
||||
|
||||
it('should apply modification if file does not exist', async () => {
|
||||
const mod = new TestFileModification(logger);
|
||||
// @ts-expect-error - This is a protected method, but we need to mock it
|
||||
mod.generatePatch = vi.fn().mockResolvedValue(createPatch(FIXTURE_PATH, '', 'modified'));
|
||||
await fs.unlink(FIXTURE_PATH);
|
||||
await expect(service.applyModification(mod)).resolves.toBe(undefined);
|
||||
expect(mockLogger.warn).toHaveBeenCalledWith('Could not load pregenerated patch for: test');
|
||||
expect(mockLogger.log).toHaveBeenCalledWith(
|
||||
'Applying modification: test - Always Apply this mod'
|
||||
);
|
||||
expect(mockLogger.log).toHaveBeenCalledWith('Modification applied successfully: test');
|
||||
const content = await fs.readFile(FIXTURE_PATH, 'utf-8');
|
||||
expect(content).toBe('modified');
|
||||
await service.rollbackAll();
|
||||
expect(fileExistsSync(FIXTURE_PATH)).toBe(false);
|
||||
expect(mockLogger.log).toHaveBeenCalledWith('Rolling back modification: test');
|
||||
expect(mockLogger.log).toHaveBeenCalledWith('Successfully rolled back modification: test');
|
||||
});
|
||||
|
||||
it('should not rollback any mods without loaded', async () => {
|
||||
await expect(service.rollbackAll()).resolves.toBe(undefined);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user