Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion apps/web/__tests__/unit/multipart-presign.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -661,8 +661,14 @@ describe("raw recording completion integrity", () => {
]),
),
);
mocks.database.mockReturnValue({
const client = {
update: () => ({ set: () => ({ where: async () => {} }) }),
delete: () => ({ where: vi.fn(async () => {}) }),
};
mocks.database.mockReturnValue({
...client,
transaction: async (run: (tx: typeof client) => Promise<void>) =>
run(client),
});
mocks.complete.mockReturnValue(Effect.succeed({ ETag: etag }));
mocks.head.mockReturnValue(
Expand All @@ -674,6 +680,9 @@ describe("raw recording completion integrity", () => {
provider: "s3",
multipart: { complete: mocks.complete },
headObject: mocks.head,
copyObject: vi.fn(() =>
Effect.succeed({ CopyObjectResult: { ETag: etag } }),
),
},
]),
);
Expand All @@ -689,6 +698,39 @@ describe("raw recording completion integrity", () => {
expect(mocks.head).toHaveBeenCalledWith("owner/video/raw-upload.webm");
});

const completeLegacy = (submittedParts = parts) =>
app.request("/complete", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
fileKey: "owner/video/raw-upload.webm",
uploadId: "same-upload",
durationInSecs: 1,
parts: submittedParts,
}),
});

it("starts processing raw recordings completed with a legacy file key", async () => {
const response = await completeLegacy();

expect(response.status).toBe(200);
expect(await response.json()).toMatchObject({
success: true,
processingStarted: true,
});
const { startVideoProcessingWorkflow } = await import(
"@/lib/video-processing"
);
expect(startVideoProcessingWorkflow).toHaveBeenCalledWith(
expect.objectContaining({ rawFileKey: "owner/video/raw-upload.webm" }),
);
});

it("rejects incomplete legacy raw uploads before completing storage", async () => {
expect((await completeLegacy([secondPart])).status).toBe(400);
expect(mocks.complete).not.toHaveBeenCalled();
});

it("reconciles a lost completion response using all part identities and object size", async () => {
mocks.complete.mockReturnValue(Effect.fail(new Error("NoSuchUpload")));
const response = await complete([...parts].reverse());
Expand Down
9 changes: 8 additions & 1 deletion apps/web/__tests__/unit/multipart-upload-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,16 @@ describe("multipart upload utils", () => {
getSubpath({
fileKey: "legacy-owner/video-456/raw-upload.webm",
}),
).toBeUndefined();
).toBe("raw-upload.webm");
});

it.each(["result.mp4", "raw-upload.mp4", "segments/video/0.m4s"])(
"preserves the legacy subpath %s",
(subpath) => {
expect(getSubpath({ fileKey: `owner/video/${subpath}` })).toBe(subpath);
},
);

it("detects raw recorder uploads", () => {
expect(isRawRecorderUpload("raw-upload.webm")).toBe(true);
expect(isRawRecorderUpload("raw-upload.mp4")).toBe(true);
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/api/upload/[...route]/multipart-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ function assertWritableFileKey(key: string) {

export const getSubpath = (input: { subpath?: string; fileKey?: string }) => {
if ("fileKey" in input) {
return undefined;
return input.fileKey?.split("/").slice(2).join("/");
}

return input.subpath ?? "result.mp4";
Expand Down
Loading