@@ -73,3 +73,149 @@ test("GET /api/admin/users filters by query", async () => {
7373 expect ( results . items . some ( ( u ) => u . id === adminUser . id ) ) . toBe ( true ) ;
7474 expect ( results . items . some ( ( u ) => u . id === otherUser . id ) ) . toBe ( false ) ;
7575} ) ;
76+
77+ test ( "GET /api/admin/users includes suspended field" , async ( ) => {
78+ const { helpers } = await serve ( ) ;
79+ const { client : adminClient } = await helpers . createUser ( {
80+ site_role : "admin" ,
81+ } ) ;
82+
83+ const response = await adminClient . admin . users . list ( ) ;
84+
85+ expect ( response . items . length ) . toBeGreaterThanOrEqual ( 1 ) ;
86+ expect ( response . items [ 0 ] . suspended ) . toBe ( false ) ;
87+ } ) ;
88+
89+ test ( "PATCH /api/admin/users/:id/suspension returns 403 for non-admin user" , async ( ) => {
90+ const { helpers } = await serve ( ) ;
91+ const { client : memberClient } = await helpers . createUser ( {
92+ site_role : "member" ,
93+ } ) ;
94+ const { user : targetUser } = await helpers . createUser ( {
95+ site_role : "member" ,
96+ } ) ;
97+
98+ await expect (
99+ memberClient . admin . users . updateSuspension ( targetUser . id , true )
100+ ) . rejects . toThrow ( "Forbidden" ) ;
101+ } ) ;
102+
103+ test ( "PATCH /api/admin/users/:id/suspension successfully suspends a user" , async ( ) => {
104+ const { helpers } = await serve ( ) ;
105+ const { client : adminClient } = await helpers . createUser ( {
106+ site_role : "admin" ,
107+ } ) ;
108+ const { user : targetUser } = await helpers . createUser ( {
109+ site_role : "member" ,
110+ } ) ;
111+
112+ const result = await adminClient . admin . users . updateSuspension (
113+ targetUser . id ,
114+ true
115+ ) ;
116+
117+ expect ( result . id ) . toBe ( targetUser . id ) ;
118+ expect ( result . suspended ) . toBe ( true ) ;
119+ } ) ;
120+
121+ test ( "PATCH /api/admin/users/:id/suspension successfully unsuspends a user" , async ( ) => {
122+ const { helpers } = await serve ( ) ;
123+ const { client : adminClient } = await helpers . createUser ( {
124+ site_role : "admin" ,
125+ } ) ;
126+ const { user : targetUser } = await helpers . createUser ( {
127+ site_role : "member" ,
128+ } ) ;
129+
130+ // First suspend the user
131+ await adminClient . admin . users . updateSuspension ( targetUser . id , true ) ;
132+
133+ // Then unsuspend
134+ const result = await adminClient . admin . users . updateSuspension (
135+ targetUser . id ,
136+ false
137+ ) ;
138+
139+ expect ( result . id ) . toBe ( targetUser . id ) ;
140+ expect ( result . suspended ) . toBe ( false ) ;
141+ } ) ;
142+
143+ test ( "PATCH /api/admin/users/:id/suspension returns 400 when trying to suspend yourself" , async ( ) => {
144+ const { helpers } = await serve ( ) ;
145+ const { client : adminClient , user : adminUser } = await helpers . createUser ( {
146+ site_role : "admin" ,
147+ } ) ;
148+
149+ await expect (
150+ adminClient . admin . users . updateSuspension ( adminUser . id , true )
151+ ) . rejects . toThrow ( "Cannot suspend your own account" ) ;
152+ } ) ;
153+
154+ test ( "PATCH /api/admin/users/:id/suspension returns 404 for non-existent user" , async ( ) => {
155+ const { helpers } = await serve ( ) ;
156+ const { client : adminClient } = await helpers . createUser ( {
157+ site_role : "admin" ,
158+ } ) ;
159+
160+ await expect (
161+ adminClient . admin . users . updateSuspension (
162+ "00000000-0000-0000-0000-000000000000" ,
163+ true
164+ )
165+ ) . rejects . toThrow ( "User not found" ) ;
166+ } ) ;
167+
168+ test ( "suspended user cannot authenticate with session" , async ( ) => {
169+ const { helpers } = await serve ( ) ;
170+ const { client : adminClient } = await helpers . createUser ( {
171+ site_role : "admin" ,
172+ } ) ;
173+ const { client : targetClient , user : targetUser } = await helpers . createUser ( {
174+ site_role : "member" ,
175+ } ) ;
176+
177+ // Verify user can access API before suspension
178+ const orgs = await targetClient . organizations . list ( ) ;
179+ expect ( orgs ) . toBeDefined ( ) ;
180+ expect ( Array . isArray ( orgs ) ) . toBe ( true ) ;
181+
182+ // Suspend the user
183+ await adminClient . admin . users . updateSuspension ( targetUser . id , true ) ;
184+
185+ // Verify suspended user cannot access API
186+ await expect ( targetClient . organizations . list ( ) ) . rejects . toThrow (
187+ "Account suspended"
188+ ) ;
189+ } ) ;
190+
191+ test ( "suspended user cannot authenticate with API key" , async ( ) => {
192+ const { helpers, url } = await serve ( ) ;
193+ const { client : adminClient } = await helpers . createUser ( {
194+ site_role : "admin" ,
195+ } ) ;
196+ const { client : targetClient , user : targetUser } = await helpers . createUser ( {
197+ site_role : "member" ,
198+ } ) ;
199+
200+ // Create an API key for the target user
201+ const apiKey = await targetClient . users . createApiKey ( {
202+ name : "Test API Key" ,
203+ } ) ;
204+ const targetApiKeyClient = new Client ( {
205+ baseURL : url . toString ( ) ,
206+ authToken : apiKey . key ,
207+ } ) ;
208+
209+ // Verify user can access API before suspension
210+ const orgs = await targetApiKeyClient . organizations . list ( ) ;
211+ expect ( orgs ) . toBeDefined ( ) ;
212+ expect ( Array . isArray ( orgs ) ) . toBe ( true ) ;
213+
214+ // Suspend the user
215+ await adminClient . admin . users . updateSuspension ( targetUser . id , true ) ;
216+
217+ // Verify suspended user cannot access API with API key
218+ await expect ( targetApiKeyClient . organizations . list ( ) ) . rejects . toThrow (
219+ "Account suspended"
220+ ) ;
221+ } ) ;
0 commit comments