🚧 back: user deletion wip
This commit is contained in:
committed by
Anton Shepilov
parent
99d78ad5ac
commit
658e8de6d4
+9
-39
@@ -4,45 +4,13 @@ import type { ExecutionContext } from "../../../../platform/framework/api/crud-s
|
|||||||
|
|
||||||
import { adminLogger, buildUserDeletionRepositories } from "../utils";
|
import { adminLogger, buildUserDeletionRepositories } from "../utils";
|
||||||
|
|
||||||
// /**
|
|
||||||
// * Create all repositories required for deleting a user
|
|
||||||
// * @deprecated Do not use this outside of this file, it is exported exclusively for e2e tests
|
|
||||||
// */
|
|
||||||
// export async function buildUserDeletionRepositories(
|
|
||||||
// db: DatabaseServiceAPI,
|
|
||||||
// search: SearchServiceAPI,
|
|
||||||
// ) {
|
|
||||||
// return {
|
|
||||||
// driveFile: await db.getRepository<DriveFile>(DriveFileType, DriveFile),
|
|
||||||
// file: await db.getRepository<File>(FileType, File),
|
|
||||||
// fileVersion: await db.getRepository<FileVersion>(FileVersionType, FileVersion),
|
|
||||||
|
|
||||||
// user: await db.getRepository<User>(UserType, User),
|
|
||||||
// companyUser: await db.getRepository<CompanyUser>(CompanyUserType, CompanyUser),
|
|
||||||
// externalUser: await db.getRepository<ExternalUser>(ExternalUserType, ExternalUser),
|
|
||||||
|
|
||||||
// // company: await db.getRepository<Company>(CompanyType, Company),
|
|
||||||
// // missed_drive_files
|
|
||||||
// // session
|
|
||||||
// // user_online
|
|
||||||
|
|
||||||
// search: {
|
|
||||||
// driveFile: await search.getRepository<DriveFile>(DriveFileType, DriveFile),
|
|
||||||
// user: await search.getRepository<User>(UserType, User),
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
export class AdminDeleteUserController {
|
export class AdminDeleteUserController {
|
||||||
private constructor(
|
private _repos: Awaited<ReturnType<typeof buildUserDeletionRepositories>>;
|
||||||
private readonly repos: Awaited<ReturnType<typeof buildUserDeletionRepositories>>,
|
private async getRepos() {
|
||||||
) {}
|
if (!this._repos)
|
||||||
public static async create() {
|
this._repos = await buildUserDeletionRepositories(gr.database, gr.platformServices.search);
|
||||||
return new AdminDeleteUserController(
|
return this._repos;
|
||||||
await buildUserDeletionRepositories(gr.database, gr.platformServices.search),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
// fisherYattesShuffleInPlace
|
|
||||||
|
|
||||||
/** Begin or forward the deletion process of a user */
|
/** Begin or forward the deletion process of a user */
|
||||||
async deleteUser(userId: string): Promise<"failed" | "deleting" | "done"> {
|
async deleteUser(userId: string): Promise<"failed" | "deleting" | "done"> {
|
||||||
@@ -51,7 +19,7 @@ export class AdminDeleteUserController {
|
|||||||
user: { server_request: true },
|
user: { server_request: true },
|
||||||
company: { id: "// TODO: REPLACE WITH COMPANY ID" },
|
company: { id: "// TODO: REPLACE WITH COMPANY ID" },
|
||||||
} as unknown as ExecutionContext);
|
} as unknown as ExecutionContext);
|
||||||
const existingUser = await this.repos.user.findOne({ id: userId });
|
const existingUser = await (await this.getRepos()).user.findOne({ id: userId });
|
||||||
if (existingUser?.deleted) {
|
if (existingUser?.deleted) {
|
||||||
if (existingUser.delete_process_started_epoch > 0) return "deleting";
|
if (existingUser.delete_process_started_epoch > 0) return "deleting";
|
||||||
}
|
}
|
||||||
@@ -65,7 +33,9 @@ export class AdminDeleteUserController {
|
|||||||
|
|
||||||
/** Get an array of user IDs that are incompletely deleted */
|
/** Get an array of user IDs that are incompletely deleted */
|
||||||
async listUsersPendingDeletion() {
|
async listUsersPendingDeletion() {
|
||||||
return (await this.repos.user.find({}, { $gt: [["delete_process_started_epoch", 0]] }))
|
return (
|
||||||
|
await (await this.getRepos()).user.find({}, { $gt: [["delete_process_started_epoch", 0]] })
|
||||||
|
)
|
||||||
.getEntities()
|
.getEntities()
|
||||||
.map(({ id }) => id);
|
.map(({ id }) => id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,14 +8,13 @@ import {
|
|||||||
descendDriveItemsDepthFirstRandomOrder,
|
descendDriveItemsDepthFirstRandomOrder,
|
||||||
loadRawVersionsOfItemForDeletion,
|
loadRawVersionsOfItemForDeletion,
|
||||||
runInBatchesAreAllTrue,
|
runInBatchesAreAllTrue,
|
||||||
TUserDeletionRepos,
|
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
|
|
||||||
export default class AdminServiceImpl implements AdminServiceAPI {
|
export default class AdminServiceImpl implements AdminServiceAPI {
|
||||||
version: "1";
|
version: "1";
|
||||||
|
|
||||||
private _repos;
|
private _repos;
|
||||||
private get repos(): TUserDeletionRepos {
|
private get repos(): ReturnType<typeof buildUserDeletionRepositories> {
|
||||||
return (this._repos ||= buildUserDeletionRepositories());
|
return (this._repos ||= buildUserDeletionRepositories());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,8 +34,9 @@ export default class AdminServiceImpl implements AdminServiceAPI {
|
|||||||
Promise.all(
|
Promise.all(
|
||||||
paths.map(path => {
|
paths.map(path => {
|
||||||
try {
|
try {
|
||||||
return gr.platformServices.storage.remove(path);
|
return gr.platformServices.storage.remove("x" + path);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
logger.error({ err, path }, "Error deleting storage item");
|
logger.error({ err, path }, "Error deleting storage item");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -71,12 +71,16 @@ export default class AdminServiceImpl implements AdminServiceAPI {
|
|||||||
*/
|
*/
|
||||||
const deleteUserLogger = adminLogger.child({ adminOp: "DeleteUser", user });
|
const deleteUserLogger = adminLogger.child({ adminOp: "DeleteUser", user });
|
||||||
const result = await descendDriveItemsDepthFirstRandomOrder(
|
const result = await descendDriveItemsDepthFirstRandomOrder(
|
||||||
this.repos,
|
await this.repos,
|
||||||
"user_" + user.id,
|
"user_" + user.id,
|
||||||
async (item, children, _parents) => {
|
async (item, children, _parents) => {
|
||||||
let canDeleteItem = true;
|
let canDeleteItem = true;
|
||||||
if (!item.is_directory) {
|
if (!item.is_directory) {
|
||||||
const versionsAssets = await loadRawVersionsOfItemForDeletion(this.repos, item, true);
|
const versionsAssets = await loadRawVersionsOfItemForDeletion(
|
||||||
|
await this.repos,
|
||||||
|
item,
|
||||||
|
true,
|
||||||
|
);
|
||||||
for (const { version, file, paths } of versionsAssets) {
|
for (const { version, file, paths } of versionsAssets) {
|
||||||
if (paths.length > 0 && !(await this.deleteS3Paths(deleteUserLogger, paths))) {
|
if (paths.length > 0 && !(await this.deleteS3Paths(deleteUserLogger, paths))) {
|
||||||
deleteUserLogger.error({ paths }, "Failed to delete paths");
|
deleteUserLogger.error({ paths }, "Failed to delete paths");
|
||||||
@@ -84,7 +88,7 @@ export default class AdminServiceImpl implements AdminServiceAPI {
|
|||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
if (file) {
|
if (file) {
|
||||||
const result = await this.repos.file.remove(file);
|
const result = await (await this.repos).file.remove(file);
|
||||||
if (!result)
|
if (!result)
|
||||||
// No error but nothing deleted, just move on
|
// No error but nothing deleted, just move on
|
||||||
deleteUserLogger.warn({ file, result }, "Failed to delete file");
|
deleteUserLogger.warn({ file, result }, "Failed to delete file");
|
||||||
@@ -96,7 +100,7 @@ export default class AdminServiceImpl implements AdminServiceAPI {
|
|||||||
if (canDeleteItem && version)
|
if (canDeleteItem && version)
|
||||||
try {
|
try {
|
||||||
if (version) {
|
if (version) {
|
||||||
const result = await this.repos.fileVersion.remove(version);
|
const result = await (await this.repos).fileVersion.remove(version);
|
||||||
if (!result)
|
if (!result)
|
||||||
// No error but nothing deleted, just move on
|
// No error but nothing deleted, just move on
|
||||||
deleteUserLogger.warn({ version, result }, "Failed to delete version");
|
deleteUserLogger.warn({ version, result }, "Failed to delete version");
|
||||||
@@ -109,18 +113,18 @@ export default class AdminServiceImpl implements AdminServiceAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (canDeleteItem) {
|
if (canDeleteItem) {
|
||||||
if (children.every(x => !!x)) {
|
if (children === undefined || children.every(x => !!x)) {
|
||||||
if (item.is_directory && children.length == 0)
|
if (item.is_directory && children.length == 0)
|
||||||
deleteUserLogger.warn({ item }, "Deleting empty directory");
|
deleteUserLogger.warn({ item }, "Deleting empty directory");
|
||||||
try {
|
try {
|
||||||
await this.repos.search.driveFile.service.remove([item as never]);
|
await (await this.repos).search.driveFile.service.remove([item as never]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
canDeleteItem = false;
|
canDeleteItem = false;
|
||||||
deleteUserLogger.error({ err, item }, "Error deleting drive item search entry");
|
deleteUserLogger.error({ err, item }, "Error deleting drive item search entry");
|
||||||
}
|
}
|
||||||
if (canDeleteItem)
|
if (canDeleteItem)
|
||||||
try {
|
try {
|
||||||
const result = await this.repos.driveFile.remove(item);
|
const result = await (await this.repos).driveFile.remove(item);
|
||||||
if (!result)
|
if (!result)
|
||||||
// No error but nothing deleted, just move on
|
// No error but nothing deleted, just move on
|
||||||
deleteUserLogger.warn({ item, result }, "Failed to delete drive item");
|
deleteUserLogger.warn({ item, result }, "Failed to delete drive item");
|
||||||
@@ -141,9 +145,9 @@ export default class AdminServiceImpl implements AdminServiceAPI {
|
|||||||
);
|
);
|
||||||
if (!result.every(x => !!x)) return false;
|
if (!result.every(x => !!x)) return false;
|
||||||
//TODO: error checking and such
|
//TODO: error checking and such
|
||||||
await this.repos.search.user.service.remove([user as never]);
|
await (await this.repos).search.user.service.remove([user as never]);
|
||||||
user.delete_process_started_epoch = 0;
|
user.delete_process_started_epoch = 0;
|
||||||
await this.repos.user.save(user);
|
await (await this.repos).user.save(user);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ export class UserServiceImpl {
|
|||||||
const user = await this.get(pk);
|
const user = await this.get(pk);
|
||||||
|
|
||||||
if (context.user.server_request || context.user.id === user.id) {
|
if (context.user.server_request || context.user.id === user.id) {
|
||||||
|
const userCopy = { ...user } as User;
|
||||||
//We keep a part of the user id as new name
|
//We keep a part of the user id as new name
|
||||||
const partialId = user.id.toString().split("-")[0];
|
const partialId = user.id.toString().split("-")[0];
|
||||||
|
|
||||||
@@ -177,6 +178,8 @@ export class UserServiceImpl {
|
|||||||
localEventBus.publish<ResourceEventsPayload>("user:deleted", {
|
localEventBus.publish<ResourceEventsPayload>("user:deleted", {
|
||||||
user: user,
|
user: user,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await gr.platformServices.admin.deleteUser(userCopy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,24 @@
|
|||||||
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from "@jest/globals";
|
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from "@jest/globals";
|
||||||
import { init, TestPlatform } from "../setup";
|
import { init, TestPlatform } from "../setup";
|
||||||
import { TestDbService } from "../utils.prepare.db";
|
import { CompanyLimitsEnum } from "../../../src/services/user/web/types";
|
||||||
import UserApi from "../common/user-api";
|
import UserApi from "../common/user-api";
|
||||||
import type { DriveExecutionContext } from "../../../src/services/documents/types";
|
import type { DriveExecutionContext } from "../../../src/services/documents/types";
|
||||||
import { DriveFile, TYPE as DriveFileType } from "../../../src/services/documents/entities/drive-file";
|
import { DriveFile, TYPE as DriveFileType } from "../../../src/services/documents/entities/drive-file";
|
||||||
import { File } from "../../../src/services/files/entities/file";
|
import { File } from "../../../src/services/files/entities/file";
|
||||||
import { FileVersion, TYPE as FileVersionType } from "../../../src/services/documents/entities/file-version";
|
import { FileVersion, TYPE as FileVersionType } from "../../../src/services/documents/entities/file-version";
|
||||||
import User, { TYPE as UserType } from "../../../src/services/user/entities/user";
|
import User, { TYPE as UserType } from "../../../src/services/user/entities/user";
|
||||||
|
import ExternalUser, { TYPE as ExternalUserType } from "../../../src/services/user/entities/external_user";
|
||||||
|
import { getThumbnailRoute } from "../../../src/services/files/web/routes";
|
||||||
import { getFilePath } from "../../../src/services/files/services";
|
import { getFilePath } from "../../../src/services/files/services";
|
||||||
import { buildUserDeletionRepositories } from "../../../src/core/platform/services/admin/controller/delete-user-controller";
|
const FileType = "files";
|
||||||
|
import { buildUserDeletionRepositories, descendDriveItemsDepthFirstRandomOrder } from "../../../src/core/platform/services/admin/utils";
|
||||||
import { getConfig } from "../../../src/core/platform/framework/api/admin";
|
import { getConfig } from "../../../src/core/platform/framework/api/admin";
|
||||||
|
|
||||||
const loadRepositories = async (platform: TestPlatform) => buildUserDeletionRepositories(platform.database, platform.search);
|
const loadRepositories = async (platform: TestPlatform) => buildUserDeletionRepositories(platform!.database, platform!.search);
|
||||||
|
|
||||||
describe("The users deletion API", () => {
|
describe("The users deletion API", () => {
|
||||||
const url = "/internal/services/users/v1";
|
const url = "/internal/services/users/v1";
|
||||||
let platform: TestPlatform;
|
let platform: TestPlatform | undefined;
|
||||||
let currentUser: UserApi;
|
let currentUser: UserApi;
|
||||||
let myUserId: string;
|
let myUserId: string;
|
||||||
let myDriveId: string;
|
let myDriveId: string;
|
||||||
@@ -23,6 +26,18 @@ describe("The users deletion API", () => {
|
|||||||
let repos: Awaited<ReturnType<typeof loadRepositories>>;
|
let repos: Awaited<ReturnType<typeof loadRepositories>>;
|
||||||
const adminConfig = getConfig();
|
const adminConfig = getConfig();
|
||||||
|
|
||||||
|
|
||||||
|
const listByUserIn = {
|
||||||
|
driveFileByCreator: async (userId: string) => repos.driveFile.find({ creator: userId }),
|
||||||
|
driveFileByParent: async (userId: string) => repos.driveFile.find({ parent_id: "user_" + userId }),
|
||||||
|
fileVersion: async (userId: string) => repos.fileVersion.find({ creator_id: userId }),
|
||||||
|
// file: async (userId: string) => repos.file.find({ user_id: userId }), // user_id is encrypted for reasons
|
||||||
|
missedDriveFile: async (userId: string) => repos.missedDriveFile.find({ creator: userId }),
|
||||||
|
// user: async (userId: string) => repos.user.find({ id: userId }), // ignored because it stays with deleted=true
|
||||||
|
companyUser: async (userId: string) => repos.companyUser.find({ user_id: userId }),
|
||||||
|
externalUser: async (userId: string) => repos.externalUser.find({ user_id: userId }),
|
||||||
|
};
|
||||||
|
|
||||||
interface UserTreeEntry {
|
interface UserTreeEntry {
|
||||||
driveFile: DriveFile;
|
driveFile: DriveFile;
|
||||||
driveFileFromSearch?: DriveFile;
|
driveFileFromSearch?: DriveFile;
|
||||||
@@ -36,7 +51,7 @@ describe("The users deletion API", () => {
|
|||||||
|
|
||||||
async function hydrateDriveFile(context: UserTreeEntry) {
|
async function hydrateDriveFile(context: UserTreeEntry) {
|
||||||
const { driveFile } = context;
|
const { driveFile } = context;
|
||||||
context.driveFileFromSearch = (await repos.search.driveFile.search({}, { $text: { $search: driveFile.name } }, { user: { id: myUserId }, company: { id: myCompanyId } } as DriveExecutionContext)).getEntities()[0];
|
context.driveFileFromSearch = (await repos.search.driveFile.search({}, { $text: { $search: "d" } })).getEntities()[0];
|
||||||
const versions = (await repos.fileVersion.find({ drive_item_id: driveFile.id })).getEntities();
|
const versions = (await repos.fileVersion.find({ drive_item_id: driveFile.id })).getEntities();
|
||||||
|
|
||||||
context.versions = await Promise.all(versions.map(async version => {
|
context.versions = await Promise.all(versions.map(async version => {
|
||||||
@@ -44,7 +59,7 @@ describe("The users deletion API", () => {
|
|||||||
const fileStoragePath = file && getFilePath(file);
|
const fileStoragePath = file && getFilePath(file);
|
||||||
return {
|
return {
|
||||||
version, file,
|
version, file,
|
||||||
storagePaths: fileStoragePath ? (await platform.storage.getConnector().enumeratePathsForFile(fileStoragePath)) : undefined,
|
storagePaths: fileStoragePath ? (await platform!.storage.getConnector().enumeratePathsForFile(fileStoragePath)) : undefined,
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -139,12 +154,12 @@ describe("The users deletion API", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
await platform.tearDown();
|
await platform!.tearDown();
|
||||||
platform = null;
|
platform = undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
async function sendDeleteUser(secret: string, userId: string, expected: number = 200) {
|
async function sendDeleteUser(secret: string, userId: string, expected: number = 200) {
|
||||||
const response = await platform.app.inject({
|
const response = await platform!.app.inject({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: `/admin/user/delete`,
|
url: `/admin/user/delete`,
|
||||||
body: { secret, userId },
|
body: { secret, userId },
|
||||||
@@ -155,7 +170,7 @@ describe("The users deletion API", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function requestPendingUserDeletions(secret: string, expected: number = 200) {
|
async function requestPendingUserDeletions(secret: string, expected: number = 200) {
|
||||||
const response = await platform.app.inject({
|
const response = await platform!.app.inject({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: `/admin/user/delete/pending`,
|
url: `/admin/user/delete/pending`,
|
||||||
body: { secret },
|
body: { secret },
|
||||||
@@ -188,17 +203,40 @@ describe("The users deletion API", () => {
|
|||||||
|
|
||||||
// const docs = await currentUser.browseDocuments(myDriveId, {});
|
// const docs = await currentUser.browseDocuments(myDriveId, {});
|
||||||
// console.error(JSON.stringify(docs, null, 2)); // works, but projected values
|
// console.error(JSON.stringify(docs, null, 2)); // works, but projected values
|
||||||
// const docs = await platform.documentService.browse(myDriveId, { }, { user: { id: myUserId }, company: { id: "string" } } as DriveExecutionContext);
|
// const docs = await platform!.documentService.browse(myDriveId, { }, { user: { id: myUserId }, company: { id: "string" } } as DriveExecutionContext);
|
||||||
// console.error(JSON.stringifya(docs, null, 2)); // doesn't work, empty children
|
// console.error(JSON.stringifya(docs, null, 2)); // doesn't work, empty children
|
||||||
|
|
||||||
// Wait for indexing, inspired by tdrive/backend/node/test/e2e/documents/documents-search.spec.ts
|
// await currentUser.uploadAllFilesOneByOne("user_" + currentUser.user.id);
|
||||||
await new Promise(resolve => setTimeout(resolve, 5000)); // TODO: search doesn't work anyway
|
|
||||||
|
|
||||||
|
// Wait for indexing, inspired by tdrive/backend/node/test/e2e/documents/documents-search.spec.ts
|
||||||
|
// await new Promise(resolve => setTimeout(resolve, 10000)); // TODO: search doesn't work anyway
|
||||||
|
// const searchResult = await currentUser.searchDocument({search: "d"});
|
||||||
|
// console.log({searchResult});
|
||||||
|
// const a = (await repos.search.driveFile.search({}, { $text: { $search: "d" } })).getEntities()[0];
|
||||||
|
// console.log({a});
|
||||||
const tree = await loadDriveFiles(myDriveId);
|
const tree = await loadDriveFiles(myDriveId);
|
||||||
console.log((await userTreeToString(tree)));
|
console.log((await userTreeToString(tree)));
|
||||||
|
|
||||||
|
const lines: string[] = [];
|
||||||
|
const res = await descendDriveItemsDepthFirstRandomOrder(repos, myDriveId, async (item, children, parents) => {
|
||||||
|
//TODO: NONONO:
|
||||||
|
// recurse each drive item
|
||||||
|
// delete all s3
|
||||||
|
// then the version
|
||||||
|
// then files
|
||||||
|
// search for file by user id after deletion
|
||||||
|
// search s3 for prefix company/userid
|
||||||
|
//
|
||||||
|
const indent = new Array(parents.length + 1).join("\t");
|
||||||
|
lines.push(`${indent} ${item.is_directory ? "📂" : "📄"} ${item.name} (${item.id}) (${children?.length ?? 0} children)`);
|
||||||
|
return children;
|
||||||
|
});
|
||||||
|
console.error(lines.join('\n'));
|
||||||
|
console.error(JSON.stringify(res, null, 2));
|
||||||
|
|
||||||
|
|
||||||
console.log((await repos.search.driveFile.search({}, { $text: { $search: "" } }, undefined)).getEntities());
|
console.log((await repos.search.driveFile.search({}, { $text: { $search: "" } }, undefined)).getEntities());
|
||||||
console.log((await repos.search.user.search({}, { $text: { $search: "" } }, undefined)).getEntities());
|
// console.log((await repos.search.user.search({}, { $text: { $search: "" } }, undefined)).getEntities());
|
||||||
|
|
||||||
const foundEntries = findEntriesInUserTree(tree);
|
const foundEntries = findEntriesInUserTree(tree);
|
||||||
expect(foundEntries.rootFolder?.driveFile).toMatchObject({ id: initialFakeFiles.rootFolder.id, parent_id: myDriveId, name: "root_folder" });
|
expect(foundEntries.rootFolder?.driveFile).toMatchObject({ id: initialFakeFiles.rootFolder.id, parent_id: myDriveId, name: "root_folder" });
|
||||||
@@ -223,6 +261,7 @@ describe("The users deletion API", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should be immediately listed after deletion", async () => {
|
it("should be immediately listed after deletion", async () => {
|
||||||
|
console.log({myCompanyId, myUserId});
|
||||||
await sendDeleteUser(adminConfig.endpointSecret!, myUserId, 200);
|
await sendDeleteUser(adminConfig.endpointSecret!, myUserId, 200);
|
||||||
// TODO: wait for message queue stuff ?
|
// TODO: wait for message queue stuff ?
|
||||||
const responseBody = await requestPendingUserDeletions(adminConfig.endpointSecret!, 200);
|
const responseBody = await requestPendingUserDeletions(adminConfig.endpointSecret!, 200);
|
||||||
@@ -238,9 +277,37 @@ describe("The users deletion API", () => {
|
|||||||
expect((user as unknown as User).delete_process_started_epoch).toBeGreaterThan(0);
|
expect((user as unknown as User).delete_process_started_epoch).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("the user should not be able to login", async () => {
|
// TODO: NONONO
|
||||||
|
it.skip("the user should not be able to login", async () => {
|
||||||
expect((await currentUser.login()).statusCode).toBe(401);
|
expect((await currentUser.login()).statusCode).toBe(401);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//TODO: NONONONO
|
||||||
|
it.skip("should have no objects left other than user (and untestable files)", async () => {
|
||||||
|
let totalCount = 0;
|
||||||
|
for (const [key, fn] of Object.entries(listByUserIn)) {
|
||||||
|
const result = (await fn(myUserId)).getEntities();
|
||||||
|
totalCount += result.length;
|
||||||
|
if (result.length)
|
||||||
|
console.error(`unexpected ${key} still there`, result);
|
||||||
|
}
|
||||||
|
expect(totalCount).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
//TODO: NONONONO
|
||||||
|
it.skip("test todo wip messy thing", async () => {
|
||||||
|
const tree = await loadDriveFiles(myDriveId);
|
||||||
|
console.log(await userTreeToString(tree));
|
||||||
|
const foundEntries = findEntriesInUserTree(tree);
|
||||||
|
const allPaths = foundEntries.fileAtRoot?.versions?.flatMap(({storagePaths}) => storagePaths);
|
||||||
|
console.error({allPaths})
|
||||||
|
});
|
||||||
|
|
||||||
|
it("in the end the user should have nothing, and be deleted with no process epoch", async () => {
|
||||||
|
const tree = await loadDriveFiles(myDriveId);
|
||||||
|
return; //TODO: NONONO Don't check user deletion until it's done etc
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user