Skip to content

Commit e3fc008

Browse files
lucilalucilastancato
andauthored
update CappedPostRanker constructor args to pass optional params last (#42)
Co-authored-by: lucilastancato <lucilastancato@automattic.com>
1 parent c77da04 commit e3fc008

4 files changed

Lines changed: 92 additions & 19 deletions

File tree

.github/workflows/mt.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,4 @@ jobs:
5555
if: github.event_name == 'pull_request'
5656
run: |
5757
git fetch --depth=1 origin $GITHUB_BASE_REF
58-
vendor/bin/infection --coverage=build/logs --threads=$(nproc) --show-mutations --no-interaction --only-covered --only-covering-test-cases --skip-initial-tests --git-diff-lines --git-diff-base=origin/$GITHUB_BASE_REF
58+
vendor/bin/infection --coverage=build/logs --threads=$(nproc) --show-mutations --no-interaction --only-covering-test-cases --only-covering-test-cases --skip-initial-tests --git-diff-lines --git-diff-base=origin/$GITHUB_BASE_REF

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ COMPOSER=$(PHP) $(shell which composer)
3535
INFECTION=vendor/bin/infection
3636
MIN_MSI=50
3737
MIN_COVERED_MSI=50
38-
INFECTION_ARGS=--min-msi=$(MIN_MSI) --min-covered-msi=$(MIN_COVERED_MSI) --threads=$(JOBS) --coverage=build/logs --show-mutations --no-interaction --only-covered --only-covering-test-cases
38+
INFECTION_ARGS=--min-msi=$(MIN_MSI) --min-covered-msi=$(MIN_COVERED_MSI) --threads=$(JOBS) --coverage=build/logs --show-mutations --no-interaction --only-covering-test-cases --only-covering-test-cases
3939

4040
all: test
4141

lib/Tumblr/StreamBuilder/StreamRankers/CappedPostRanker.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,20 +61,20 @@ class CappedPostRanker extends StreamRanker
6161
* CappedPostRanker constructor.
6262
* @param User $user User
6363
* @param string $identity The identity of this ranker
64-
* @param bool $debug Need extra debug infos or not. e.g. Ranking score
6564
* @param bool $cap_desc Whether we are looking for the first violated blog post in descending order (top-down)
6665
* @param int $cap The cap applied (higher values are making the reranking less strict)
6766
* @param string $ranking_context The context that ranking is being applied to e.g. dashboard
6867
* @param bool $panel_allow_ranking Whether the meta key on panel regarding ranking is enabled or not
68+
* @param bool $debug Need extra debug infos or not. e.g. Ranking score
6969
*/
7070
public function __construct(
7171
User $user,
7272
string $identity,
73-
?bool $debug = null,
7473
bool $cap_desc,
7574
int $cap,
7675
string $ranking_context,
77-
bool $panel_allow_ranking
76+
bool $panel_allow_ranking,
77+
?bool $debug = null
7878
) {
7979
parent::__construct($identity);
8080
$this->user = $user;
@@ -115,11 +115,11 @@ public static function from_template(StreamContext $context)
115115
return new self(
116116
$context->get_meta_by_key('user'),
117117
$context->get_current_identity(),
118-
$context->get_meta_by_key('client_meta')->is_panel(),
119118
$context->get_optional_property('cap_desc', true),
120119
$context->get_optional_property('cap', 2),
121120
$context->get_optional_property('ranking_context', 'dashboard'),
122121
$context->get_meta_by_key('allow_ranking'),
122+
$context->get_meta_by_key('client_meta')->is_panel()
123123
);
124124
}
125125

tests/unit/Tumblr/StreamBuilder/StreamRankers/CappedPostRankerTest.php

Lines changed: 86 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,12 @@ protected function setUp(): void
7676
$this->invalid_stream_elements = $this->setup_capped_invalid_stream_elements($blog_ids);
7777

7878
$this->enabled_ranker_instance = $this->getMockBuilder(CappedPostRanker::class)
79-
->setConstructorArgs([$this->mock_user, $this->identity, false, true, 2, 'dashboard', false])
79+
->setConstructorArgs([$this->mock_user, $this->identity, true, 2, 'dashboard', false, false])
8080
->getMock();
8181
$this->enabled_ranker_instance->method('can_rank')->willReturn(true);
8282

8383
$this->disabled_ranker_instance = $this->getMockBuilder(CappedPostRanker::class)
84-
->setConstructorArgs([$this->mock_user, $this->identity, false, true, 2, 'dashboard', false])
84+
->setConstructorArgs([$this->mock_user, $this->identity, true, 2, 'dashboard', false, false])
8585
->getMock();
8686
$this->disabled_ranker_instance->method('can_rank')->willReturn(false);
8787
}
@@ -165,7 +165,7 @@ public function test_disabled_rank(): void
165165
public function test_invalid_elements(): void
166166
{
167167
$this->expectException(TypeMismatchException::class);
168-
$this->invokeMethod($this->enabled_ranker_instance, 'pre_fetch', $this->invalid_stream_elements);
168+
$this->invokeMethod($this->enabled_ranker_instance, 'pre_fetch', [$this->invalid_stream_elements]);
169169
}
170170

171171
/**
@@ -176,7 +176,7 @@ public function test_get_blog_dictionaries(): void
176176
$dictionaries = $this->invokeMethod(
177177
$this->enabled_ranker_instance,
178178
'get_blog_dictionaries',
179-
$this->stream_elements
179+
[$this->stream_elements]
180180
);
181181
$post_to_element = $dictionaries[0];
182182
$blog_to_posts_ids = $dictionaries[1];
@@ -186,28 +186,101 @@ public function test_get_blog_dictionaries(): void
186186
$this->assertSame($post_id, $original_element->get_post_id());
187187
}
188188
# Which posts belong to which blogs
189-
$expected_blog_to_posts_ids = [111 => [1, 2, 3, 4, 5], 222 => [6, 7], 333 => [8]];
190-
$this->assertEquals($blog_to_posts_ids, $expected_blog_to_posts_ids);
189+
$expected_blog_to_posts_ids = [111 => ['1', '2', '3', '4', '5'], 222 => ['6', '7'], 333 => ['8']];
190+
$this->assertSame($expected_blog_to_posts_ids, $blog_to_posts_ids);
191191
$expected_stats_per_blog = [111 => [5, 0], 222 => [2, 0], 333 => [1, 0]];
192-
$this->assertEquals($stats_per_blog, $expected_stats_per_blog);
192+
$this->assertSame($expected_stats_per_blog, $stats_per_blog);
193+
}
194+
195+
/**
196+
* Test that invalidate_post_id properly removes posts from blog_to_posts_ids
197+
*/
198+
public function test_invalidate_post_id_removes_posts(): void
199+
{
200+
$ranker = new CappedPostRanker($this->mock_user, $this->identity, true, 2, 'dashboard', false);
201+
202+
// Get initial blog dictionaries using reflection
203+
$dictionaries = $this->invokeMethod($ranker, 'get_blog_dictionaries', [$this->stream_elements]);
204+
$blog_to_posts_ids = $dictionaries[1];
205+
206+
// Verify initial state
207+
$expected_initial = [111 => ['1', '2', '3', '4', '5'], 222 => ['6', '7'], 333 => ['8']];
208+
$expected_post_counts = [111 => [5, 0], 222 => [2, 0], 333 => [1, 0]];
209+
$this->assertSame($expected_initial, $blog_to_posts_ids);
210+
$this->assertSame($expected_post_counts, $dictionaries[2]);
211+
212+
// Test invalidating a specific post using reflection
213+
$violated_post_id = $this->invokeMethod($ranker, 'invalidate_post_id', ['111', '3', &$blog_to_posts_ids]);
214+
// The method returns null when a specific post ID is provided, but the post should be removed
215+
$this->assertNull($violated_post_id);
216+
217+
// Verify post was removed from blog 111
218+
$this->assertCount(4, $blog_to_posts_ids[111]); // Should have 4 posts instead of 5
219+
$this->assertContains('1', $blog_to_posts_ids[111]);
220+
$this->assertContains('2', $blog_to_posts_ids[111]);
221+
$this->assertContains('4', $blog_to_posts_ids[111]);
222+
$this->assertContains('5', $blog_to_posts_ids[111]);
223+
}
224+
225+
/**
226+
* Test that invalidate_post_id returns the first available post when no specific post is provided
227+
*/
228+
public function test_invalidate_post_id_returns_first_available(): void
229+
{
230+
$ranker = new CappedPostRanker($this->mock_user, $this->identity, true, 2, 'dashboard', false);
231+
232+
$dictionaries = $this->invokeMethod($ranker, 'get_blog_dictionaries', [$this->stream_elements]);
233+
$blog_to_posts_ids = $dictionaries[1];
234+
235+
// Test getting first available post from blog 111 using reflection
236+
$first_post = $this->invokeMethod($ranker, 'invalidate_post_id', ['111', null, &$blog_to_posts_ids]);
237+
$this->assertSame(1, $first_post); // Returns integer, not string
238+
239+
// Verify first post was removed
240+
$this->assertNotContains('1', $blog_to_posts_ids[111]);
241+
$this->assertCount(4, $blog_to_posts_ids[111]);
242+
$this->assertContains('2', $blog_to_posts_ids[111]);
243+
$this->assertContains('3', $blog_to_posts_ids[111]);
244+
$this->assertContains('4', $blog_to_posts_ids[111]);
245+
$this->assertContains('5', $blog_to_posts_ids[111]);
246+
}
247+
248+
/**
249+
* Test that invalidate_post_id handles non-existent posts gracefully
250+
*/
251+
public function test_invalidate_post_id_nonexistent_post(): void
252+
{
253+
$ranker = new CappedPostRanker($this->mock_user, $this->identity, true, 2, 'dashboard', false, false);
254+
255+
$dictionaries = $this->invokeMethod($ranker, 'get_blog_dictionaries', [$this->stream_elements]);
256+
$blog_to_posts_ids = $dictionaries[1];
257+
258+
// Try to invalidate a non-existent post using reflection
259+
$result = $this->invokeMethod($ranker, 'invalidate_post_id', ['111', '999', &$blog_to_posts_ids]);
260+
$this->assertNull($result);
261+
262+
// Verify no changes were made
263+
$this->assertCount(5, $blog_to_posts_ids[111]);
264+
$this->assertContains('1', $blog_to_posts_ids[111]);
265+
$this->assertContains('2', $blog_to_posts_ids[111]);
266+
$this->assertContains('3', $blog_to_posts_ids[111]);
267+
$this->assertContains('4', $blog_to_posts_ids[111]);
268+
$this->assertContains('5', $blog_to_posts_ids[111]);
193269
}
194270

195271
/**
196272
* Helper method to enable testing private and protected methods
197-
* @param object $object The object that the private method belongs to
273+
* @param CappedPostRanker $object The object that the private method belongs to
198274
* @param string $method_name The name of the method we want to test
199275
* @param array $parameters List of parameters passed to the method
200276
* @return mixed Whatever the method would return
201277
* @throws \ReflectionException Something went wrong with reflection
202278
*/
203-
public function invokeMethod(object &$object, string $method_name, array $parameters = [])
279+
public function invokeMethod(CappedPostRanker &$object, string $method_name, array $parameters = [])
204280
{
205-
$reflection = new \ReflectionClass(get_class($object));
281+
$reflection = new \ReflectionClass(CappedPostRanker::class);
206282
$method = $reflection->getMethod($method_name);
207283
$method->setAccessible(true);
208-
if ($method_name = 'get_blog_dictionaries') {
209-
$parameters = [$parameters];
210-
}
211284
return $method->invokeArgs($object, $parameters);
212285
}
213286
}

0 commit comments

Comments
 (0)