@@ -9,18 +9,19 @@ const ATLASSIAN_SCOPES = ['read:me', 'read:jira-work', 'offline_access']
99
1010describe ( 'Atlassian managed OAuth connector' , ( ) => {
1111 it ( 'uses the existing connector callback contract and verifies the current account' , async ( ) => {
12- const fetchMock = vi . fn ( ) . mockResolvedValue (
13- new Response (
14- JSON . stringify ( {
15- account_type : 'atlassian' ,
16- account_id : 'account-1' ,
17- email : 'person@example.com' ,
18- name : 'Person' ,
19- picture : 'https://example.com/avatar.png' ,
20- account_status : 'active' ,
21- } ) ,
22- { status : 200 , headers : { 'Content-Type' : 'application/json' } }
23- )
12+ const fetchMock = vi . fn ( ) . mockImplementation (
13+ async ( ) =>
14+ new Response (
15+ JSON . stringify ( {
16+ account_type : 'atlassian' ,
17+ account_id : 'account-1' ,
18+ email : 'person@example.com' ,
19+ name : 'Person' ,
20+ picture : 'https://example.com/avatar.png' ,
21+ account_status : 'active' ,
22+ } ) ,
23+ { status : 200 , headers : { 'Content-Type' : 'application/json' } }
24+ )
2425 )
2526 vi . stubGlobal ( 'fetch' , fetchMock )
2627 const connector = createAtlassianManagedOAuthConnector ( 'jira' )
@@ -60,18 +61,76 @@ describe('Atlassian managed OAuth connector', () => {
6061 } )
6162 } )
6263
64+ const ATLASSIAN_PROFILE = {
65+ account_type : 'atlassian' ,
66+ account_id : 'account-1' ,
67+ email : 'person@example.com' ,
68+ account_status : 'active' ,
69+ }
70+ const json = ( body : unknown , status = 200 ) =>
71+ new Response ( JSON . stringify ( body ) , {
72+ status,
73+ headers : { 'Content-Type' : 'application/json' } ,
74+ } )
75+ function stubAtlassian ( sites : ( ) => Response ) {
76+ vi . stubGlobal (
77+ 'fetch' ,
78+ vi . fn ( async ( url : string ) =>
79+ url === 'https://api.atlassian.com/me' ? json ( ATLASSIAN_PROFILE ) : sites ( )
80+ )
81+ )
82+ }
83+ const verify = ( provider : 'jira' | 'confluence' = 'confluence' ) =>
84+ createAtlassianManagedOAuthConnector ( provider ) . verifyIdentity ( {
85+ tokens : { tokenType : 'Bearer' , accessToken : 'access-1' , scopes : ATLASSIAN_SCOPES } ,
86+ clientId : 'client-1' ,
87+ } )
88+
89+ it ( 'records the sites the grant reaches, so an added site is visible' , async ( ) => {
90+ stubAtlassian ( ( ) =>
91+ json ( [
92+ { id : 'cloud-1' , name : 'Acme' , url : 'https://acme.example.net' , scopes : [ ] } ,
93+ { id : 'cloud-2' , name : 'Beta' , url : 'https://beta.example.net/' , scopes : [ ] } ,
94+ { id : 'cloud-3' , name : 'Insecure' , url : 'http://insecure.example.net' } ,
95+ { id : 'cloud-4' , url : 'https://unnamed.example.net' } ,
96+ ] )
97+ )
98+
99+ expect ( ( await verify ( ) ) . sites ) . toEqual ( [
100+ { name : 'Acme' , url : 'https://acme.example.net' } ,
101+ { name : 'Beta' , url : 'https://beta.example.net' } ,
102+ ] )
103+ } )
104+
105+ it . each ( [
106+ [ 'an error status' , ( ) => json ( { message : 'unavailable' } , 503 ) ] ,
107+ [
108+ 'a network failure' ,
109+ ( ) => {
110+ throw new TypeError ( 'fetch failed' )
111+ } ,
112+ ] ,
113+ [ 'a malformed body' , ( ) => json ( { sites : 'nope' } ) ] ,
114+ ] ) ( 'still connects without a site list after %s' , async ( _name , sites ) => {
115+ stubAtlassian ( sites )
116+ const identity = await verify ( 'jira' )
117+ expect ( identity ) . toMatchObject ( { providerSubjectId : 'account-1' } )
118+ expect ( identity . sites ) . toBeUndefined ( )
119+ } )
120+
63121 it ( 'fails closed for an inactive or incomplete Atlassian identity' , async ( ) => {
64122 vi . stubGlobal (
65123 'fetch' ,
66- vi . fn ( ) . mockResolvedValue (
67- new Response (
68- JSON . stringify ( {
69- account_type : 'atlassian' ,
70- account_id : 'account-1' ,
71- account_status : 'inactive' ,
72- } ) ,
73- { status : 200 , headers : { 'Content-Type' : 'application/json' } }
74- )
124+ vi . fn ( ) . mockImplementation (
125+ async ( ) =>
126+ new Response (
127+ JSON . stringify ( {
128+ account_type : 'atlassian' ,
129+ account_id : 'account-1' ,
130+ account_status : 'inactive' ,
131+ } ) ,
132+ { status : 200 , headers : { 'Content-Type' : 'application/json' } }
133+ )
75134 )
76135 )
77136 const connector = createAtlassianManagedOAuthConnector ( 'confluence' )
0 commit comments