11/**
22 * OAuth Authentication API Tests
3+ *
4+ * Runs at the end of the suite (Phase 23) to avoid interfering with
5+ * existing auth tokens. Reuses the testContext authtoken created during
6+ * global setup so we don't hit the 20-token-per-user limit.
37 */
48
59import { expect } from 'chai'
610import { describe , it , before } from 'mocha'
711import { contentstackClient } from '../utility/ContentstackClient.js'
12+ import * as testSetup from '../utility/testSetup.js'
813import axios from 'axios'
914
1015let client = null
@@ -24,60 +29,67 @@ const appId = process.env.APP_ID
2429const redirectUri = process . env . REDIRECT_URI
2530const organizationUid = process . env . ORGANIZATION
2631
32+ /**
33+ * Compute Developer Hub URL from HOST environment variable
34+ * Handles the special case where dev11 uses 'dev-' prefix instead of 'dev11-'
35+ *
36+ * @param {string } host - API host from environment (e.g., 'dev9-api.csnonprod.com')
37+ * @returns {string } Developer Hub URL (e.g., 'https://dev9-developerhub-api.csnonprod.com')
38+ */
39+ function getDeveloperHubUrl ( host ) {
40+ if ( ! host ) return 'https://developerhub-api.contentstack.com'
41+
42+ let devHubUrl = host
43+ . replace ( 'api' , 'developerhub-api' )
44+ . replace ( '.io' , '.com' )
45+
46+ // Special case: dev11 uses 'dev-' prefix instead of 'dev11-'
47+ if ( devHubUrl . includes ( 'dev11-' ) ) {
48+ devHubUrl = devHubUrl . replace ( 'dev11-' , 'dev-' )
49+ }
50+
51+ // Ensure https:// protocol
52+ if ( ! devHubUrl . startsWith ( 'http' ) ) {
53+ devHubUrl = `https://${ devHubUrl } `
54+ }
55+
56+ return devHubUrl
57+ }
58+
2759describe ( 'OAuth Authentication API Tests' , ( ) => {
2860 before ( function ( ) {
29- client = contentstackClient ( )
30-
3161 // Skip all OAuth tests if credentials not configured
3262 if ( ! clientId || ! appId || ! redirectUri ) {
33- console . log ( 'OAuth credentials not configured - skipping OAuth tests' )
63+ console . log ( ' OAuth: skipped (CLIENT_ID / APP_ID / REDIRECT_URI not configured)' )
64+ this . skip ( )
65+ }
66+
67+ // Reuse the authtoken from global setup — avoids creating a duplicate
68+ // session that could push us past the 20-token-per-user limit.
69+ const ctx = testSetup . testContext
70+ if ( ctx && ctx . authtoken ) {
71+ authtoken = ctx . authtoken
72+ client = contentstackClient ( authtoken )
73+ } else {
74+ console . log ( ' OAuth: skipped (no authtoken from testSetup)' )
75+ this . skip ( )
3476 }
3577 } )
3678
3779 describe ( 'OAuth Setup and Authorization' , ( ) => {
38- it ( 'should login with credentials to get authtoken' , async function ( ) {
80+ it ( 'should have a valid authtoken from global setup' , function ( ) {
3981 this . timeout ( 15000 )
4082
41- if ( ! process . env . EMAIL || ! process . env . PASSWORD ) {
42- this . skip ( )
43- }
44-
45- try {
46- const response = await client . login ( {
47- email : process . env . EMAIL ,
48- password : process . env . PASSWORD
49- } , {
50- include_orgs : true ,
51- include_orgs_roles : true ,
52- include_stack_roles : true ,
53- include_user_settings : true
54- } )
55-
56- authtoken = response . user . authtoken
57-
58- expect ( response . notice ) . to . equal ( 'Login Successful.' )
59- expect ( authtoken ) . to . not . equal ( undefined )
60-
61- // Use a client with the new authtoken so subsequent tests (getUser, OAuth flow) are authenticated
62- client = contentstackClient ( authtoken )
63- } catch ( error ) {
64- console . log ( 'Login warning:' , error . message )
65- this . skip ( )
66- }
83+ expect ( authtoken ) . to . be . a ( 'string' )
84+ expect ( authtoken ) . to . not . equal ( '' )
6785 } )
6886
6987 it ( 'should get current user info' , async function ( ) {
7088 this . timeout ( 15000 )
7189
72- try {
73- const user = await client . getUser ( )
74-
75- expect ( user . uid ) . to . not . equal ( undefined )
76- expect ( user . email ) . to . not . equal ( undefined )
77- } catch ( error ) {
78- // User might not be logged in
79- this . skip ( )
80- }
90+ const user = await client . getUser ( )
91+ expect ( user . uid ) . to . not . equal ( undefined )
92+ expect ( user . email ) . to . not . equal ( undefined )
8193 } )
8294
8395 it ( 'should fail with invalid OAuth app credentials' , async function ( ) {
@@ -95,25 +107,16 @@ describe('OAuth Authentication API Tests', () => {
95107 }
96108 } )
97109
98- it ( 'should initialize OAuth client with valid credentials' , async function ( ) {
110+ it ( 'should initialize OAuth client with valid credentials' , function ( ) {
99111 this . timeout ( 15000 )
100112
101- if ( ! clientId || ! appId || ! redirectUri ) {
102- this . skip ( )
103- }
104-
105- try {
106- oauthClient = client . oauth ( {
107- clientId : clientId ,
108- appId : appId ,
109- redirectUri : redirectUri
110- } )
113+ oauthClient = client . oauth ( {
114+ clientId : clientId ,
115+ appId : appId ,
116+ redirectUri : redirectUri
117+ } )
111118
112- expect ( oauthClient ) . to . not . equal ( undefined )
113- } catch ( error ) {
114- console . log ( 'OAuth client initialization warning:' , error . message )
115- this . skip ( )
116- }
119+ expect ( oauthClient ) . to . not . equal ( undefined )
117120 } )
118121
119122 it ( 'should generate OAuth authorization URL' , async function ( ) {
@@ -123,22 +126,17 @@ describe('OAuth Authentication API Tests', () => {
123126 this . skip ( )
124127 }
125128
126- try {
127- authUrl = await oauthClient . authorize ( )
129+ authUrl = await oauthClient . authorize ( )
128130
129- expect ( authUrl ) . to . not . equal ( undefined )
130- expect ( authUrl ) . to . include ( clientId )
131+ expect ( authUrl ) . to . not . equal ( undefined )
132+ expect ( authUrl ) . to . include ( clientId )
131133
132- const url = new URL ( authUrl )
133- codeChallenge = url . searchParams . get ( 'code_challenge' )
134- codeChallengeMethod = url . searchParams . get ( 'code_challenge_method' )
134+ const url = new URL ( authUrl )
135+ codeChallenge = url . searchParams . get ( 'code_challenge' )
136+ codeChallengeMethod = url . searchParams . get ( 'code_challenge_method' )
135137
136- expect ( codeChallenge ) . to . not . equal ( '' )
137- expect ( codeChallengeMethod ) . to . not . equal ( '' )
138- } catch ( error ) {
139- console . log ( 'Authorization URL warning:' , error . message )
140- this . skip ( )
141- }
138+ expect ( codeChallenge ) . to . not . equal ( '' )
139+ expect ( codeChallengeMethod ) . to . not . equal ( '' )
142140 } )
143141
144142 it ( 'should simulate authorization and get auth code' , async function ( ) {
@@ -148,38 +146,35 @@ describe('OAuth Authentication API Tests', () => {
148146 this . skip ( )
149147 }
150148
151- try {
152- const authorizationEndpoint = oauthClient . axiosInstance . defaults . developerHubBaseUrl
153-
154- axios . defaults . headers . common . authtoken = authtoken
155- axios . defaults . headers . common . organization_uid = organizationUid
156-
157- const response = await axios . post (
158- `${ authorizationEndpoint } /manifests/${ appId } /authorize` ,
159- {
160- client_id : clientId ,
161- redirect_uri : redirectUri ,
162- code_challenge : codeChallenge ,
163- code_challenge_method : codeChallengeMethod ,
164- response_type : 'code'
165- }
166- )
167-
168- const redirectUrl = response . data . data . redirect_url
169- const url = new URL ( redirectUrl )
170- authCode = url . searchParams . get ( 'code' )
171-
172- expect ( redirectUrl ) . to . not . equal ( '' )
173- expect ( authCode ) . to . not . equal ( null )
174-
175- // Set OAuth client properties
176- oauthClient . axiosInstance . oauth . appId = appId
177- oauthClient . axiosInstance . oauth . clientId = clientId
178- oauthClient . axiosInstance . oauth . redirectUri = redirectUri
179- } catch ( error ) {
180- console . log ( 'Authorization simulation warning:' , error . message )
181- this . skip ( )
182- }
149+ // Compute the correct Developer Hub URL from HOST env variable
150+ // This handles the dev11 special case (dev11 -> dev) without modifying SDK code
151+ const authorizationEndpoint = getDeveloperHubUrl ( process . env . HOST )
152+ console . log ( ' Developer Hub endpoint:' , authorizationEndpoint )
153+
154+ axios . defaults . headers . common . authtoken = authtoken
155+ axios . defaults . headers . common . organization_uid = organizationUid
156+
157+ const response = await axios . post (
158+ `${ authorizationEndpoint } /manifests/${ appId } /authorize` ,
159+ {
160+ client_id : clientId ,
161+ redirect_uri : redirectUri ,
162+ code_challenge : codeChallenge ,
163+ code_challenge_method : codeChallengeMethod ,
164+ response_type : 'code'
165+ }
166+ )
167+
168+ const redirectUrl = response . data . data . redirect_url
169+ const url = new URL ( redirectUrl )
170+ authCode = url . searchParams . get ( 'code' )
171+
172+ expect ( redirectUrl ) . to . not . equal ( '' )
173+ expect ( authCode ) . to . not . equal ( null )
174+
175+ oauthClient . axiosInstance . oauth . appId = appId
176+ oauthClient . axiosInstance . oauth . clientId = clientId
177+ oauthClient . axiosInstance . oauth . redirectUri = redirectUri
183178 } )
184179 } )
185180
@@ -191,20 +186,15 @@ describe('OAuth Authentication API Tests', () => {
191186 this . skip ( )
192187 }
193188
194- try {
195- const response = await oauthClient . exchangeCodeForToken ( authCode )
189+ const response = await oauthClient . exchangeCodeForToken ( authCode )
196190
197- accessToken = response . access_token
198- refreshToken = response . refresh_token
199- loggedinUserId = response . user_uid
191+ accessToken = response . access_token
192+ refreshToken = response . refresh_token
193+ loggedinUserId = response . user_uid
200194
201- expect ( response . organization_uid ) . to . equal ( organizationUid )
202- expect ( response . access_token ) . to . not . equal ( null )
203- expect ( response . refresh_token ) . to . not . equal ( null )
204- } catch ( error ) {
205- console . log ( 'Token exchange warning:' , error . message )
206- this . skip ( )
207- }
195+ expect ( response . organization_uid ) . to . equal ( organizationUid )
196+ expect ( response . access_token ) . to . not . equal ( null )
197+ expect ( response . refresh_token ) . to . not . equal ( null )
208198 } )
209199
210200 it ( 'should get user info using access token' , async function ( ) {
@@ -214,17 +204,12 @@ describe('OAuth Authentication API Tests', () => {
214204 this . skip ( )
215205 }
216206
217- try {
218- const user = await client . getUser ( {
219- authorization : `Bearer ${ accessToken } `
220- } )
207+ const user = await client . getUser ( {
208+ authorization : `Bearer ${ accessToken } `
209+ } )
221210
222- expect ( user . uid ) . to . equal ( loggedinUserId )
223- expect ( user . email ) . to . equal ( process . env . EMAIL )
224- } catch ( error ) {
225- console . log ( 'Get user with token warning:' , error . message )
226- this . skip ( )
227- }
211+ expect ( user . uid ) . to . equal ( loggedinUserId )
212+ expect ( user . email ) . to . equal ( process . env . EMAIL )
228213 } )
229214
230215 it ( 'should refresh access token using refresh token' , async function ( ) {
@@ -234,18 +219,13 @@ describe('OAuth Authentication API Tests', () => {
234219 this . skip ( )
235220 }
236221
237- try {
238- const response = await oauthClient . refreshAccessToken ( refreshToken )
222+ const response = await oauthClient . refreshAccessToken ( refreshToken )
239223
240- accessToken = response . access_token
241- refreshToken = response . refresh_token
224+ accessToken = response . access_token
225+ refreshToken = response . refresh_token
242226
243- expect ( response . access_token ) . to . not . equal ( null )
244- expect ( response . refresh_token ) . to . not . equal ( null )
245- } catch ( error ) {
246- console . log ( 'Token refresh warning:' , error . message )
247- this . skip ( )
248- }
227+ expect ( response . access_token ) . to . not . equal ( null )
228+ expect ( response . refresh_token ) . to . not . equal ( null )
249229 } )
250230 } )
251231
@@ -257,14 +237,8 @@ describe('OAuth Authentication API Tests', () => {
257237 this . skip ( )
258238 }
259239
260- try {
261- const response = await oauthClient . logout ( )
262-
263- expect ( response ) . to . equal ( 'Logged out successfully' )
264- } catch ( error ) {
265- console . log ( 'Logout warning:' , error . message )
266- this . skip ( )
267- }
240+ const response = await oauthClient . logout ( )
241+ expect ( response ) . to . equal ( 'Logged out successfully' )
268242 } )
269243
270244 it ( 'should fail API request with expired/revoked token' , async function ( ) {
0 commit comments