@@ -101,8 +101,23 @@ vi.mock('@/connectors/registry.server', () => ({
101101 } ,
102102 notion : {
103103 auth : { mode : 'apiKey' , optional : true } ,
104+ configFields : [ ] ,
104105 validateConfig : vi . fn ( ) . mockResolvedValue ( { valid : true } ) ,
105106 } ,
107+ jira : {
108+ name : 'Jira' ,
109+ auth : { mode : 'oauth' , provider : 'jira' } ,
110+ permissionScopedListing : { capFieldIds : [ ] } ,
111+ configFields : [
112+ { id : 'projectSelector' , canonicalParamId : 'projectKey' , selectAllValue : '*' } ,
113+ ] ,
114+ } ,
115+ confluence : {
116+ name : 'Confluence' ,
117+ auth : { mode : 'oauth' , provider : 'confluence' } ,
118+ permissionScopedListing : { capFieldIds : [ ] } ,
119+ configFields : [ { id : 'spaceSelector' , canonicalParamId : 'spaceKey' , selectAllValue : '*' } ] ,
120+ } ,
106121 google_drive : {
107122 name : 'Google Drive' ,
108123 auth : { mode : 'oauth' , provider : 'google-drive' } ,
@@ -155,6 +170,30 @@ describe('performCreateKnowledgeConnector', () => {
155170 resolveAccessToken : vi . fn ( ) ,
156171 }
157172
173+ it . each ( [
174+ { connectorType : 'jira' , field : 'projectKey' } ,
175+ { connectorType : 'confluence' , field : 'spaceKey' } ,
176+ ] ) (
177+ 'rejects mixed All keys for credentialless $connectorType members' ,
178+ async ( { connectorType, field } ) => {
179+ const outcome = await performCreateKnowledgeConnector ( {
180+ ...createParams ,
181+ connectorType,
182+ sourceConfig : { domain : 'example.atlassian.net' , [ field ] : [ '*' , 'ENG' ] } ,
183+ accessMode : 'members' ,
184+ membersBinding : { credentialGroupId : 'group-1' , credentialGroupOptionId : 'option-1' } ,
185+ } )
186+ expect ( outcome ) . toMatchObject ( {
187+ success : false ,
188+ errorCode : 'validation' ,
189+ error : 'Use "*" by itself for All, or remove it to select individual items.' ,
190+ } )
191+ expect ( dbChainMockFns . insert ) . not . toHaveBeenCalled ( )
192+ expect ( mockGrant ) . not . toHaveBeenCalled ( )
193+ expect ( createParams . resolveAccessToken ) . not . toHaveBeenCalled ( )
194+ }
195+ )
196+
158197 it ( 'validates and encrypts a GitHub PAT without resolving an OAuth account or returning the secret' , async ( ) => {
159198 dbChainMockFns . limit . mockResolvedValueOnce ( [ { id : 'kb-1' } ] )
160199 dbChainMockFns . returning . mockResolvedValueOnce ( [
@@ -481,6 +520,41 @@ describe('performUpdateKnowledgeConnector', () => {
481520
482521 afterAll ( resetDbChainMock )
483522
523+ it . each ( [
524+ { connectorType : 'jira' , field : 'projectKey' } ,
525+ { connectorType : 'confluence' , field : 'spaceKey' } ,
526+ ] ) (
527+ 'rejects mixed All keys when editing credentialless $connectorType members' ,
528+ async ( { connectorType, field } ) => {
529+ queueTableRows ( schemaMock . knowledgeConnector , [
530+ {
531+ id : 'conn-1' ,
532+ connectorType,
533+ accessMode : 'members' ,
534+ status : 'active' ,
535+ memberSyncStatus : 'idle' ,
536+ credentialId : null ,
537+ } ,
538+ ] )
539+ const validateSourceConfig = vi . fn ( )
540+ const outcome = await performUpdateKnowledgeConnector ( {
541+ ...ACTOR ,
542+ knowledgeBase : KB ,
543+ connectorId : 'conn-1' ,
544+ updates : { sourceConfig : { domain : 'example.atlassian.net' , [ field ] : '*, ENG' } } ,
545+ resolveBillingAttribution,
546+ validateSourceConfig,
547+ } )
548+ expect ( outcome ) . toMatchObject ( {
549+ success : false ,
550+ errorCode : 'validation' ,
551+ error : 'Use "*" by itself for All, or remove it to select individual items.' ,
552+ } )
553+ expect ( dbChainMockFns . update ) . not . toHaveBeenCalled ( )
554+ expect ( validateSourceConfig ) . not . toHaveBeenCalled ( )
555+ }
556+ )
557+
484558 it ( 'rejects an update that names nothing before reading the connector' , async ( ) => {
485559 const outcome = await performUpdateKnowledgeConnector ( {
486560 ...ACTOR ,
0 commit comments