| 
									
										
										
										
											2025-06-03 19:08:50 +03:00
										 |  |  | import { Application } from "express"; | 
					
						
							| 
									
										
										
										
											2025-06-03 19:16:59 +03:00
										 |  |  | import { beforeAll, beforeEach, describe, expect, it } from "vitest"; | 
					
						
							| 
									
										
										
										
											2025-06-03 19:08:50 +03:00
										 |  |  | import supertest from "supertest"; | 
					
						
							|  |  |  | import { login } from "./utils.js"; | 
					
						
							|  |  |  | import config from "../../src/services/config.js"; | 
					
						
							|  |  |  | import { randomInt } from "crypto"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | let app: Application; | 
					
						
							|  |  |  | let token: string; | 
					
						
							|  |  |  | let createdNoteId: string; | 
					
						
							|  |  |  | let createdBranchId: string; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const USER = "etapi"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-03 19:16:59 +03:00
										 |  |  | type EntityType = "attachments" | "attributes" | "branches" | "notes"; | 
					
						
							| 
									
										
										
										
											2025-06-03 19:11:27 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | describe("etapi/delete-entities", () => { | 
					
						
							| 
									
										
										
										
											2025-06-03 19:08:50 +03:00
										 |  |  |     beforeAll(async () => { | 
					
						
							|  |  |  |         config.General.noAuthentication = false; | 
					
						
							|  |  |  |         const buildApp = (await (import("../../src/app.js"))).default; | 
					
						
							|  |  |  |         app = await buildApp(); | 
					
						
							|  |  |  |         token = await login(app); | 
					
						
							| 
									
										
										
										
											2025-06-03 19:16:59 +03:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2025-06-03 19:08:50 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-03 19:16:59 +03:00
										 |  |  |     beforeEach(async () => { | 
					
						
							| 
									
										
										
										
											2025-06-03 19:08:50 +03:00
										 |  |  |         ({ createdNoteId, createdBranchId } = await createNote()); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-03 19:11:27 +03:00
										 |  |  |     it("deletes attachment", async () => { | 
					
						
							| 
									
										
										
										
											2025-06-03 19:08:50 +03:00
										 |  |  |         const attachmentId = await createAttachment(); | 
					
						
							| 
									
										
										
										
											2025-06-03 19:23:11 +03:00
										 |  |  |         await deleteEntity("attachments", attachmentId); | 
					
						
							|  |  |  |         await expectNotFound("attachments", attachmentId); | 
					
						
							| 
									
										
										
										
											2025-06-03 19:11:27 +03:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2025-06-03 19:08:50 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-03 19:11:27 +03:00
										 |  |  |     it("deletes attribute", async () => { | 
					
						
							|  |  |  |         const attributeId = await createAttribute(); | 
					
						
							| 
									
										
										
										
											2025-06-03 19:23:11 +03:00
										 |  |  |         await deleteEntity("attributes", attributeId); | 
					
						
							|  |  |  |         await expectNotFound("attributes", attributeId); | 
					
						
							| 
									
										
										
										
											2025-06-03 19:08:50 +03:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2025-06-03 19:16:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it("deletes cloned branch", async () => { | 
					
						
							| 
									
										
										
										
											2025-06-03 19:24:23 +03:00
										 |  |  |         const clonedBranchId = await createClone(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-03 19:23:11 +03:00
										 |  |  |         await expectFound("branches", createdBranchId); | 
					
						
							|  |  |  |         await expectFound("branches", clonedBranchId); | 
					
						
							| 
									
										
										
										
											2025-06-03 19:16:59 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-03 19:23:11 +03:00
										 |  |  |         await deleteEntity("branches", createdBranchId); | 
					
						
							|  |  |  |         await expectNotFound("branches", createdBranchId); | 
					
						
							| 
									
										
										
										
											2025-06-03 19:16:59 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-03 19:23:11 +03:00
										 |  |  |         await expectFound("branches", clonedBranchId); | 
					
						
							|  |  |  |         await expectFound("notes", createdNoteId); | 
					
						
							| 
									
										
										
										
											2025-06-03 19:16:59 +03:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2025-06-03 19:21:02 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it("deletes note with all branches", async () => { | 
					
						
							|  |  |  |         const attributeId = await createAttribute(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-03 19:24:23 +03:00
										 |  |  |         const clonedBranchId = await createClone(); | 
					
						
							| 
									
										
										
										
											2025-06-03 19:21:02 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-03 19:23:11 +03:00
										 |  |  |         await expectFound("notes", createdNoteId); | 
					
						
							|  |  |  |         await expectFound("branches", createdBranchId); | 
					
						
							|  |  |  |         await expectFound("branches", clonedBranchId); | 
					
						
							|  |  |  |         await expectFound("attributes", attributeId); | 
					
						
							|  |  |  |         await deleteEntity("notes", createdNoteId); | 
					
						
							| 
									
										
										
										
											2025-06-03 19:21:02 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-03 19:23:11 +03:00
										 |  |  |         await expectNotFound("branches", createdBranchId); | 
					
						
							|  |  |  |         await expectNotFound("branches", clonedBranchId); | 
					
						
							|  |  |  |         await expectNotFound("notes", createdNoteId); | 
					
						
							|  |  |  |         await expectNotFound("attributes", attributeId); | 
					
						
							| 
									
										
										
										
											2025-06-03 19:21:02 +03:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2025-06-03 19:08:50 +03:00
										 |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function createNote() { | 
					
						
							|  |  |  |     const noteId = `forcedId${randomInt(1000)}`; | 
					
						
							|  |  |  |     const response = await supertest(app) | 
					
						
							|  |  |  |         .post("/etapi/create-note") | 
					
						
							|  |  |  |         .auth(USER, token, { "type": "basic"}) | 
					
						
							|  |  |  |         .send({ | 
					
						
							|  |  |  |             "noteId": noteId, | 
					
						
							|  |  |  |             "parentNoteId": "root", | 
					
						
							|  |  |  |             "title": "Hello", | 
					
						
							|  |  |  |             "type": "text", | 
					
						
							|  |  |  |             "content": "Hi there!", | 
					
						
							|  |  |  |             "dateCreated": "2023-08-21 23:38:51.123+0200", | 
					
						
							|  |  |  |             "utcDateCreated": "2023-08-21 23:38:51.123Z" | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         .expect(201); | 
					
						
							|  |  |  |     expect(response.body.note.noteId).toStrictEqual(noteId); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |         createdNoteId: response.body.note.noteId, | 
					
						
							|  |  |  |         createdBranchId: response.body.branch.branchId | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function createClone() { | 
					
						
							|  |  |  |     const response = await supertest(app) | 
					
						
							|  |  |  |         .post("/etapi/branches") | 
					
						
							|  |  |  |         .auth(USER, token, { "type": "basic"}) | 
					
						
							|  |  |  |         .send({ | 
					
						
							|  |  |  |             noteId: createdNoteId, | 
					
						
							|  |  |  |             parentNoteId: "_hidden" | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         .expect(201); | 
					
						
							|  |  |  |     expect(response.body.parentNoteId).toStrictEqual("_hidden"); | 
					
						
							|  |  |  |     return response.body.branchId; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function createAttribute() { | 
					
						
							|  |  |  |     const attributeId = `forcedId${randomInt(1000)}`; | 
					
						
							|  |  |  |     const response = await supertest(app) | 
					
						
							|  |  |  |         .post("/etapi/attributes") | 
					
						
							|  |  |  |         .auth(USER, token, { "type": "basic"}) | 
					
						
							|  |  |  |         .send({ | 
					
						
							|  |  |  |             "attributeId": attributeId, | 
					
						
							|  |  |  |             "noteId": createdNoteId, | 
					
						
							|  |  |  |             "type": "label", | 
					
						
							|  |  |  |             "name": "mylabel", | 
					
						
							|  |  |  |             "value": "val", | 
					
						
							|  |  |  |             "isInheritable": true | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         .expect(201); | 
					
						
							|  |  |  |     expect(response.body.attributeId).toStrictEqual(attributeId); | 
					
						
							|  |  |  |     return response.body.attributeId; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function createAttachment() { | 
					
						
							|  |  |  |     const response = await supertest(app) | 
					
						
							|  |  |  |         .post("/etapi/attachments") | 
					
						
							|  |  |  |         .auth(USER, token, { "type": "basic"}) | 
					
						
							|  |  |  |         .send({ | 
					
						
							|  |  |  |             "ownerId": createdNoteId, | 
					
						
							|  |  |  |             "role": "file", | 
					
						
							|  |  |  |             "mime": "plain/text", | 
					
						
							|  |  |  |             "title": "my attachment", | 
					
						
							|  |  |  |             "content": "my text" | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         .expect(201); | 
					
						
							|  |  |  |     return response.body.attachmentId; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-03 19:11:27 +03:00
										 |  |  | async function deleteEntity(entity: EntityType, id: string) { | 
					
						
							| 
									
										
										
										
											2025-06-03 19:08:50 +03:00
										 |  |  |     // Delete twice to test idempotency.
 | 
					
						
							|  |  |  |     for (let i=0; i < 2; i++) { | 
					
						
							|  |  |  |         await supertest(app) | 
					
						
							|  |  |  |             .delete(`/etapi/${entity}/${id}`) | 
					
						
							|  |  |  |             .auth(USER, token, { "type": "basic"}) | 
					
						
							|  |  |  |             .expect(204); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-06-03 19:11:27 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-03 19:23:11 +03:00
										 |  |  | const MISSING_ENTITY_ERROR_CODES: Record<EntityType, string> = { | 
					
						
							|  |  |  |     attachments: "ATTACHMENT_NOT_FOUND", | 
					
						
							|  |  |  |     attributes: "ATTRIBUTE_NOT_FOUND", | 
					
						
							|  |  |  |     branches: "BRANCH_NOT_FOUND", | 
					
						
							|  |  |  |     notes: "NOTE_NOT_FOUND" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-03 19:11:27 +03:00
										 |  |  | async function expectNotFound(entity: EntityType, id: string) { | 
					
						
							|  |  |  |     const response = await supertest(app) | 
					
						
							|  |  |  |             .get(`/etapi/${entity}/${id}`) | 
					
						
							|  |  |  |             .auth(USER, token, { "type": "basic"}) | 
					
						
							|  |  |  |             .expect(404); | 
					
						
							| 
									
										
										
										
											2025-06-03 19:23:11 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     expect(response.body.code).toStrictEqual(MISSING_ENTITY_ERROR_CODES[entity]); | 
					
						
							| 
									
										
										
										
											2025-06-03 19:11:27 +03:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2025-06-03 19:16:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | async function expectFound(entity: EntityType, id: string) { | 
					
						
							|  |  |  |     await supertest(app) | 
					
						
							|  |  |  |             .get(`/etapi/${entity}/${id}`) | 
					
						
							|  |  |  |             .auth(USER, token, { "type": "basic"}) | 
					
						
							|  |  |  |             .expect(200); | 
					
						
							|  |  |  | } |