mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 09:19:51 +00:00
Addresses senior-review findings on the media-send feature: - Storage lifecycle: uploads are GC'd on the abandonment paths (discarded draft, failed/errored send, unmount with a staged draft, and replacing one draft with another) via a new deleteAccountMedia helper. Previously every cancel/failure orphaned a public object. - Per-kind size caps (image 5MB, video/audio/document 16MB) mirror Meta's limits and are enforced BEFORE upload, so a too-large image no longer uploads then fails at the Meta call. - Voice notes auto-stop at a 5-min cap and always render as a voice note (transcode anything that isn't already OGG/Opus — Safari MP4 included), not just Chromium WebM. - Send route rejects unknown message_type and captions over Meta's 1024 cap up front; transcode route guards its output size too. - DraftPreview hoisted to a stable module-scope component so the caption input no longer remounts (and drops focus) on each keystroke. Tests: per-kind cap assertions added (418 total). typecheck/lint/build green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
47 lines
1.8 KiB
TypeScript
47 lines
1.8 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { buildMediaPath, MEDIA_MAX_BYTES_BY_KIND } from "./upload-media";
|
|
|
|
const ACCOUNT = "11111111-2222-3333-4444-555555555555";
|
|
|
|
describe("buildMediaPath", () => {
|
|
it("namespaces under account-<id> so RLS write policies match", () => {
|
|
const path = buildMediaPath(ACCOUNT, "photo.png", 1700000000000);
|
|
expect(path).toBe(`account-${ACCOUNT}/1700000000000-photo.png`);
|
|
expect(path.split("/")[0]).toBe(`account-${ACCOUNT}`);
|
|
});
|
|
|
|
it("lower-cases the extension and sanitizes the basename", () => {
|
|
const path = buildMediaPath(ACCOUNT, "My Invoice (final).PDF", 1700000000000);
|
|
expect(path).toBe(`account-${ACCOUNT}/1700000000000-My_Invoice_final_.pdf`);
|
|
});
|
|
|
|
it("caps the basename at 40 chars", () => {
|
|
const long = "a".repeat(100) + ".png";
|
|
const path = buildMediaPath(ACCOUNT, long, 1700000000000);
|
|
const base = path.split("/")[1].replace("1700000000000-", "").replace(".png", "");
|
|
expect(base.length).toBe(40);
|
|
});
|
|
|
|
it("falls back to 'file' / 'bin' for a nameless input", () => {
|
|
const path = buildMediaPath(ACCOUNT, "", 1700000000000);
|
|
expect(path).toBe(`account-${ACCOUNT}/1700000000000-file.bin`);
|
|
});
|
|
|
|
it("defaults the extension to bin when there is none", () => {
|
|
const path = buildMediaPath(ACCOUNT, "README", 1700000000000);
|
|
expect(path).toBe(`account-${ACCOUNT}/1700000000000-README.bin`);
|
|
});
|
|
});
|
|
|
|
describe("MEDIA_MAX_BYTES_BY_KIND", () => {
|
|
it("caps images at Meta's tighter 5 MB limit", () => {
|
|
expect(MEDIA_MAX_BYTES_BY_KIND.image).toBe(5 * 1024 * 1024);
|
|
});
|
|
|
|
it("caps video/audio/document at the 16 MB bucket limit", () => {
|
|
expect(MEDIA_MAX_BYTES_BY_KIND.video).toBe(16 * 1024 * 1024);
|
|
expect(MEDIA_MAX_BYTES_BY_KIND.audio).toBe(16 * 1024 * 1024);
|
|
expect(MEDIA_MAX_BYTES_BY_KIND.document).toBe(16 * 1024 * 1024);
|
|
});
|
|
});
|