✅♻️⚰️ back: add e2e test for trashing anonymously uploaded files, deleted e2e_deleteDocument (#433)
This commit is contained in:
committed by
ericlinagora
parent
882120bef0
commit
f228e07373
@@ -23,6 +23,10 @@ import { UserQuota } from "../../../src/services/user/web/types";
|
|||||||
import { Api } from "../utils.api";
|
import { Api } from "../utils.api";
|
||||||
import { OidcJwtVerifier } from "../../../src/services/console/clients/remote-jwks-verifier";
|
import { OidcJwtVerifier } from "../../../src/services/console/clients/remote-jwks-verifier";
|
||||||
|
|
||||||
|
/** The UserApi is an abstraction for E2E tests that
|
||||||
|
* represents the high level actions a user can take
|
||||||
|
* in the application.
|
||||||
|
*/
|
||||||
export default class UserApi {
|
export default class UserApi {
|
||||||
|
|
||||||
private static readonly DOC_URL = "/internal/services/documents/v1";
|
private static readonly DOC_URL = "/internal/services/documents/v1";
|
||||||
@@ -54,8 +58,8 @@ export default class UserApi {
|
|||||||
|
|
||||||
private async init(newUser: boolean, options?: {}) {
|
private async init(newUser: boolean, options?: {}) {
|
||||||
this.dbService = await TestDbService.getInstance(this.platform, true);
|
this.dbService = await TestDbService.getInstance(this.platform, true);
|
||||||
|
this.workspace = this.platform.workspace;
|
||||||
if (newUser) {
|
if (newUser) {
|
||||||
this.workspace = this.platform.workspace;
|
|
||||||
const workspacePK = {
|
const workspacePK = {
|
||||||
id: this.workspace.workspace_id,
|
id: this.workspace.workspace_id,
|
||||||
company_id: this.workspace.company_id,
|
company_id: this.workspace.company_id,
|
||||||
@@ -69,7 +73,6 @@ export default class UserApi {
|
|||||||
uuidv1());
|
uuidv1());
|
||||||
} else {
|
} else {
|
||||||
this.user = this.platform.currentUser;
|
this.user = this.platform.currentUser;
|
||||||
this.workspace = this.platform.workspace;
|
|
||||||
}
|
}
|
||||||
this.api = new Api(this.platform, this.user);
|
this.api = new Api(this.platform, this.user);
|
||||||
this.jwt = await this.doLogin();
|
this.jwt = await this.doLogin();
|
||||||
@@ -227,6 +230,21 @@ export default class UserApi {
|
|||||||
return directory;
|
return directory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Run the provided callback using the specified bearer JWT token.
|
||||||
|
* //TODO: Warning: does not override calls using `this.api` have to discuss
|
||||||
|
*/
|
||||||
|
async impersonateWithJWT<T>(jwt: string, cb: () => Promise<T>): Promise<T> {
|
||||||
|
const previous = this.jwt;
|
||||||
|
this.jwt = jwt;
|
||||||
|
let result: T | undefined = undefined;
|
||||||
|
try {
|
||||||
|
result = await cb();
|
||||||
|
} finally {
|
||||||
|
this.jwt = previous;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
async createDocument(
|
async createDocument(
|
||||||
item: Partial<DriveFile>,
|
item: Partial<DriveFile>,
|
||||||
version: Partial<FileVersion>
|
version: Partial<FileVersion>
|
||||||
@@ -241,25 +259,26 @@ export default class UserApi {
|
|||||||
return deserialize<DriveFile>(DriveFile, response.body);
|
return deserialize<DriveFile>(DriveFile, response.body);
|
||||||
};
|
};
|
||||||
|
|
||||||
async createDefaultDocument(): Promise<DriveFile> {
|
async createDefaultDocument(overrides?: Partial<DriveFile>): Promise<DriveFile> {
|
||||||
const scope: "personal" | "shared" = "shared";
|
const scope: "personal" | "shared" = "shared";
|
||||||
const item = {
|
const item = {
|
||||||
name: "new test file",
|
name: "new test file",
|
||||||
parent_id: "root",
|
parent_id: "root",
|
||||||
company_id: this.platform.workspace.company_id,
|
company_id: this.platform.workspace.company_id,
|
||||||
scope,
|
scope,
|
||||||
|
...overrides,
|
||||||
};
|
};
|
||||||
|
|
||||||
return await this.createDocument(item, {});
|
return await this.createDocument(item, {});
|
||||||
};
|
};
|
||||||
|
|
||||||
async shareWithPublicLink(doc: Partial<DriveFile>, accessLevel: publicAccessLevel) {
|
async shareWithPublicLink(doc: Partial<DriveFile> & { id: string }, accessLevel: publicAccessLevel) {
|
||||||
return await this.updateDocument(doc.id, {
|
return await this.updateDocument(doc.id, {
|
||||||
...doc,
|
...doc,
|
||||||
access_info: {
|
access_info: {
|
||||||
...doc.access_info,
|
...doc.access_info!,
|
||||||
public: {
|
public: {
|
||||||
...doc.access_info.public!,
|
...doc.access_info?.public!,
|
||||||
level: accessLevel
|
level: accessLevel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { TestDbService } from "../utils.prepare.db";
|
|||||||
import {
|
import {
|
||||||
e2e_createDocumentFile,
|
e2e_createDocumentFile,
|
||||||
e2e_createVersion,
|
e2e_createVersion,
|
||||||
e2e_deleteDocument,
|
|
||||||
e2e_updateDocument,
|
e2e_updateDocument,
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
import UserApi from "../common/user-api";
|
import UserApi from "../common/user-api";
|
||||||
@@ -68,22 +67,12 @@ describe("the Drive feature", () => {
|
|||||||
expect(result.item.name).toEqual("Shared Drive");
|
expect(result.item.name).toEqual("Shared Drive");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("did fetch the trash", async () => {
|
|
||||||
await TestDbService.getInstance(platform, true);
|
|
||||||
|
|
||||||
const response = await currentUser.getDocument("trash");
|
|
||||||
const result = deserialize<DriveItemDetailsMockClass>(DriveItemDetailsMockClass, response.body);
|
|
||||||
|
|
||||||
expect(result.item.id).toEqual("trash");
|
|
||||||
expect(result.item.name).toEqual("Trash");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("did delete an item", async () => {
|
it("did delete an item", async () => {
|
||||||
const createItemResult = await currentUser.createDefaultDocument();
|
const createItemResult = await currentUser.createDefaultDocument();
|
||||||
|
|
||||||
expect(createItemResult.id).toBeDefined();
|
expect(createItemResult.id).toBeDefined();
|
||||||
|
|
||||||
const deleteResponse = await e2e_deleteDocument(platform, createItemResult.id);
|
const deleteResponse = await currentUser.delete(createItemResult.id);
|
||||||
expect(deleteResponse.statusCode).toEqual(200);
|
expect(deleteResponse.statusCode).toEqual(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -122,25 +111,6 @@ describe("the Drive feature", () => {
|
|||||||
expect(zipResponse.body.length).toBeGreaterThanOrEqual(100);
|
expect(zipResponse.body.length).toBeGreaterThanOrEqual(100);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("did move an item to trash", async () => {
|
|
||||||
const createItemResult = await currentUser.createDefaultDocument();
|
|
||||||
|
|
||||||
expect(createItemResult.id).toBeDefined();
|
|
||||||
|
|
||||||
const moveToTrashResponse = await e2e_deleteDocument(platform, createItemResult.id);
|
|
||||||
expect(moveToTrashResponse.statusCode).toEqual(200);
|
|
||||||
|
|
||||||
const listTrashResponse = await currentUser.getDocument("trash");
|
|
||||||
const listTrashResult = deserialize<DriveItemDetailsMockClass>(
|
|
||||||
DriveItemDetailsMockClass,
|
|
||||||
listTrashResponse.body,
|
|
||||||
);
|
|
||||||
expect(listTrashResult.item.name).toEqual("Trash");
|
|
||||||
expect(createItemResult).toBeDefined();
|
|
||||||
expect(createItemResult.scope).toEqual("shared");
|
|
||||||
expect(listTrashResult.children.some(({ id }) => id === createItemResult.id)).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("did create a version for a drive item", async () => {
|
it("did create a version for a drive item", async () => {
|
||||||
const item = await currentUser.createDefaultDocument();
|
const item = await currentUser.createDefaultDocument();
|
||||||
const fileUploadResponse = await e2e_createDocumentFile(platform);
|
const fileUploadResponse = await e2e_createDocumentFile(platform);
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
import { describe, beforeAll, it, expect, afterAll } from "@jest/globals";
|
||||||
|
import { deserialize } from "class-transformer";
|
||||||
|
import type { DriveFile } from "../../../src/services/documents/entities/drive-file";
|
||||||
|
import { init, TestPlatform } from "../setup";
|
||||||
|
import { TestDbService } from "../utils.prepare.db";
|
||||||
|
import UserApi from "../common/user-api";
|
||||||
|
import {
|
||||||
|
DriveItemDetailsMockClass,
|
||||||
|
} from "../common/entities/mock_entities";
|
||||||
|
|
||||||
|
describe("the Drive's documents' trash feature", () => {
|
||||||
|
let platform: TestPlatform | null;
|
||||||
|
let currentUser: UserApi;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
platform = await init({
|
||||||
|
services: [
|
||||||
|
"webserver",
|
||||||
|
"database",
|
||||||
|
"applications",
|
||||||
|
"search",
|
||||||
|
"storage",
|
||||||
|
"message-queue",
|
||||||
|
"user",
|
||||||
|
"search",
|
||||||
|
"files",
|
||||||
|
"websocket",
|
||||||
|
"messages",
|
||||||
|
"auth",
|
||||||
|
"realtime",
|
||||||
|
"channels",
|
||||||
|
"counter",
|
||||||
|
"statistics",
|
||||||
|
"platform-services",
|
||||||
|
"documents",
|
||||||
|
],
|
||||||
|
});
|
||||||
|
currentUser = await UserApi.getInstance(platform);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await platform?.tearDown();
|
||||||
|
platform = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("did fetch the trash", async () => {
|
||||||
|
await TestDbService.getInstance(platform!, true);
|
||||||
|
|
||||||
|
const response = await currentUser.getDocument("trash");
|
||||||
|
const result = deserialize<DriveItemDetailsMockClass>(DriveItemDetailsMockClass, response.body);
|
||||||
|
|
||||||
|
expect(result.item.id).toEqual("trash");
|
||||||
|
expect(result.item.name).toEqual("Trash");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("did move an item to trash", async () => {
|
||||||
|
const createItemResult = await currentUser.createDefaultDocument();
|
||||||
|
|
||||||
|
expect(createItemResult.id).toBeDefined();
|
||||||
|
|
||||||
|
const moveToTrashResponse = await currentUser.delete(createItemResult.id);
|
||||||
|
expect(moveToTrashResponse.statusCode).toEqual(200);
|
||||||
|
|
||||||
|
const listTrashResponse = await currentUser.getDocument("trash");
|
||||||
|
const listTrashResult = deserialize<DriveItemDetailsMockClass>(
|
||||||
|
DriveItemDetailsMockClass,
|
||||||
|
listTrashResponse.body,
|
||||||
|
);
|
||||||
|
expect(listTrashResult.item.name).toEqual("Trash");
|
||||||
|
expect(createItemResult).toBeDefined();
|
||||||
|
expect(createItemResult.scope).toEqual("shared");
|
||||||
|
expect(listTrashResult.children.some(({ id }) => id === createItemResult.id)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("deleting a file uploaded by an anonymous user should go to the sharers trash", () => {
|
||||||
|
async function getTrashContentIds() {
|
||||||
|
const listTrashResponse = await currentUser.getDocument("trash");
|
||||||
|
expect(listTrashResponse.statusCode).toBe(200);
|
||||||
|
const listTrashResult = deserialize<DriveItemDetailsMockClass>(
|
||||||
|
DriveItemDetailsMockClass,
|
||||||
|
listTrashResponse.body,
|
||||||
|
);
|
||||||
|
return listTrashResult.children.map(({id}) => id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function uploadFileAsAnonymousUser(destinationSharedFolder: DriveFile) {
|
||||||
|
const publicToken = await currentUser.getPublicLinkAccessToken(destinationSharedFolder);
|
||||||
|
expect(publicToken?.value?.length ?? "").toBeGreaterThan(0);
|
||||||
|
return await currentUser.impersonateWithJWT(publicToken.value, () =>
|
||||||
|
currentUser.createDefaultDocument({
|
||||||
|
parent_id: destinationSharedFolder.id,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
it("finds the owner from the immediate parent folder", async () => {
|
||||||
|
const publiclyWriteableFolder = await currentUser.createDirectory();
|
||||||
|
const setPublicWriteableResponse = await currentUser.shareWithPublicLink(publiclyWriteableFolder, "write");
|
||||||
|
expect(setPublicWriteableResponse.statusCode).toBe(200);
|
||||||
|
|
||||||
|
const anonymouslyUploadedDoc = await uploadFileAsAnonymousUser(publiclyWriteableFolder);
|
||||||
|
|
||||||
|
const deletionToTrashResponse = await currentUser.delete(anonymouslyUploadedDoc.id);
|
||||||
|
expect(deletionToTrashResponse.statusCode).toBe(200);
|
||||||
|
|
||||||
|
expect((await getTrashContentIds()).indexOf(anonymouslyUploadedDoc.id)).toBeGreaterThanOrEqual(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -6,18 +6,6 @@ import * as fs from "fs";
|
|||||||
|
|
||||||
const url = "/internal/services/documents/v1";
|
const url = "/internal/services/documents/v1";
|
||||||
|
|
||||||
export const e2e_deleteDocument = async (platform: TestPlatform, id: string | "root" | "trash") => {
|
|
||||||
const token = await platform.auth.getJWTToken();
|
|
||||||
|
|
||||||
return await platform.app.inject({
|
|
||||||
method: "DELETE",
|
|
||||||
url: `${url}/companies/${platform.workspace.company_id}/item/${id}`,
|
|
||||||
headers: {
|
|
||||||
authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const e2e_updateDocument = async (
|
export const e2e_updateDocument = async (
|
||||||
platform: TestPlatform,
|
platform: TestPlatform,
|
||||||
id: string | "root" | "trash",
|
id: string | "root" | "trash",
|
||||||
@@ -35,7 +23,6 @@ export const e2e_updateDocument = async (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const e2e_createVersion = async (
|
export const e2e_createVersion = async (
|
||||||
platform: TestPlatform,
|
platform: TestPlatform,
|
||||||
id: string,
|
id: string,
|
||||||
|
|||||||
Reference in New Issue
Block a user