@@ -8,6 +8,17 @@ jest.mock('../auth/auth.server', () => ({
88 auth : { api : { getSession : jest . fn ( ) } } ,
99} ) ) ;
1010
11+ // The controller pulls in the auth guard (and therefore the Prisma client and
12+ // the @trycompai /auth permission definitions) at import time. The service and
13+ // guards are fully mocked/overridden below, so none of this is exercised — stub
14+ // them out to keep this a hermetic unit test (matches the convention used by
15+ // the other controller specs in this app).
16+ jest . mock ( '@db' , ( ) => ( { db : { } } ) ) ;
17+ jest . mock ( '@trycompai/auth' , ( ) => ( {
18+ statement : { } ,
19+ BUILT_IN_ROLE_PERMISSIONS : { } ,
20+ } ) ) ;
21+
1122describe ( 'KnowledgeBaseController' , ( ) => {
1223 let controller : KnowledgeBaseController ;
1324 let service : jest . Mocked < KnowledgeBaseService > ;
@@ -89,10 +100,16 @@ describe('KnowledgeBaseController', () => {
89100 } ) ;
90101 } ) ;
91102
103+ // These handlers scope to the caller's active organization from the auth
104+ // context. Each test passes one org in the body and a different authenticated
105+ // org, and asserts the authenticated org is what reaches the service.
106+ const AUTH_ORG = 'org_authenticated' ;
107+ const OTHER_ORG = 'org_supplied_in_body' ;
108+
92109 describe ( 'uploadDocument' , ( ) => {
93- it ( 'should delegate to service ' , async ( ) => {
110+ it ( 'uses the authenticated organization, not the body organizationId ' , async ( ) => {
94111 const dto = {
95- organizationId : 'org_1' ,
112+ organizationId : OTHER_ORG ,
96113 fileName : 'doc.pdf' ,
97114 fileType : 'application/pdf' ,
98115 fileData : 'base64' ,
@@ -103,50 +120,73 @@ describe('KnowledgeBaseController', () => {
103120 s3Key : 'key' ,
104121 } ) ;
105122
106- const result = await controller . uploadDocument ( dto as any ) ;
123+ const result = await controller . uploadDocument ( AUTH_ORG , dto as any ) ;
107124
108125 expect ( result . id ) . toBe ( 'd1' ) ;
109- expect ( service . uploadDocument ) . toHaveBeenCalledWith ( dto ) ;
126+ expect ( service . uploadDocument ) . toHaveBeenCalledWith ( {
127+ ...dto ,
128+ organizationId : AUTH_ORG ,
129+ } ) ;
110130 } ) ;
111131 } ) ;
112132
113133 describe ( 'getDownloadUrl' , ( ) => {
114- it ( 'should merge documentId param with dto ' , async ( ) => {
115- const dto = { organizationId : 'org_1' } ;
134+ it ( 'scopes to the authenticated organization and merges documentId ' , async ( ) => {
135+ const dto = { organizationId : OTHER_ORG } ;
116136 mockService . getDownloadUrl . mockResolvedValue ( {
117137 signedUrl : 'https://example.com/signed' ,
118138 fileName : 'doc.pdf' ,
119139 } ) ;
120140
121- const result = await controller . getDownloadUrl ( 'd1' , dto as any ) ;
141+ const result = await controller . getDownloadUrl ( 'd1' , AUTH_ORG , dto as any ) ;
122142
123143 expect ( result . signedUrl ) . toBe ( 'https://example.com/signed' ) ;
124144 expect ( service . getDownloadUrl ) . toHaveBeenCalledWith ( {
125- ...dto ,
126145 documentId : 'd1' ,
146+ organizationId : AUTH_ORG ,
147+ } ) ;
148+ } ) ;
149+ } ) ;
150+
151+ describe ( 'getViewUrl' , ( ) => {
152+ it ( 'scopes to the authenticated organization and merges documentId' , async ( ) => {
153+ const dto = { organizationId : OTHER_ORG } ;
154+ mockService . getViewUrl . mockResolvedValue ( {
155+ signedUrl : 'https://example.com/view' ,
156+ fileName : 'doc.pdf' ,
157+ fileType : 'application/pdf' ,
158+ viewableInBrowser : true ,
159+ } ) ;
160+
161+ const result = await controller . getViewUrl ( 'd1' , AUTH_ORG , dto as any ) ;
162+
163+ expect ( result . signedUrl ) . toBe ( 'https://example.com/view' ) ;
164+ expect ( service . getViewUrl ) . toHaveBeenCalledWith ( {
165+ documentId : 'd1' ,
166+ organizationId : AUTH_ORG ,
127167 } ) ;
128168 } ) ;
129169 } ) ;
130170
131171 describe ( 'deleteDocument' , ( ) => {
132- it ( 'should merge documentId param with dto ' , async ( ) => {
133- const dto = { organizationId : 'org_1' } ;
172+ it ( 'scopes to the authenticated organization and merges documentId ' , async ( ) => {
173+ const dto = { organizationId : OTHER_ORG } ;
134174 mockService . deleteDocument . mockResolvedValue ( { success : true } ) ;
135175
136- const result = await controller . deleteDocument ( 'd1' , dto as any ) ;
176+ const result = await controller . deleteDocument ( 'd1' , AUTH_ORG , dto as any ) ;
137177
138178 expect ( result ) . toEqual ( { success : true } ) ;
139179 expect ( service . deleteDocument ) . toHaveBeenCalledWith ( {
140- ...dto ,
141180 documentId : 'd1' ,
181+ organizationId : AUTH_ORG ,
142182 } ) ;
143183 } ) ;
144184 } ) ;
145185
146186 describe ( 'processDocuments' , ( ) => {
147- it ( 'should delegate to service ' , async ( ) => {
187+ it ( 'uses the authenticated organization, not the body organizationId ' , async ( ) => {
148188 const dto = {
149- organizationId : 'org_1' ,
189+ organizationId : OTHER_ORG ,
150190 documentIds : [ 'd1' , 'd2' ] ,
151191 } ;
152192 mockService . processDocuments . mockResolvedValue ( {
@@ -155,56 +195,49 @@ describe('KnowledgeBaseController', () => {
155195 message : 'Processing 2 documents in parallel...' ,
156196 } ) ;
157197
158- const result = await controller . processDocuments ( dto as any ) ;
198+ const result = await controller . processDocuments ( AUTH_ORG , dto as any ) ;
159199
160200 expect ( result . success ) . toBe ( true ) ;
161- expect ( service . processDocuments ) . toHaveBeenCalledWith ( dto ) ;
162- } ) ;
163- } ) ;
164-
165- describe ( 'createRunToken' , ( ) => {
166- it ( 'should return token when created' , async ( ) => {
167- mockService . createRunReadToken . mockResolvedValue ( 'token_123' ) ;
168-
169- const result = await controller . createRunToken ( 'run_1' ) ;
170-
171- expect ( result ) . toEqual ( { success : true , token : 'token_123' } ) ;
172- expect ( service . createRunReadToken ) . toHaveBeenCalledWith ( 'run_1' ) ;
173- } ) ;
174-
175- it ( 'should return success false when token creation fails' , async ( ) => {
176- mockService . createRunReadToken . mockResolvedValue ( undefined ) ;
177-
178- const result = await controller . createRunToken ( 'run_1' ) ;
179-
180- expect ( result ) . toEqual ( { success : false , token : undefined } ) ;
201+ expect ( service . processDocuments ) . toHaveBeenCalledWith ( {
202+ ...dto ,
203+ organizationId : AUTH_ORG ,
204+ } ) ;
181205 } ) ;
182206 } ) ;
183207
184208 describe ( 'deleteManualAnswer' , ( ) => {
185- it ( 'should merge manualAnswerId param with dto ' , async ( ) => {
186- const dto = { organizationId : 'org_1' } ;
209+ it ( 'scopes to the authenticated organization and merges manualAnswerId ' , async ( ) => {
210+ const dto = { organizationId : OTHER_ORG } ;
187211 mockService . deleteManualAnswer . mockResolvedValue ( { success : true } ) ;
188212
189- const result = await controller . deleteManualAnswer ( 'ma1' , dto as any ) ;
213+ const result = await controller . deleteManualAnswer (
214+ 'ma1' ,
215+ AUTH_ORG ,
216+ dto as any ,
217+ ) ;
190218
191219 expect ( result ) . toEqual ( { success : true } ) ;
192220 expect ( service . deleteManualAnswer ) . toHaveBeenCalledWith ( {
193- ...dto ,
194221 manualAnswerId : 'ma1' ,
222+ organizationId : AUTH_ORG ,
195223 } ) ;
196224 } ) ;
197225 } ) ;
198226
199227 describe ( 'deleteAllManualAnswers' , ( ) => {
200- it ( 'should delegate to service ' , async ( ) => {
201- const dto = { organizationId : 'org_1' } ;
228+ it ( 'uses the authenticated organization, not the body organizationId ' , async ( ) => {
229+ const dto = { organizationId : OTHER_ORG } ;
202230 mockService . deleteAllManualAnswers . mockResolvedValue ( { success : true } ) ;
203231
204- const result = await controller . deleteAllManualAnswers ( dto as any ) ;
232+ const result = await controller . deleteAllManualAnswers (
233+ AUTH_ORG ,
234+ dto as any ,
235+ ) ;
205236
206237 expect ( result ) . toEqual ( { success : true } ) ;
207- expect ( service . deleteAllManualAnswers ) . toHaveBeenCalledWith ( dto ) ;
238+ expect ( service . deleteAllManualAnswers ) . toHaveBeenCalledWith ( {
239+ organizationId : AUTH_ORG ,
240+ } ) ;
208241 } ) ;
209242 } ) ;
210243} ) ;
0 commit comments