@@ -15,6 +15,11 @@ class LoginTests {
1515
1616 @Test ( " Login Spec Example 1: Valid URL " )
1717 func testValidURL( ) async throws {
18+ let stubs = HTTPStubs ( stubs: [
19+ try HTTPStubs . stub ( url: " https://vanilla.wpmt.co/ " , with: . withApiRoot( " https://vanilla.wpmt.co/wp-json/ " ) ) ,
20+ try HTTPStubs . stub ( url: " https://vanilla.wpmt.co/wp-json/ " , with: . loginMockResponse( named: " vanilla-api-root " ) )
21+ ] )
22+ let client = WordPressLoginClient ( requestExecutor: stubs)
1823 let parsedUrl = try await client. findLoginUrl ( forSite: " https://vanilla.wpmt.co " )
1924 #expect( " https://vanilla.wpmt.co/wp-admin/authorize-application.php " == parsedUrl. url ( ) )
2025 }
@@ -50,20 +55,46 @@ class LoginTests {
5055 ( " https://vanilla.wpmt.co/wp-admin " , " https://vanilla.wpmt.co/wp-admin/authorize-application.php " )
5156 ] )
5257 func testAdminUrlProvided( _ provided: String , _ expected: String ) async throws {
58+ // The UserInput attempt uses the admin URL as-is (no stub found -> fails).
59+ // The AutoStrippedHttps attempt strips the admin suffix and succeeds.
60+ let stubs = HTTPStubs ( stubs: [
61+ try HTTPStubs . stub ( url: " https://vanilla.wpmt.co/ " , with: . withApiRoot( " https://vanilla.wpmt.co/wp-json/ " ) ) ,
62+ try HTTPStubs . stub ( url: " https://vanilla.wpmt.co/wp-json/ " , with: . loginMockResponse( named: " vanilla-api-root " ) )
63+ ] )
64+ let client = WordPressLoginClient ( requestExecutor: stubs)
5365 let parsedUrl = try await client. findLoginUrl ( forSite: provided)
5466 #expect( expected == parsedUrl. url ( ) )
5567 }
5668
5769 @Test ( " Login Spec Example 4: HTTP URL with HTTPS Support " )
5870 func testAutoHttpsSupport( ) async throws {
71+ // UserInput attempt fetches http://vanilla.wpmt.co/ (no stub -> fails).
72+ // AutoStrippedHttps attempt converts to https:// and succeeds.
73+ let stubs = HTTPStubs ( stubs: [
74+ try HTTPStubs . stub ( url: " https://vanilla.wpmt.co/ " , with: . withApiRoot( " https://vanilla.wpmt.co/wp-json/ " ) ) ,
75+ try HTTPStubs . stub ( url: " https://vanilla.wpmt.co/wp-json/ " , with: . loginMockResponse( named: " vanilla-api-root " ) )
76+ ] )
77+ let client = WordPressLoginClient ( requestExecutor: stubs)
5978 let parsedUrl = try await client. findLoginUrl ( forSite: " http://vanilla.wpmt.co " )
6079 #expect( " https://vanilla.wpmt.co/wp-admin/authorize-application.php " == parsedUrl. url ( ) )
6180 }
6281
6382 @Test ( " Login Spec Example 5: HTTP-only site " )
6483 func testHttpOnlySite( ) async {
84+ let stubs : HTTPStubs
85+ do {
86+ stubs = HTTPStubs ( stubs: [
87+ try HTTPStubs . stub ( url: " http://no-https.wpmt.co/ " , with: . withApiRoot( " http://no-https.wpmt.co/wp-json/ " ) ) ,
88+ try HTTPStubs . stub ( url: " http://no-https.wpmt.co/wp-json/ " , with: . loginMockResponse( named: " http-only-api-root " ) )
89+ ] )
90+ } catch {
91+ Issue . record ( " Failed to create stubs: \( error) " )
92+ return
93+ }
94+ let client = WordPressLoginClient ( requestExecutor: stubs)
95+
6596 await #expect( performing: {
66- _ = try await self . client. findLoginUrl ( forSite: " http://no-https.wpmt.co " )
97+ _ = try await client. findLoginUrl ( forSite: " http://no-https.wpmt.co " )
6798 } , throws: { error in
6899 let reason = try #require( try self . getApplicationPasswordsNotSupportedReason ( from: error) )
69100
@@ -78,12 +109,23 @@ class LoginTests {
78109
79110 @Test ( " Login Spec Example 6: HTTP-Only Site with Application Password Override " )
80111 func testHttpOnlySiteWithApplicationPasswordsEnabled( ) async throws {
112+ let stubs = HTTPStubs ( stubs: [
113+ try HTTPStubs . stub ( url: " http://no-https-with-application-passwords.wpmt.co/ " , with: . withApiRoot( " http://no-https-with-application-passwords.wpmt.co/wp-json/ " ) ) ,
114+ try HTTPStubs . stub ( url: " http://no-https-with-application-passwords.wpmt.co/wp-json/ " , with: . loginMockResponse( named: " http-only-with-app-passwords-api-root " ) )
115+ ] )
116+ let client = WordPressLoginClient ( requestExecutor: stubs)
81117 let parsedUrl = try await client. findLoginUrl ( forSite: " http://no-https-with-application-passwords.wpmt.co " )
82118 #expect( " http://no-https-with-application-passwords.wpmt.co/wp-admin/authorize-application.php " == parsedUrl. url ( ) )
83119 }
84120
85121 @Test ( " Login Spec Example 7: CDN-Cached Site " )
86122 func testAggressivelyCachedSiteWithNoLinkheader( ) async throws {
123+ // Homepage has no Link header, but HTML contains a <link> tag pointing to the API root
124+ let stubs = HTTPStubs ( stubs: [
125+ try HTTPStubs . stub ( url: " https://aggressive-caching.wpmt.co/ " , with: . htmlResponse( named: " homepage-with-link-tag " ) ) ,
126+ try HTTPStubs . stub ( url: " https://aggressive-caching.wpmt.co/wp-json/ " , with: . loginMockResponse( named: " aggressive-caching-api-root " ) )
127+ ] )
128+ let client = WordPressLoginClient ( requestExecutor: stubs)
87129 let parsedUrl = try await client. findLoginUrl ( forSite: " https://aggressive-caching.wpmt.co " )
88130 #expect( " https://aggressive-caching.wpmt.co/wp-admin/authorize-application.php " == parsedUrl. url ( ) )
89131 }
@@ -111,8 +153,20 @@ class LoginTests {
111153 " https://google.com "
112154 ] )
113155 func testNotWordPressSite( url: String ) async throws {
156+ // Homepage returns non-WordPress HTML, no Link header, and no WP markers
157+ let stubs : HTTPStubs
158+ do {
159+ stubs = HTTPStubs ( stubs: [
160+ try HTTPStubs . stub ( url: " https://google.com/ " , with: . htmlResponse( named: " homepage-not-wordpress " ) )
161+ ] )
162+ } catch {
163+ Issue . record ( " Failed to create stubs: \( error) " )
164+ return
165+ }
166+ let client = WordPressLoginClient ( requestExecutor: stubs)
167+
114168 await #expect( performing: {
115- _ = try await self . client. findLoginUrl ( forSite: url)
169+ _ = try await client. findLoginUrl ( forSite: url)
116170 } , throws: { error in
117171 try #require( error is AutoDiscoveryAttemptFailure )
118172
@@ -130,26 +184,55 @@ class LoginTests {
130184
131185 @Test ( " Login Spec Example 10: WordPress in a subdirectory with a link header " )
132186 func testWordPressSubdirectoryWithLinkHeader( ) async throws {
187+ let stubs = HTTPStubs ( stubs: [
188+ try HTTPStubs . stub ( url: " https://subdirectory.wpmt.co/index.php?link_header=true " , with: . withApiRoot( " https://subdirectory.wpmt.co/wordpress/wp-json/ " ) ) ,
189+ try HTTPStubs . stub ( url: " https://subdirectory.wpmt.co/wordpress/wp-json/ " , with: . loginMockResponse( named: " subdirectory-api-root " ) )
190+ ] )
191+ let client = WordPressLoginClient ( requestExecutor: stubs)
133192 let parsedUrl = try await client. findLoginUrl ( forSite: " https://subdirectory.wpmt.co/index.php?link_header=true " )
134193 #expect( " https://subdirectory.wpmt.co/wordpress/wp-admin/authorize-application.php " == parsedUrl. url ( ) )
135194 }
136195
137196 @Test ( " Login Spec Example 11: WordPress in a subdirectory with a link tag " )
138197 func testWordPressSubdirectoryWithLinkTag( ) async throws {
198+ // Homepage has no Link header but HTML contains a <link> tag pointing to subdirectory wp-json
199+ let stubs = HTTPStubs ( stubs: [
200+ try HTTPStubs . stub ( url: " https://subdirectory.wpmt.co/index.php?link_tag=true " , with: . htmlResponse( named: " homepage-with-subdirectory-link-tag " ) ) ,
201+ try HTTPStubs . stub ( url: " https://subdirectory.wpmt.co/wordpress/wp-json/ " , with: . loginMockResponse( named: " subdirectory-api-root " ) )
202+ ] )
203+ let client = WordPressLoginClient ( requestExecutor: stubs)
139204 let parsedUrl = try await client. findLoginUrl ( forSite: " https://subdirectory.wpmt.co/index.php?link_tag=true " )
140205 #expect( " https://subdirectory.wpmt.co/wordpress/wp-admin/authorize-application.php " == parsedUrl. url ( ) )
141206 }
142207
143208 @Test ( " Login Spec Example 12: WordPress in a subdirectory with a redirect " )
144209 func testWordPressSubdirectory( ) async throws {
210+ // In the real scenario, the server redirects to /wordpress/ which has the Link header.
211+ // With mocks, we simulate the final response directly on the requested URL.
212+ let stubs = HTTPStubs ( stubs: [
213+ try HTTPStubs . stub ( url: " https://subdirectory.wpmt.co/index.php?redirect=true " , with: . withApiRoot( " https://subdirectory.wpmt.co/wordpress/wp-json/ " ) ) ,
214+ try HTTPStubs . stub ( url: " https://subdirectory.wpmt.co/wordpress/wp-json/ " , with: . loginMockResponse( named: " subdirectory-api-root " ) )
215+ ] )
216+ let client = WordPressLoginClient ( requestExecutor: stubs)
145217 let parsedUrl = try await client. findLoginUrl ( forSite: " https://subdirectory.wpmt.co/index.php?redirect=true " )
146218 #expect( " https://subdirectory.wpmt.co/wordpress/wp-admin/authorize-application.php " == parsedUrl. url ( ) )
147219 }
148220
149221 @Test ( " Login Spec Example 13: Site uses HTTP basic with no provided credentials " )
150222 func testWordPressHttpBasic( ) async throws {
223+ let stubs : HTTPStubs
224+ do {
225+ stubs = HTTPStubs ( stubs: [
226+ try HTTPStubs . stub ( host: " basic-auth.wpmt.co " , with: . responseWithStatus( 401 , headers: [ " WWW-Authenticate " : " Basic realm= \" Restricted \" " ] ) )
227+ ] )
228+ } catch {
229+ Issue . record ( " Failed to create stubs: \( error) " )
230+ return
231+ }
232+ let client = WordPressLoginClient ( requestExecutor: stubs)
233+
151234 await #expect( performing: {
152- _ = try await self . client. findLoginUrl ( forSite: " https://basic-auth.wpmt.co " )
235+ _ = try await client. findLoginUrl ( forSite: " https://basic-auth.wpmt.co " )
153236 } , throws: { error in
154237 let reason = try #require( try self . getRequestExecutionErrorReason ( from: error) )
155238
@@ -168,11 +251,20 @@ class LoginTests {
168251
169252 @Test ( " Login Spec Example 13: Site uses HTTP basic with invalid credentials provided " )
170253 func testWordPressHttpBasicWithInvalidCredentials( ) async throws {
254+ let stubs : HTTPStubs
255+ do {
256+ stubs = HTTPStubs ( stubs: [
257+ try HTTPStubs . stub ( host: " basic-auth.wpmt.co " , with: . responseWithStatus( 401 , headers: [ " WWW-Authenticate " : " Basic realm= \" Restricted \" " ] ) )
258+ ] )
259+ } catch {
260+ Issue . record ( " Failed to create stubs: \( error) " )
261+ return
262+ }
171263 let invalid = ApiDiscoveryAuthenticationMiddleware ( username: " invalid " , password: " invalid " )
172264
173265 await #expect( performing: {
174266 _ = try await WordPressLoginClient (
175- urlSession : . init ( configuration : . ephemeral ) ,
267+ requestExecutor : stubs ,
176268 middleware: MiddlewarePipeline ( middlewares: invalid)
177269 ) . findLoginUrl ( forSite: " https://basic-auth.wpmt.co " )
178270 } , throws: { error in
@@ -193,10 +285,14 @@ class LoginTests {
193285
194286 @Test ( " Login Spec Example 13: Site uses HTTP basic with correct credentials provided " )
195287 func testWordPressHttpBasicWithValidCredentials( ) async throws {
288+ let stubs = HTTPStubs ( stubs: [
289+ try HTTPStubs . stub ( url: " https://basic-auth.wpmt.co/ " , with: . withApiRoot( " https://basic-auth.wpmt.co/wp-json/ " ) ) ,
290+ try HTTPStubs . stub ( url: " https://basic-auth.wpmt.co/wp-json/ " , with: . loginMockResponse( named: " basic-auth-api-root " ) )
291+ ] )
196292 let valid = ApiDiscoveryAuthenticationMiddleware ( username: " test@example.com " , password: " str0ngp4ssw0rd! " )
197293
198294 let parsedUrl = try await WordPressLoginClient (
199- urlSession : . init ( configuration : . ephemeral ) ,
295+ requestExecutor : stubs ,
200296 middleware: MiddlewarePipeline ( middlewares: valid)
201297 ) . findLoginUrl ( forSite: " https://basic-auth.wpmt.co " )
202298
@@ -205,6 +301,13 @@ class LoginTests {
205301
206302 @Test ( " Login Spec Example 14: Custom REST API Prefix " )
207303 func testWordPressCustomRestApiPrefix( ) async throws {
304+ // Site uses a custom REST prefix (e.g., /custom-api/ instead of /wp-json/)
305+ // The Link header points to the custom API root
306+ let stubs = HTTPStubs ( stubs: [
307+ try HTTPStubs . stub ( url: " https://custom-rest-prefix.wpmt.co/ " , with: . withApiRoot( " https://custom-rest-prefix.wpmt.co/custom-api/ " ) ) ,
308+ try HTTPStubs . stub ( url: " https://custom-rest-prefix.wpmt.co/custom-api/ " , with: . loginMockResponse( named: " custom-rest-prefix-api-root " ) )
309+ ] )
310+ let client = WordPressLoginClient ( requestExecutor: stubs)
208311 let parsedUrl = try await client. findLoginUrl ( forSite: " https://custom-rest-prefix.wpmt.co " )
209312 #expect( " https://custom-rest-prefix.wpmt.co/wp-admin/authorize-application.php " == parsedUrl. url ( ) )
210313 }
0 commit comments