Barazo AppView backend
barazo.forum
1import { describe, it, expect } from 'vitest'
2import {
3 userPreferencesSchema,
4 communityPreferencesSchema,
5 ageDeclarationSchema,
6} from '../../../src/validation/profiles.js'
7
8// ===========================================================================
9// Tests
10// ===========================================================================
11
12describe('profile validation schemas', () => {
13 // =========================================================================
14 // userPreferencesSchema
15 // =========================================================================
16
17 describe('userPreferencesSchema', () => {
18 it('parses a fully populated valid body', () => {
19 const result = userPreferencesSchema.safeParse({
20 maturityLevel: 'mature',
21 mutedWords: ['spoiler', 'nsfw'],
22 blockedDids: ['did:plc:blocked1'],
23 mutedDids: ['did:plc:muted1'],
24 crossPostBluesky: true,
25 crossPostFrontpage: false,
26 })
27
28 expect(result.success).toBe(true)
29 if (result.success) {
30 expect(result.data.maturityLevel).toBe('mature')
31 expect(result.data.mutedWords).toEqual(['spoiler', 'nsfw'])
32 expect(result.data.crossPostBluesky).toBe(true)
33 }
34 })
35
36 it('parses an empty body (all fields optional)', () => {
37 const result = userPreferencesSchema.safeParse({})
38
39 expect(result.success).toBe(true)
40 if (result.success) {
41 expect(result.data.maturityLevel).toBeUndefined()
42 expect(result.data.mutedWords).toBeUndefined()
43 expect(result.data.blockedDids).toBeUndefined()
44 expect(result.data.mutedDids).toBeUndefined()
45 expect(result.data.crossPostBluesky).toBeUndefined()
46 expect(result.data.crossPostFrontpage).toBeUndefined()
47 }
48 })
49
50 it('parses partial updates (only maturityLevel)', () => {
51 const result = userPreferencesSchema.safeParse({
52 maturityLevel: 'sfw',
53 })
54
55 expect(result.success).toBe(true)
56 if (result.success) {
57 expect(result.data.maturityLevel).toBe('sfw')
58 }
59 })
60
61 it('fails for invalid maturityLevel', () => {
62 const result = userPreferencesSchema.safeParse({
63 maturityLevel: 'adult',
64 })
65 expect(result.success).toBe(false)
66 })
67
68 it('fails for empty strings in mutedWords', () => {
69 const result = userPreferencesSchema.safeParse({
70 mutedWords: [''],
71 })
72 expect(result.success).toBe(false)
73 })
74
75 it('fails for non-boolean crossPostBluesky', () => {
76 const result = userPreferencesSchema.safeParse({
77 crossPostBluesky: 'yes',
78 })
79 expect(result.success).toBe(false)
80 })
81
82 it("accepts maturityLevel 'sfw'", () => {
83 const result = userPreferencesSchema.safeParse({
84 maturityLevel: 'sfw',
85 })
86 expect(result.success).toBe(true)
87 })
88
89 it("accepts maturityLevel 'mature'", () => {
90 const result = userPreferencesSchema.safeParse({
91 maturityLevel: 'mature',
92 })
93 expect(result.success).toBe(true)
94 })
95 })
96
97 // =========================================================================
98 // communityPreferencesSchema
99 // =========================================================================
100
101 describe('communityPreferencesSchema', () => {
102 it('parses a fully populated valid body', () => {
103 const result = communityPreferencesSchema.safeParse({
104 maturityOverride: 'mature',
105 mutedWords: ['spoiler'],
106 blockedDids: ['did:plc:blocked1'],
107 mutedDids: ['did:plc:muted1'],
108 notificationPrefs: {
109 replies: true,
110 reactions: false,
111 mentions: true,
112 modActions: true,
113 },
114 })
115
116 expect(result.success).toBe(true)
117 if (result.success) {
118 expect(result.data.maturityOverride).toBe('mature')
119 expect(result.data.notificationPrefs?.replies).toBe(true)
120 expect(result.data.notificationPrefs?.reactions).toBe(false)
121 }
122 })
123
124 it('parses an empty body (all fields optional)', () => {
125 const result = communityPreferencesSchema.safeParse({})
126
127 expect(result.success).toBe(true)
128 if (result.success) {
129 expect(result.data.maturityOverride).toBeUndefined()
130 expect(result.data.mutedWords).toBeUndefined()
131 expect(result.data.notificationPrefs).toBeUndefined()
132 }
133 })
134
135 it('accepts null values for nullable fields', () => {
136 const result = communityPreferencesSchema.safeParse({
137 maturityOverride: null,
138 mutedWords: null,
139 blockedDids: null,
140 mutedDids: null,
141 notificationPrefs: null,
142 })
143
144 expect(result.success).toBe(true)
145 if (result.success) {
146 expect(result.data.maturityOverride).toBeNull()
147 expect(result.data.mutedWords).toBeNull()
148 expect(result.data.notificationPrefs).toBeNull()
149 }
150 })
151
152 it('fails for invalid maturityOverride', () => {
153 const result = communityPreferencesSchema.safeParse({
154 maturityOverride: 'adult',
155 })
156 expect(result.success).toBe(false)
157 })
158
159 it('fails for incomplete notificationPrefs', () => {
160 const result = communityPreferencesSchema.safeParse({
161 notificationPrefs: {
162 replies: true,
163 // missing other required fields
164 },
165 })
166 expect(result.success).toBe(false)
167 })
168
169 it('fails for non-boolean values in notificationPrefs', () => {
170 const result = communityPreferencesSchema.safeParse({
171 notificationPrefs: {
172 replies: 'yes',
173 reactions: true,
174 mentions: true,
175 modActions: true,
176 },
177 })
178 expect(result.success).toBe(false)
179 })
180 })
181
182 // =========================================================================
183 // ageDeclarationSchema
184 // =========================================================================
185
186 describe('ageDeclarationSchema', () => {
187 it('parses valid declaredAge: 0 (rather not say)', () => {
188 const result = ageDeclarationSchema.safeParse({ declaredAge: 0 })
189 expect(result.success).toBe(true)
190 if (result.success) {
191 expect(result.data.declaredAge).toBe(0)
192 }
193 })
194
195 it('parses valid declaredAge: 13', () => {
196 const result = ageDeclarationSchema.safeParse({ declaredAge: 13 })
197 expect(result.success).toBe(true)
198 if (result.success) {
199 expect(result.data.declaredAge).toBe(13)
200 }
201 })
202
203 it('parses valid declaredAge: 14', () => {
204 const result = ageDeclarationSchema.safeParse({ declaredAge: 14 })
205 expect(result.success).toBe(true)
206 })
207
208 it('parses valid declaredAge: 15', () => {
209 const result = ageDeclarationSchema.safeParse({ declaredAge: 15 })
210 expect(result.success).toBe(true)
211 })
212
213 it('parses valid declaredAge: 16', () => {
214 const result = ageDeclarationSchema.safeParse({ declaredAge: 16 })
215 expect(result.success).toBe(true)
216 })
217
218 it('parses valid declaredAge: 18', () => {
219 const result = ageDeclarationSchema.safeParse({ declaredAge: 18 })
220 expect(result.success).toBe(true)
221 })
222
223 it('fails for declaredAge: 1 (invalid value)', () => {
224 const result = ageDeclarationSchema.safeParse({ declaredAge: 1 })
225 expect(result.success).toBe(false)
226 })
227
228 it('fails for declaredAge: 12 (below minimum)', () => {
229 const result = ageDeclarationSchema.safeParse({ declaredAge: 12 })
230 expect(result.success).toBe(false)
231 })
232
233 it('fails for declaredAge: 17 (not a valid bracket)', () => {
234 const result = ageDeclarationSchema.safeParse({ declaredAge: 17 })
235 expect(result.success).toBe(false)
236 })
237
238 it('fails for declaredAge: 19 (above valid range)', () => {
239 const result = ageDeclarationSchema.safeParse({ declaredAge: 19 })
240 expect(result.success).toBe(false)
241 })
242
243 it('fails for declaredAge: -1 (negative)', () => {
244 const result = ageDeclarationSchema.safeParse({ declaredAge: -1 })
245 expect(result.success).toBe(false)
246 })
247
248 it("fails for declaredAge: 'sixteen' (string)", () => {
249 const result = ageDeclarationSchema.safeParse({ declaredAge: 'sixteen' })
250 expect(result.success).toBe(false)
251 })
252
253 it('fails when declaredAge is missing', () => {
254 const result = ageDeclarationSchema.safeParse({})
255 expect(result.success).toBe(false)
256 })
257 })
258})