mirror of
https://github.com/kmc7468/arkvault.git
synced 2026-11-04 08:10:28 +09:00
파일 시스템 및 업로드 관련 비즈니스 규칙과 트랜잭션 경계를 Repository에서 서비스로 이동하고 IntegrityError를 제거하며 즐겨찾기 API를 멱등하게 변경
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
73b2d29b2c
commit
75e8ed6c77
18 files changed
+869
-933
No files matched your search
@@ -1,6 +1,6 @@
|
||||
import { access } from "fs/promises";
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { UploadRepo } from "#lib/server/db/index.ts";
|
||||
import { UploadRepo, db } from "#lib/server/db/index.ts";
|
||||
import { processPendingFileDeletions } from "#lib/server/services/file-deletion.ts";
|
||||
import { DirectoryService, FileService } from "#lib/server/services/index.ts";
|
||||
import {
|
||||
@@ -58,7 +58,7 @@ describe("file deletion", () => {
|
||||
await createFile(userId, grandchildId, paths[1]!),
|
||||
];
|
||||
const otherFileId = await createFile(userId, otherId, paths[2]!);
|
||||
await UploadRepo.createFileUploadSession({
|
||||
await UploadRepo.createFileUploadSession(db, {
|
||||
...newEntryParams(userId, childId),
|
||||
id: crypto.randomUUID(),
|
||||
path: paths[3]!,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { CategoryRepo, DirectoryRepo, UploadRepo, IntegrityError } from "#lib/server/db/index.ts";
|
||||
import { CategoryService, DirectoryService, UploadService } from "#lib/server/services/index.ts";
|
||||
import {
|
||||
createCategory,
|
||||
createDirectory,
|
||||
@@ -16,10 +16,10 @@ describe("parent ownership", () => {
|
||||
const { userId } = await createUser();
|
||||
const parentId = await createDirectory(userId);
|
||||
|
||||
await DirectoryRepo.registerDirectory(newEntryParams(userId, parentId));
|
||||
await DirectoryService.createDirectory(newEntryParams(userId, parentId));
|
||||
|
||||
const directories = await DirectoryRepo.getAllDirectoriesByParent(userId, parentId);
|
||||
expect(directories).toHaveLength(1);
|
||||
const { subDirectories } = await DirectoryService.getDirectory(userId, parentId);
|
||||
expect(subDirectories).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("rejects creating a directory under another user's directory", async () => {
|
||||
@@ -28,8 +28,8 @@ describe("parent ownership", () => {
|
||||
const victimDirectoryId = await createDirectory(victimId);
|
||||
|
||||
await expect(
|
||||
DirectoryRepo.registerDirectory(newEntryParams(attackerId, victimDirectoryId)),
|
||||
).rejects.toThrow(new IntegrityError("Directory not found"));
|
||||
DirectoryService.createDirectory(newEntryParams(attackerId, victimDirectoryId)),
|
||||
).rejects.toMatchObject({ code: "NOT_FOUND", message: "Directory not found" });
|
||||
});
|
||||
|
||||
it("rejects creating a category under another user's category", async () => {
|
||||
@@ -38,8 +38,8 @@ describe("parent ownership", () => {
|
||||
const victimCategoryId = await createCategory(victimId);
|
||||
|
||||
await expect(
|
||||
CategoryRepo.registerCategory(newEntryParams(attackerId, victimCategoryId)),
|
||||
).rejects.toThrow(new IntegrityError("Category not found"));
|
||||
CategoryService.createCategory(newEntryParams(attackerId, victimCategoryId)),
|
||||
).rejects.toMatchObject({ code: "NOT_FOUND", message: "Category not found" });
|
||||
});
|
||||
|
||||
it("rejects uploading a file into another user's directory", async () => {
|
||||
@@ -48,17 +48,14 @@ describe("parent ownership", () => {
|
||||
const victimDirectoryId = await createDirectory(victimId);
|
||||
|
||||
await expect(
|
||||
UploadRepo.createFileUploadSession({
|
||||
UploadService.startFileUpload({
|
||||
...newEntryParams(attackerId, victimDirectoryId),
|
||||
id: crypto.randomUUID(),
|
||||
path: "unused",
|
||||
totalChunks: 1,
|
||||
expiresAt: new Date(Date.now() + 60_000),
|
||||
hskVersion: null,
|
||||
contentType: "image/png",
|
||||
encCreatedAt: null,
|
||||
encLastModifiedAt: ciphertext(),
|
||||
}),
|
||||
).rejects.toThrow(new IntegrityError("Directory not found"));
|
||||
).rejects.toMatchObject({ code: "NOT_FOUND", message: "Directory not found" });
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createHash } from "crypto";
|
||||
import { Readable } from "stream";
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { UploadRepo } from "#lib/server/db/index.ts";
|
||||
import { UploadRepo, db } from "#lib/server/db/index.ts";
|
||||
import { FileService, UploadService } from "#lib/server/services/index.ts";
|
||||
import { isChunkUploaded } from "#lib/server/services/upload.ts";
|
||||
import { ciphertext, createUser, newEntryParams, resetDatabase } from "../helpers/database.ts";
|
||||
@@ -12,7 +12,7 @@ describe("upload session bitmap", () => {
|
||||
it("matches the bit order of PostgreSQL's set_bit", async () => {
|
||||
const { userId } = await createUser();
|
||||
const id = crypto.randomUUID();
|
||||
await UploadRepo.createFileUploadSession({
|
||||
await UploadRepo.createFileUploadSession(db, {
|
||||
...newEntryParams(userId, "root"),
|
||||
id,
|
||||
path: "unused",
|
||||
@@ -26,10 +26,10 @@ describe("upload session bitmap", () => {
|
||||
|
||||
const uploaded = [1, 8, 9, 20];
|
||||
for (const chunkIndex of uploaded) {
|
||||
await UploadRepo.markChunkAsUploaded(id, chunkIndex);
|
||||
await UploadRepo.markChunkAsUploaded(db, id, chunkIndex);
|
||||
}
|
||||
|
||||
const session = await UploadRepo.getUploadSession(id, userId);
|
||||
const session = await UploadRepo.getUploadSession(db, id, userId);
|
||||
expect(session?.uploadedChunks).toBe(uploaded.length);
|
||||
for (let chunkIndex = 1; chunkIndex <= 20; chunkIndex++) {
|
||||
expect(isChunkUploaded(session!.bitmap, chunkIndex)).toBe(uploaded.includes(chunkIndex));
|
||||
@@ -80,7 +80,7 @@ describe("upload completion", () => {
|
||||
await expect(
|
||||
UploadService.completeFileUpload(userId, uploadId, null, "invalid"),
|
||||
).rejects.toMatchObject({ code: "CONFLICT" });
|
||||
expect(await UploadRepo.getUploadSession(uploadId, userId)).toBeNull();
|
||||
expect(await UploadRepo.getUploadSession(db, uploadId, userId)).toBeNull();
|
||||
});
|
||||
|
||||
it("rejects chunks of invalid sizes", async () => {
|
||||
|
||||
Reference in new issue
Block a user