AccessToken.schemas.test.ts193 lines · main
1import { describe, expect, it } from 'vitest'
2
3import { PermissionRowSchema, TokenSchema } from './AccessToken.schemas'
4
5const validTokenData = {
6 tokenName: 'My Token',
7 expiresAt: 'day',
8 resourceAccess: 'all-orgs' as const,
9 permissionRows: [{ resource: 'organization:billing', actions: ['read'] }],
10}
11
12// --- PermissionRowSchema ---
13
14describe('PermissionRowSchema', () => {
15 it('should pass for a valid permission row', () => {
16 const result = PermissionRowSchema.safeParse({
17 resource: 'organization:billing',
18 actions: ['read'],
19 })
20 expect(result.success).toBe(true)
21 })
22
23 it('should fail when resource is empty', () => {
24 const result = PermissionRowSchema.safeParse({
25 resource: '',
26 actions: ['read'],
27 })
28 expect(result.success).toBe(false)
29 if (!result.success) {
30 expect(result.error.issues[0].message).toBe('Please select a resource')
31 }
32 })
33
34 it('should fail when actions is empty', () => {
35 const result = PermissionRowSchema.safeParse({
36 resource: 'organization:billing',
37 actions: [],
38 })
39 expect(result.success).toBe(false)
40 if (!result.success) {
41 expect(result.error.issues[0].message).toBe('Please select at least one action')
42 }
43 })
44
45 it('should fail when resource is missing', () => {
46 const result = PermissionRowSchema.safeParse({ actions: ['read'] })
47 expect(result.success).toBe(false)
48 })
49
50 it('should fail when actions is missing', () => {
51 const result = PermissionRowSchema.safeParse({ resource: 'organization:billing' })
52 expect(result.success).toBe(false)
53 })
54})
55
56// --- TokenSchema ---
57
58describe('TokenSchema', () => {
59 it('should pass for valid token data', () => {
60 const result = TokenSchema.safeParse(validTokenData)
61 expect(result.success).toBe(true)
62 })
63
64 it('should fail when tokenName is empty', () => {
65 const result = TokenSchema.safeParse({ ...validTokenData, tokenName: '' })
66 expect(result.success).toBe(false)
67 if (!result.success) {
68 const nameError = result.error.issues.find((i) => i.path.includes('tokenName'))
69 expect(nameError?.message).toBe('Please enter a name for the token')
70 }
71 })
72
73 it('should fail when tokenName is missing', () => {
74 const { tokenName, ...rest } = validTokenData
75 const result = TokenSchema.safeParse(rest)
76 expect(result.success).toBe(false)
77 })
78
79 it('should fail when permissionRows is empty', () => {
80 const result = TokenSchema.safeParse({ ...validTokenData, permissionRows: [] })
81 expect(result.success).toBe(false)
82 if (!result.success) {
83 const permError = result.error.issues.find((i) => i.path.includes('permissionRows'))
84 expect(permError?.message).toBe('Please configure at least one permission')
85 }
86 })
87
88 it('should fail when resourceAccess is not a valid enum value', () => {
89 const result = TokenSchema.safeParse({ ...validTokenData, resourceAccess: 'invalid' })
90 expect(result.success).toBe(false)
91 })
92
93 it('should accept all valid resourceAccess enum values', () => {
94 for (const value of ['all-orgs', 'selected-orgs', 'selected-projects'] as const) {
95 const result = TokenSchema.safeParse({ ...validTokenData, resourceAccess: value })
96 expect(result.success).toBe(true)
97 }
98 })
99
100 describe('expiresAt preprocessing', () => {
101 it('should convert "never" to undefined', () => {
102 const result = TokenSchema.safeParse({ ...validTokenData, expiresAt: 'never' })
103 expect(result.success).toBe(true)
104 if (result.success) {
105 expect(result.data.expiresAt).toBeUndefined()
106 }
107 })
108
109 it('should pass through other string values', () => {
110 const result = TokenSchema.safeParse({ ...validTokenData, expiresAt: 'day' })
111 expect(result.success).toBe(true)
112 if (result.success) {
113 expect(result.data.expiresAt).toBe('day')
114 }
115 })
116
117 it('should allow expiresAt to be omitted', () => {
118 const { expiresAt, ...rest } = validTokenData
119 const result = TokenSchema.safeParse(rest)
120 expect(result.success).toBe(true)
121 })
122 })
123
124 describe('custom expiry refinement', () => {
125 it('should fail when expiresAt is "custom" and customExpiryDate is not provided', () => {
126 const result = TokenSchema.safeParse({
127 ...validTokenData,
128 expiresAt: 'custom',
129 customExpiryDate: undefined,
130 })
131 expect(result.success).toBe(false)
132 if (!result.success) {
133 const customError = result.error.issues.find((i) => i.path.includes('expiresAt'))
134 expect(customError?.message).toBe('Please select a custom expiry date')
135 }
136 })
137
138 it('should fail when expiresAt is "custom" and customExpiryDate is empty string', () => {
139 const result = TokenSchema.safeParse({
140 ...validTokenData,
141 expiresAt: 'custom',
142 customExpiryDate: '',
143 })
144 expect(result.success).toBe(false)
145 })
146
147 it('should pass when expiresAt is "custom" and customExpiryDate is provided', () => {
148 const result = TokenSchema.safeParse({
149 ...validTokenData,
150 expiresAt: 'custom',
151 customExpiryDate: '2026-12-31T00:00:00Z',
152 })
153 expect(result.success).toBe(true)
154 })
155
156 it('should pass when expiresAt is not "custom" even without customExpiryDate', () => {
157 const result = TokenSchema.safeParse({
158 ...validTokenData,
159 expiresAt: 'day',
160 })
161 expect(result.success).toBe(true)
162 })
163 })
164
165 describe('nested permissionRows validation', () => {
166 it('should fail when a permission row has an empty resource', () => {
167 const result = TokenSchema.safeParse({
168 ...validTokenData,
169 permissionRows: [{ resource: '', actions: ['read'] }],
170 })
171 expect(result.success).toBe(false)
172 })
173
174 it('should fail when a permission row has empty actions', () => {
175 const result = TokenSchema.safeParse({
176 ...validTokenData,
177 permissionRows: [{ resource: 'organization:billing', actions: [] }],
178 })
179 expect(result.success).toBe(false)
180 })
181
182 it('should pass with multiple valid permission rows', () => {
183 const result = TokenSchema.safeParse({
184 ...validTokenData,
185 permissionRows: [
186 { resource: 'organization:billing', actions: ['read'] },
187 { resource: 'organization:members', actions: ['read', 'write'] },
188 ],
189 })
190 expect(result.success).toBe(true)
191 })
192 })
193})