mirror of https://github.com/laurent22/joplin.git
Fxied tests
parent
439d29387f
commit
a088061de9
|
@ -189,6 +189,7 @@ export default abstract class BaseModel<T> {
|
|||
protected async isNew(object: T, options: SaveOptions): Promise<boolean> {
|
||||
if (options.isNew === false) return false;
|
||||
if (options.isNew === true) return true;
|
||||
if ('id' in object && !(object as WithUuid).id) throw new Error('ID cannot be undefined or null');
|
||||
return !(object as WithUuid).id;
|
||||
}
|
||||
|
||||
|
|
|
@ -266,8 +266,8 @@ export default class FileModel extends BaseModel<File> {
|
|||
|
||||
if ('name' in file && !file.is_root) {
|
||||
const existingFile = await this.fileByName(parentId, file.name);
|
||||
if (existingFile && options.isNew) throw new ErrorConflict(`Already a file with name "${file.name}"`);
|
||||
if (existingFile && file.id === existingFile.id) throw new ErrorConflict(`Already a file with name "${file.name}"`);
|
||||
if (existingFile && options.isNew) throw new ErrorConflict(`Already a file with name "${file.name}" (1)`);
|
||||
if (existingFile && file.id !== existingFile.id) throw new ErrorConflict(`Already a file with name "${file.name}" (2)`);
|
||||
}
|
||||
|
||||
if ('name' in file) {
|
||||
|
@ -471,8 +471,30 @@ export default class FileModel extends BaseModel<File> {
|
|||
const isNew = await this.isNew(object, options);
|
||||
|
||||
const file: File = { ... object };
|
||||
let sourceFile: File = null;
|
||||
|
||||
if ('content' in file) file.size = file.content ? file.content.byteLength : 0;
|
||||
if ('content' in file) {
|
||||
let sourceFileId: string = null;
|
||||
|
||||
if (file.id) {
|
||||
if (!('source_file_id' in file)) throw new Error('source_file_id is required when setting the content');
|
||||
sourceFileId = file.source_file_id;
|
||||
}
|
||||
|
||||
const fileSize = file.content ? file.content.byteLength : 0;
|
||||
|
||||
if (sourceFileId) {
|
||||
sourceFile = {
|
||||
id: sourceFileId,
|
||||
content: file.content,
|
||||
size: fileSize,
|
||||
source_file_id: '',
|
||||
};
|
||||
delete file.content;
|
||||
} else {
|
||||
file.size = fileSize;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNew) {
|
||||
if (!file.parent_id && !file.is_root) file.parent_id = await this.userRootFileId();
|
||||
|
@ -486,7 +508,10 @@ export default class FileModel extends BaseModel<File> {
|
|||
file.owner_id = this.userId;
|
||||
}
|
||||
|
||||
return super.save(file, options);
|
||||
return this.withTransaction(async () => {
|
||||
if (sourceFile) await this.save(sourceFile);
|
||||
return super.save(file, options);
|
||||
});
|
||||
}
|
||||
|
||||
public async childrenCount(id: string): Promise<number> {
|
||||
|
|
|
@ -60,8 +60,23 @@ router.put('api/files/:id/content', async (path: SubPath, ctx: AppContext) => {
|
|||
// https://github.com/laurent22/joplin/issues/4402
|
||||
const buffer = result?.files?.file ? await fs.readFile(result.files.file.path) : Buffer.alloc(0);
|
||||
|
||||
const file: File = await fileModel.pathToFile(fileId, { mustExist: false, returnFullEntity: false });
|
||||
file.content = buffer;
|
||||
const parsedFile: File = await fileModel.pathToFile(fileId, { mustExist: false });
|
||||
|
||||
const isNewFile = !parsedFile.id;
|
||||
|
||||
const file: File = {
|
||||
name: parsedFile.name,
|
||||
content: buffer,
|
||||
source_file_id: 'source_file_id' in parsedFile ? parsedFile.source_file_id : '',
|
||||
};
|
||||
|
||||
if (!isNewFile) {
|
||||
file.id = parsedFile.id;
|
||||
} else {
|
||||
file.name = parsedFile.name;
|
||||
if ('parent_id' in parsedFile) file.parent_id = parsedFile.parent_id;
|
||||
}
|
||||
|
||||
return fileModel.toApiOutput(await fileModel.save(file, { validationRules: { mustBeFile: true } }));
|
||||
});
|
||||
|
||||
|
@ -70,8 +85,12 @@ router.del('api/files/:id/content', async (path: SubPath, ctx: AppContext) => {
|
|||
const fileId = path.id;
|
||||
const file: File = await fileModel.pathToFile(fileId, { mustExist: false, returnFullEntity: false });
|
||||
if (!file) return;
|
||||
file.content = Buffer.alloc(0);
|
||||
await fileModel.save(file, { validationRules: { mustBeFile: true } });
|
||||
|
||||
await fileModel.save({
|
||||
id: file.id,
|
||||
content: Buffer.alloc(0),
|
||||
source_file_id: file.source_file_id,
|
||||
}, { validationRules: { mustBeFile: true } });
|
||||
});
|
||||
|
||||
router.get('api/files/:id/delta', async (path: SubPath, ctx: AppContext) => {
|
||||
|
|
|
@ -44,7 +44,7 @@ describe('api_shares', function() {
|
|||
test('should share a file with another user', async function() {
|
||||
const { user: user1, session: session1 } = await createUserAndSession(1);
|
||||
const { user: user2, session: session2 } = await createUserAndSession(2);
|
||||
await createFile(user1.id, 'root:/test.txt:', 'testing share');
|
||||
await createFile(user1.id, 'root:/test.txt:', 'created by sharer');
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Create the file share object
|
||||
|
@ -85,7 +85,16 @@ describe('api_shares', function() {
|
|||
expect(results.items[0].name).toBe('test.txt');
|
||||
|
||||
const fileContent = await getApi<Buffer>(session2.id, 'files/root:/test.txt:/content');
|
||||
expect(fileContent.toString()).toBe('testing share');
|
||||
expect(fileContent.toString()).toBe('created by sharer');
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// If file is changed by sharee, sharer should see the change too
|
||||
// ----------------------------------------------------------------
|
||||
{
|
||||
await updateFile(user2.id, 'root:/test.txt:', 'modified by sharee');
|
||||
const fileContent = await getApi<Buffer>(session1.id, 'files/root:/test.txt:/content');
|
||||
expect(fileContent.toString()).toBe('modified by sharee');
|
||||
}
|
||||
});
|
||||
|
||||
test('should get updated time of shared file', async function() {
|
||||
|
@ -131,6 +140,10 @@ describe('api_shares', function() {
|
|||
|
||||
await msleep(1);
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// If file is changed on sharer side, sharee should see the changes
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
await updateFile(user1.id, sharerFile.id, 'from sharer');
|
||||
|
||||
{
|
||||
|
@ -147,8 +160,15 @@ describe('api_shares', function() {
|
|||
expect(page2.items[0].item.updated_time).toBe(page1.items[0].item.updated_time);
|
||||
cursor2 = page2.cursor;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// If file is changed on sharee side, sharer should see the changes
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
// TODO: test delta:
|
||||
// - File is changed on sharer side
|
||||
// - File is changed on sharee side
|
||||
|
|
|
@ -239,9 +239,12 @@ export async function createFile(userId: string, path: string, content: string):
|
|||
|
||||
export async function updateFile(userId: string, path: string, content: string): Promise<File> {
|
||||
const fileModel = models().file({ userId });
|
||||
const file: File = await fileModel.pathToFile(path, { returnFullEntity: false });
|
||||
file.content = Buffer.from(content);
|
||||
await fileModel.save(file);
|
||||
const file: File = await fileModel.pathToFile(path, { returnFullEntity: true });
|
||||
await fileModel.save({
|
||||
id: file.id,
|
||||
content: Buffer.from(content),
|
||||
source_file_id: file.source_file_id,
|
||||
});
|
||||
return fileModel.load(file.id);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue