1- import { createMockRequest } from '@sim/testing'
1+ import { createMockRequest , dbChainMockFns , resetDbChainMock } from '@sim/testing'
22import { createRouteContext } from '@sim/testing/helpers/http'
33import { authMockFns } from '@sim/testing/mocks/auth.mock'
44import { workspaceAuthzMock , workspaceAuthzMockFns } from '@sim/testing/mocks/workspace-authz.mock'
@@ -8,19 +8,13 @@ import {
88} from '@sim/testing/mocks/workspace-context.mock'
99import { beforeEach , describe , expect , it , vi } from 'vitest'
1010
11- const hoisted = vi . hoisted ( ( ) => ( {
12- record : vi . fn ( ) ,
13- } ) )
14-
1511vi . mock ( '@sim/platform-authz/workspace' , ( ) => workspaceAuthzMock )
1612vi . mock ( '@/lib/workspaces/application/context' , ( ) => workspaceContextMock )
17- vi . mock ( '@/lib/workspaces/visits' , ( ) => ( { recordWorkspaceVisitRecord : hoisted . record } ) )
1813
1914import { OrchestrationError } from '@/lib/core/orchestration/types'
2015import { POST } from '@/app/api/workspaces/[id]/visit/route'
2116
2217const mocks = {
23- ...hoisted ,
2418 getSession : authMockFns . mockGetSession ,
2519 role : workspaceAuthzMockFns . mockResolveEffectiveWorkspacePermission ,
2620 context : workspaceContextMockFns . mockResolveActiveWorkspaceApplicationContext ,
@@ -30,14 +24,14 @@ const routeContext = createRouteContext({ id: 'ws-1' })
3024
3125describe ( 'POST /api/workspaces/[id]/visit' , ( ) => {
3226 beforeEach ( ( ) => {
27+ resetDbChainMock ( )
3328 mocks . getSession . mockResolvedValue ( { user : { id : 'user-1' } , session : { id : 'session-1' } } )
3429 mocks . context . mockImplementation ( async ( workspaceId : string ) => ( {
3530 workspaceId,
3631 workspaceOrganizationId : null ,
3732 allowPersonalApiKeys : true ,
3833 } ) )
3934 mocks . role . mockResolvedValue ( 'read' )
40- mocks . record . mockResolvedValue ( undefined )
4135 } )
4236
4337 it ( 'answers 404 for a workspace outside the caller reach, same as a missing one' , async ( ) => {
@@ -50,6 +44,38 @@ describe('POST /api/workspaces/[id]/visit', () => {
5044 expect ( denied . status ) . toBe ( 404 )
5145 expect ( missing . status ) . toBe ( 404 )
5246 expect ( await denied . json ( ) ) . toEqual ( await missing . json ( ) )
53- expect ( mocks . record ) . not . toHaveBeenCalled ( )
47+ expect ( dbChainMockFns . insert ) . not . toHaveBeenCalled ( )
48+ } )
49+
50+ /**
51+ * Account deletion removes the user row while a page load still carries its
52+ * session, so the visit insert loses the `user_id` foreign key. The caller is
53+ * no longer signed in as anyone, which is a 401, not a server fault.
54+ */
55+ it ( 'answers 401 when the signed-in account is deleted before the visit lands' , async ( ) => {
56+ dbChainMockFns . onConflictDoUpdate . mockRejectedValueOnce (
57+ Object . assign ( new Error ( 'insert or update on table "workspace_visit" violates foreign key' ) , {
58+ code : '23503' ,
59+ constraint_name : 'workspace_visit_user_id_user_id_fk' ,
60+ } )
61+ )
62+
63+ const response = await POST ( createMockRequest ( 'POST' ) , routeContext )
64+
65+ expect ( response . status ) . toBe ( 401 )
66+ expect ( await response . json ( ) ) . toEqual ( { error : 'Unauthorized' } )
67+ } )
68+
69+ it ( 'keeps any other insert failure a server fault' , async ( ) => {
70+ dbChainMockFns . onConflictDoUpdate . mockRejectedValueOnce (
71+ Object . assign ( new Error ( 'insert or update on table "workspace_visit" violates foreign key' ) , {
72+ code : '23503' ,
73+ constraint_name : 'workspace_visit_workspace_id_workspace_id_fk' ,
74+ } )
75+ )
76+
77+ const response = await POST ( createMockRequest ( 'POST' ) , routeContext )
78+
79+ expect ( response . status ) . toBe ( 500 )
5480 } )
5581} )
0 commit comments