This repository has been archived on 2026-03-09. You can view files and clone it, but cannot push or open issues or pull requests.
php-agentic/tests/Feature/ScanForWorkTest.php
Snider b3cf2a4b7d feat: add ScanForWork action for Forgejo epic scanning
Co-Authored-By: Virgil <virgil@lethean.io>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 14:37:12 +00:00

102 lines
3 KiB
PHP

<?php
/*
* Core PHP Framework
*
* Licensed under the European Union Public Licence (EUPL) v1.2.
* See LICENSE file for details.
*/
declare(strict_types=1);
use Core\Mod\Agentic\Actions\Forge\ScanForWork;
use Core\Mod\Agentic\Services\ForgejoService;
use Illuminate\Support\Facades\Http;
beforeEach(function () {
$this->service = new ForgejoService(
baseUrl: 'https://forge.example.com',
token: 'test-token-abc',
);
$this->app->instance(ForgejoService::class, $this->service);
});
it('finds unchecked children needing coding', function () {
Http::fake([
// List epic issues
'forge.example.com/api/v1/repos/core/app/issues?state=open&type=issues&labels=epic' => Http::response([
[
'number' => 1,
'title' => 'Epic: Build the widget',
'body' => "## Tasks\n- [ ] #2\n- [x] #3\n- [ ] #4",
],
]),
// List PRs — only #4 has a linked PR
'forge.example.com/api/v1/repos/core/app/pulls?state=all' => Http::response([
[
'number' => 10,
'title' => 'Fix for issue 4',
'body' => 'Closes #4',
],
]),
// Child issue #2 (no PR, should be returned)
'forge.example.com/api/v1/repos/core/app/issues/2' => Http::response([
'number' => 2,
'title' => 'Add colour picker',
'body' => 'We need a colour picker component.',
'assignees' => [['login' => 'virgil']],
]),
]);
$items = ScanForWork::run('core', 'app');
expect($items)->toBeArray()->toHaveCount(1);
expect($items[0])->toMatchArray([
'epic_number' => 1,
'issue_number' => 2,
'issue_title' => 'Add colour picker',
'issue_body' => 'We need a colour picker component.',
'assignee' => 'virgil',
'repo_owner' => 'core',
'repo_name' => 'app',
'needs_coding' => true,
'has_pr' => false,
]);
});
it('skips checked items and items with PRs', function () {
Http::fake([
'forge.example.com/api/v1/repos/core/app/issues?state=open&type=issues&labels=epic' => Http::response([
[
'number' => 1,
'title' => 'Epic: Build the widget',
'body' => "- [x] #2\n- [x] #3\n- [ ] #4",
],
]),
'forge.example.com/api/v1/repos/core/app/pulls?state=all' => Http::response([
[
'number' => 10,
'title' => 'Fix for issue 4',
'body' => 'Resolves #4',
],
]),
]);
$items = ScanForWork::run('core', 'app');
expect($items)->toBeArray()->toHaveCount(0);
});
it('returns empty for repos with no epics', function () {
Http::fake([
'forge.example.com/api/v1/repos/core/app/issues?state=open&type=issues&labels=epic' => Http::response([]),
]);
$items = ScanForWork::run('core', 'app');
expect($items)->toBeArray()->toHaveCount(0);
});