Skip to content

Commit 879a3d1

Browse files
committed
Update tests
1 parent 71ccbb6 commit 879a3d1

4 files changed

Lines changed: 295 additions & 10 deletions

File tree

tests/Unit/AI/Consent/Application/Consent_Handler/Abstract_Consent_Handler_Test.php

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@
44
// phpcs:disable Yoast.NamingConventions.NamespaceName.MaxExceeded
55
namespace Yoast\WP\SEO\Tests\Unit\AI\Consent\Application\Consent_Handler;
66

7+
use Brain\Monkey;
78
use Mockery;
9+
use WP_User;
10+
use Yoast\WP\SEO\AI\Authorization\Application\Token_Manager;
811
use Yoast\WP\SEO\AI\Consent\Application\Consent_Handler;
12+
use Yoast\WP\SEO\AI\HTTP_Request\Application\Request_Handler;
913
use Yoast\WP\SEO\Helpers\User_Helper;
14+
use Yoast\WP\SEO\Loggers\Logger;
1015
use Yoast\WP\SEO\Tests\Unit\TestCase;
1116

1217
/**
@@ -24,12 +29,33 @@ abstract class Abstract_Consent_Handler_Test extends TestCase {
2429
protected $instance;
2530

2631
/**
27-
* The options helper instance.
32+
* The user helper instance.
2833
*
2934
* @var Mockery\MockInterface|User_Helper
3035
*/
3136
protected $user_helper;
3237

38+
/**
39+
* The token manager instance.
40+
*
41+
* @var Mockery\MockInterface|Token_Manager
42+
*/
43+
protected $token_manager;
44+
45+
/**
46+
* The request handler instance.
47+
*
48+
* @var Mockery\MockInterface|Request_Handler
49+
*/
50+
protected $request_handler;
51+
52+
/**
53+
* The logger instance.
54+
*
55+
* @var Mockery\MockInterface|Logger
56+
*/
57+
protected $logger;
58+
3359
/**
3460
* Setup the test.
3561
*
@@ -38,8 +64,35 @@ abstract class Abstract_Consent_Handler_Test extends TestCase {
3864
protected function setUp(): void {
3965
parent::setUp();
4066

41-
$this->user_helper = Mockery::mock( User_Helper::class );
67+
$this->user_helper = Mockery::mock( User_Helper::class );
68+
$this->token_manager = Mockery::mock( Token_Manager::class );
69+
$this->request_handler = Mockery::mock( Request_Handler::class );
70+
$this->logger = Mockery::mock( Logger::class );
71+
72+
$this->instance = new Consent_Handler(
73+
$this->user_helper,
74+
$this->token_manager,
75+
$this->request_handler,
76+
$this->logger,
77+
);
78+
}
79+
80+
/**
81+
* Stubs WordPress's `get_user_by( 'id', $user_id )` to return a mock WP_User with the given ID.
82+
*
83+
* @param int $user_id The user ID to set on the mock WP_User.
84+
*
85+
* @return WP_User|Mockery\MockInterface The mock WP_User returned by the stubbed `get_user_by`.
86+
*/
87+
protected function stub_get_user_by( int $user_id ) {
88+
$user = Mockery::mock( WP_User::class );
89+
$user->ID = $user_id;
90+
91+
Monkey\Functions\expect( 'get_user_by' )
92+
->once()
93+
->with( 'id', $user_id )
94+
->andReturn( $user );
4295

43-
$this->instance = new Consent_Handler( $this->user_helper );
96+
return $user;
4497
}
4598
}

tests/Unit/AI/Consent/Application/Consent_Handler/Constructor_Test.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
// phpcs:disable Yoast.NamingConventions.NamespaceName.MaxExceeded
55
namespace Yoast\WP\SEO\Tests\Unit\AI\Consent\Application\Consent_Handler;
66

7+
use Yoast\WP\SEO\AI\Authorization\Application\Token_Manager;
8+
use Yoast\WP\SEO\AI\HTTP_Request\Application\Request_Handler;
79
use Yoast\WP\SEO\Helpers\User_Helper;
10+
use Yoast\WP\SEO\Loggers\Logger;
811

912
/**
1013
* Tests the Consent_Handler constructor.
@@ -25,5 +28,17 @@ public function test_constructor() {
2528
User_Helper::class,
2629
$this->getPropertyValue( $this->instance, 'user_helper' ),
2730
);
31+
$this->assertInstanceOf(
32+
Token_Manager::class,
33+
$this->getPropertyValue( $this->instance, 'token_manager' ),
34+
);
35+
$this->assertInstanceOf(
36+
Request_Handler::class,
37+
$this->getPropertyValue( $this->instance, 'request_handler' ),
38+
);
39+
$this->assertInstanceOf(
40+
Logger::class,
41+
$this->getPropertyValue( $this->instance, 'logger' ),
42+
);
2843
}
2944
}

tests/Unit/AI/Consent/Application/Consent_Handler/Grant_Consent_Test.php

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
// phpcs:disable Yoast.NamingConventions.NamespaceName.MaxExceeded
55
namespace Yoast\WP\SEO\Tests\Unit\AI\Consent\Application\Consent_Handler;
66

7+
use Mockery;
8+
use Yoast\WP\SEO\AI\HTTP_Request\Domain\Exceptions\Forbidden_Exception;
9+
use Yoast\WP\SEO\AI\HTTP_Request\Domain\Exceptions\Internal_Server_Error_Exception;
10+
use Yoast\WP\SEO\AI\HTTP_Request\Domain\Request;
11+
712
/**
813
* Tests the Consent_Handler's grant_consent method.
914
*
@@ -14,19 +19,88 @@
1419
final class Grant_Consent_Test extends Abstract_Consent_Handler_Test {
1520

1621
/**
17-
* Tests granting the consent.
22+
* Tests granting the consent on the happy path: token fetched, POST succeeds, local meta updated.
1823
*
1924
* @return void
2025
*/
21-
public function test_grant_consent() {
22-
// Current user ID is used for the consent permission.
26+
public function test_grant_consent_success() {
2327
$user_id = 1;
24-
// Has consent.
28+
$user = $this->stub_get_user_by( $user_id );
29+
30+
$this->token_manager->expects( 'get_or_request_access_token' )
31+
->once()
32+
->with( $user )
33+
->andReturn( 'jwt-token' );
34+
35+
$this->request_handler->expects( 'handle' )
36+
->once()
37+
->with(
38+
Mockery::on(
39+
static function ( $request ) {
40+
return $request instanceof Request
41+
&& $request->get_action_path() === '/user/consent'
42+
&& $request->get_http_method() === Request::METHOD_POST
43+
&& $request->get_headers() === [ 'Authorization' => 'Bearer jwt-token' ]
44+
&& $request->get_body() === [];
45+
},
46+
),
47+
);
48+
2549
$this->user_helper->expects( 'update_meta' )
2650
->once()
2751
->with( $user_id, '_yoast_wpseo_ai_consent', true )
2852
->andReturn( true );
2953

3054
$this->instance->grant_consent( $user_id );
3155
}
56+
57+
/**
58+
* Tests that grant_consent propagates a Forbidden_Exception thrown while fetching the access token
59+
* and does NOT update the local meta.
60+
*
61+
* @return void
62+
*/
63+
public function test_grant_consent_propagates_token_fetch_exception() {
64+
$user_id = 1;
65+
$user = $this->stub_get_user_by( $user_id );
66+
67+
$this->token_manager->expects( 'get_or_request_access_token' )
68+
->once()
69+
->with( $user )
70+
->andThrow( new Forbidden_Exception( 'Forbidden', 403 ) );
71+
72+
// Local meta must NOT be touched on failure.
73+
$this->user_helper->shouldNotReceive( 'update_meta' );
74+
75+
$this->expectException( Forbidden_Exception::class );
76+
77+
$this->instance->grant_consent( $user_id );
78+
}
79+
80+
/**
81+
* Tests that grant_consent propagates an Internal_Server_Error_Exception thrown by the POST call
82+
* and does NOT update the local meta.
83+
*
84+
* @return void
85+
*/
86+
public function test_grant_consent_propagates_remote_exception() {
87+
$user_id = 1;
88+
$user = $this->stub_get_user_by( $user_id );
89+
90+
$this->token_manager->expects( 'get_or_request_access_token' )
91+
->once()
92+
->with( $user )
93+
->andReturn( 'jwt-token' );
94+
95+
$this->request_handler->expects( 'handle' )
96+
->once()
97+
->andThrow( new Internal_Server_Error_Exception( 'Internal Server Error', 500 ) );
98+
99+
// Local meta must NOT be touched on failure.
100+
$this->user_helper->shouldNotReceive( 'update_meta' );
101+
102+
$this->expectException( Internal_Server_Error_Exception::class );
103+
104+
$this->instance->grant_consent( $user_id );
105+
}
32106
}

tests/Unit/AI/Consent/Application/Consent_Handler/Revoke_Consent_Test.php

Lines changed: 146 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44
// phpcs:disable Yoast.NamingConventions.NamespaceName.MaxExceeded
55
namespace Yoast\WP\SEO\Tests\Unit\AI\Consent\Application\Consent_Handler;
66

7+
use Mockery;
8+
use RuntimeException;
9+
use Yoast\WP\SEO\AI\HTTP_Request\Domain\Exceptions\Forbidden_Exception;
10+
use Yoast\WP\SEO\AI\HTTP_Request\Domain\Exceptions\Internal_Server_Error_Exception;
11+
use Yoast\WP\SEO\AI\HTTP_Request\Domain\Exceptions\WP_Request_Exception;
12+
use Yoast\WP\SEO\AI\HTTP_Request\Domain\Request;
13+
714
/**
815
* Tests the Consent_Handler's revoke_consent method.
916
*
@@ -14,13 +21,34 @@
1421
final class Revoke_Consent_Test extends Abstract_Consent_Handler_Test {
1522

1623
/**
17-
* Tests revoking the consent.
24+
* Tests revoking the consent on the happy path: token fetched, DELETE succeeds, local meta deleted.
1825
*
1926
* @return void
2027
*/
21-
public function test_revoke_consent() {
22-
// Current user ID is used for the consent permission.
28+
public function test_revoke_consent_success() {
2329
$user_id = 1;
30+
$user = $this->stub_get_user_by( $user_id );
31+
32+
$this->token_manager->expects( 'get_or_request_access_token' )
33+
->once()
34+
->with( $user )
35+
->andReturn( 'jwt-token' );
36+
37+
$this->request_handler->expects( 'handle' )
38+
->once()
39+
->with(
40+
Mockery::on(
41+
static function ( $request ) {
42+
return $request instanceof Request
43+
&& $request->get_action_path() === '/user/consent'
44+
&& $request->get_http_method() === Request::METHOD_DELETE
45+
&& $request->get_headers() === [ 'Authorization' => 'Bearer jwt-token' ]
46+
&& $request->get_body() === [];
47+
},
48+
),
49+
);
50+
51+
$this->logger->shouldNotReceive( 'warning' );
2452

2553
$this->user_helper->expects( 'delete_meta' )
2654
->once()
@@ -29,4 +57,119 @@ public function test_revoke_consent() {
2957

3058
$this->instance->revoke_consent( $user_id );
3159
}
60+
61+
/**
62+
* Tests that revoke_consent catches a Remote_Request_Exception thrown by the DELETE call,
63+
* logs a warning, and still clears the local meta (security-first).
64+
*
65+
* @return void
66+
*/
67+
public function test_revoke_consent_swallows_remote_exception_on_delete() {
68+
$user_id = 1;
69+
$user = $this->stub_get_user_by( $user_id );
70+
71+
$this->token_manager->expects( 'get_or_request_access_token' )
72+
->once()
73+
->with( $user )
74+
->andReturn( 'jwt-token' );
75+
76+
$this->request_handler->expects( 'handle' )
77+
->once()
78+
->andThrow( new Internal_Server_Error_Exception( 'Internal Server Error', 500 ) );
79+
80+
$this->logger->expects( 'warning' )
81+
->once()
82+
->with( Mockery::type( 'string' ), Mockery::type( 'array' ) );
83+
84+
$this->user_helper->expects( 'delete_meta' )
85+
->once()
86+
->with( $user_id, '_yoast_wpseo_ai_consent' )
87+
->andReturn( true );
88+
89+
$this->instance->revoke_consent( $user_id );
90+
}
91+
92+
/**
93+
* Tests that revoke_consent catches a Remote_Request_Exception thrown while fetching the access
94+
* token, logs a warning, and still clears the local meta.
95+
*
96+
* @return void
97+
*/
98+
public function test_revoke_consent_swallows_remote_exception_on_token_fetch() {
99+
$user_id = 1;
100+
$user = $this->stub_get_user_by( $user_id );
101+
102+
$this->token_manager->expects( 'get_or_request_access_token' )
103+
->once()
104+
->with( $user )
105+
->andThrow( new Forbidden_Exception( 'Forbidden', 403 ) );
106+
107+
// DELETE should not be attempted when the token fetch fails.
108+
$this->request_handler->shouldNotReceive( 'handle' );
109+
110+
$this->logger->expects( 'warning' )
111+
->once()
112+
->with( Mockery::type( 'string' ), Mockery::type( 'array' ) );
113+
114+
$this->user_helper->expects( 'delete_meta' )
115+
->once()
116+
->with( $user_id, '_yoast_wpseo_ai_consent' )
117+
->andReturn( true );
118+
119+
$this->instance->revoke_consent( $user_id );
120+
}
121+
122+
/**
123+
* Tests that revoke_consent catches a WP_Request_Exception (transport-level error) and still
124+
* clears the local meta.
125+
*
126+
* @return void
127+
*/
128+
public function test_revoke_consent_swallows_wp_request_exception() {
129+
$user_id = 1;
130+
$user = $this->stub_get_user_by( $user_id );
131+
132+
$this->token_manager->expects( 'get_or_request_access_token' )
133+
->once()
134+
->with( $user )
135+
->andReturn( 'jwt-token' );
136+
137+
$this->request_handler->expects( 'handle' )
138+
->once()
139+
->andThrow( new WP_Request_Exception( 'WP_HTTP_REQUEST_ERROR' ) );
140+
141+
$this->logger->expects( 'warning' )
142+
->once();
143+
144+
$this->user_helper->expects( 'delete_meta' )
145+
->once()
146+
->with( $user_id, '_yoast_wpseo_ai_consent' )
147+
->andReturn( true );
148+
149+
$this->instance->revoke_consent( $user_id );
150+
}
151+
152+
/**
153+
* Tests that revoke_consent does NOT swallow non-HTTP-layer exceptions: a RuntimeException from
154+
* the token manager must propagate out so that programmer errors stay visible.
155+
*
156+
* @return void
157+
*/
158+
public function test_revoke_consent_does_not_swallow_runtime_exception() {
159+
$user_id = 1;
160+
$user = $this->stub_get_user_by( $user_id );
161+
162+
$this->token_manager->expects( 'get_or_request_access_token' )
163+
->once()
164+
->with( $user )
165+
->andThrow( new RuntimeException( 'unexpected programmer error' ) );
166+
167+
$this->request_handler->shouldNotReceive( 'handle' );
168+
$this->logger->shouldNotReceive( 'warning' );
169+
$this->user_helper->shouldNotReceive( 'delete_meta' );
170+
171+
$this->expectException( RuntimeException::class );
172+
173+
$this->instance->revoke_consent( $user_id );
174+
}
32175
}

0 commit comments

Comments
 (0)