auth-branding-logo.test.ts76 lines · main
1import { describe, expect, it } from 'bun:test';
2
3import { ValidationError } from '@briven/shared';
4
5import {
6 ALLOWED_LOGO_TYPES,
7 LOGO_MAX_BYTES,
8 brandingLogoPublicUrl,
9 validateLogoUpload,
10} from './auth-branding-logo.js';
11
12describe('validateLogoUpload', () => {
13 it('accepts every allowed content-type at a sane size', () => {
14 for (const ct of ALLOWED_LOGO_TYPES) {
15 expect(() => validateLogoUpload({ contentType: ct, size: 1024 })).not.toThrow();
16 }
17 });
18
19 it('accepts svg with a charset suffix', () => {
20 expect(() =>
21 validateLogoUpload({ contentType: 'image/svg+xml; charset=utf-8', size: 512 }),
22 ).not.toThrow();
23 });
24
25 it('is case-insensitive on the media type', () => {
26 expect(() => validateLogoUpload({ contentType: 'IMAGE/PNG', size: 512 })).not.toThrow();
27 });
28
29 it('accepts exactly the cap', () => {
30 expect(() =>
31 validateLogoUpload({ contentType: 'image/png', size: LOGO_MAX_BYTES }),
32 ).not.toThrow();
33 });
34
35 it('rejects a disallowed content-type', () => {
36 expect(() => validateLogoUpload({ contentType: 'image/gif', size: 512 })).toThrow(
37 ValidationError,
38 );
39 expect(() => validateLogoUpload({ contentType: 'application/pdf', size: 512 })).toThrow(
40 ValidationError,
41 );
42 expect(() => validateLogoUpload({ contentType: 'text/html', size: 512 })).toThrow(
43 ValidationError,
44 );
45 });
46
47 it('rejects an empty content-type', () => {
48 expect(() => validateLogoUpload({ contentType: '', size: 512 })).toThrow(ValidationError);
49 });
50
51 it('rejects an over-cap file', () => {
52 expect(() =>
53 validateLogoUpload({ contentType: 'image/png', size: LOGO_MAX_BYTES + 1 }),
54 ).toThrow(ValidationError);
55 });
56
57 it('rejects an empty / non-positive file', () => {
58 expect(() => validateLogoUpload({ contentType: 'image/png', size: 0 })).toThrow(
59 ValidationError,
60 );
61 expect(() => validateLogoUpload({ contentType: 'image/png', size: -5 })).toThrow(
62 ValidationError,
63 );
64 expect(() => validateLogoUpload({ contentType: 'image/png', size: Number.NaN })).toThrow(
65 ValidationError,
66 );
67 });
68});
69
70describe('brandingLogoPublicUrl', () => {
71 it('points at the public serve route and is cache-busted', () => {
72 const url = brandingLogoPublicUrl('p_abc123');
73 expect(url).toContain('/v1/projects/p_abc123/auth/branding/logo');
74 expect(url).toMatch(/\?v=\d+$/);
75 });
76});