diff --git a/app/Mailman/Exceptions/MailmanServiceException.php b/app/Mailman/Exceptions/MailmanServiceException.php new file mode 100644 index 00000000..56e0f349 --- /dev/null +++ b/app/Mailman/Exceptions/MailmanServiceException.php @@ -0,0 +1,9 @@ +status(); } + /** + * @return LazyCollection + */ + public function members(string $listId): LazyCollection + { + $page = 1; + + return LazyCollection::make(function () use ($listId, $page) { + while (!isset($totalEntries) || ($page - 1) * 10 + 1 <= $totalEntries) { + $response = $this->http()->get('/lists/'.$listId.'/roster/member?page='.$page.'&count=10'); + throw_unless($response->ok(), MailmanServiceException::class, 'Fetching members for listId '.$listId.' failed.'); + /** @var array|null */ + $entries = data_get($response->json(), 'entries'); + throw_if(is_null($entries), MailmanServiceException::class, 'Failed getting member list from response'); + $totalEntries = data_get($response->json(), 'total_size'); + + foreach ($entries as $entry) { + yield $entry['email']; + } + + ++$page; + } + }); + } + private function http(): PendingRequest { return Http::withBasicAuth($this->username, $this->password)->withOptions(['base_uri' => $this->baseUrl]); diff --git a/tests/Unit/Mailman/ServiceTest.php b/tests/Unit/Mailman/ServiceTest.php index b1291c27..30bdff45 100644 --- a/tests/Unit/Mailman/ServiceTest.php +++ b/tests/Unit/Mailman/ServiceTest.php @@ -2,7 +2,9 @@ namespace Tests\Unit\Mailman; +use App\Mailman\Exceptions\MailmanServiceException; use App\Mailman\Support\MailmanService; +use Generator; use Illuminate\Support\Facades\Http; use Tests\TestCase; @@ -35,4 +37,65 @@ class ServiceTest extends TestCase Http::assertSentCount(1); Http::assertSent(fn ($request) => 'GET' === $request->method() && 'http://mailman.test/api/system/versions' === $request->url() && $request->header('Authorization') === ['Basic '.base64_encode('user:secret')]); } + + public function testItGetsMembersFromList(): void + { + Http::fake([ + 'http://mailman.test/api/lists/listid/roster/member?page=1&count=10' => Http::response(json_encode([ + 'entries' => [ + ['email' => 'test@example.com'], + ['email' => 'test2@example.com'], + ], + 'total_size' => 2, + ]), 200), + ]); + + $result = app(MailmanService::class)->setCredentials('http://mailman.test/api/', 'user', 'secret')->members('listid'); + + $this->assertEquals(['test@example.com', 'test2@example.com'], $result->toArray()); + Http::assertSentCount(1); + Http::assertSent(fn ($request) => 'GET' === $request->method() && 'http://mailman.test/api/lists/listid/roster/member?page=1&count=10' === $request->url() && $request->header('Authorization') === ['Basic '.base64_encode('user:secret')]); + } + + public function testItThrowsExceptionWhenLoginFailed(): void + { + $this->expectException(MailmanServiceException::class); + Http::fake([ + 'http://mailman.test/api/lists/listid/roster/member?page=1&count=10' => Http::response('', 401), + ]); + + app(MailmanService::class)->setCredentials('http://mailman.test/api/', 'user', 'secret')->members('listid')->first(); + } + + public function listDataProvider(): Generator + { + foreach (range(3, 40) as $i) { + yield [ + collect(range(1, $i)) + ->map(fn ($num) => ['email' => 'test'.$num.'@example.com']) + ->toArray(), + ]; + } + } + + /** + * @dataProvider listDataProvider + */ + public function testItReturnsMoreThanOneResult(array $totals): void + { + $totals = collect($totals); + foreach ($totals->chunk(10) as $n => $chunk) { + Http::fake([ + 'http://mailman.test/api/lists/listid/roster/member?page='.($n + 1).'&count=10' => Http::response(json_encode([ + 'entries' => $chunk, + 'total_size' => $totals->count(), + ]), 200), + ]); + } + + $result = app(MailmanService::class)->setCredentials('http://mailman.test/api/', 'user', 'secret')->members('listid'); + + $this->assertCount($totals->count(), $result->toArray()); + Http::assertSentCount($totals->chunk(10)->count()); + } }