Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
32 / 32
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
ExtensionTestingReportController
100.00% covered (success)
100.00%
32 / 32
100.00% covered (success)
100.00%
6 / 6
11
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 template
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 index
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 store
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 update
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
3
 destroy
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3namespace App\Http\Controllers\v2\Admin;
4
5use App\Http\Controllers\Controller;
6use App\Http\Requests\v2\Extension\Admin\CreateTestingReportRequest;
7use App\Http\Requests\v2\Extension\Admin\DeleteTestingReportRequest;
8use App\Http\Requests\v2\Extension\Admin\GetTemplateRequest;
9use App\Http\Requests\v2\Extension\Admin\IndexTestingReportRequest;
10use App\Http\Requests\v2\Extension\Admin\UpdateTestingReportRequest;
11use App\Http\Resources\v2\ExtensionTestingReportResource;
12use App\Http\Services\ExtensionTestingReportService;
13use Illuminate\Auth\Access\AuthorizationException;
14use Illuminate\Http\JsonResponse;
15use Illuminate\Validation\ValidationException;
16use Symfony\Component\HttpKernel\Exception\HttpException;
17
18/**
19 * Extension Testing Report Controller
20 *
21 * Manages QA testing reports for FlyMSG extension artifacts.
22 * Admin-only endpoints for VENGRESO_ADMIN users.
23 */
24class ExtensionTestingReportController extends Controller
25{
26    public function __construct(
27        private ExtensionTestingReportService $service
28    ) {}
29
30    /**
31     * Get the predefined domain/test-case template
32     *
33     * Returns the static template structure (Gmail, LinkedIn, Outlook) used to
34     * seed new testing reports. All test case results are null.
35     *
36     * @param  GetTemplateRequest  $request  Validated admin request
37     *
38     * @response 200 {
39     *   "result": [
40     *     {
41     *       "id": "gmail",
42     *       "label": "Gmail",
43     *       "hostname": "mail.google.com",
44     *       "icon": "mail",
45     *       "isCustom": false,
46     *       "testAreas": [...]
47     *     }
48     *   ]
49     * }
50     */
51    public function template(GetTemplateRequest $request): JsonResponse
52    {
53        return response()->json($this->service->getTemplate());
54    }
55
56    /**
57     * List all extension testing reports
58     *
59     * Returns all reports, optionally filtered by artifact, environment, or status.
60     *
61     * @param  IndexTestingReportRequest  $request  Validated request with optional filters
62     *
63     * @response 200 {
64     *   "result": [
65     *     {
66     *       "id": "507f1f77bcf86cd799439011",
67     *       "name": "Chrome v1.2.3 Staging QA",
68     *       "version_tested": "1.2.3",
69     *       "environment": "staging",
70     *       "artifact_id": "chrome-staging-1.2.3",
71     *       "tester_name": "Jane Doe",
72     *       "created_by": "507f1f77bcf86cd799439012",
73     *       "status": "in_progress",
74     *       "domains": [...],
75     *       "created_at": "2026-03-13T00:00:00.000000Z",
76     *       "updated_at": "2026-03-13T00:00:00.000000Z"
77     *     }
78     *   ]
79     * }
80     */
81    public function index(IndexTestingReportRequest $request): JsonResponse
82    {
83        $filters = array_filter([
84            'artifact_id' => $request->artifactId(),
85            'environment' => $request->environment(),
86            'status' => $request->status(),
87        ]);
88
89        $reports = $this->service->listReports($filters);
90
91        return response()->json(ExtensionTestingReportResource::collection($reports));
92    }
93
94    /**
95     * Create a new extension testing report
96     *
97     * Creates a report for the authenticated admin user. One report per user per artifact.
98     * If domains are not provided, the full template is seeded automatically.
99     *
100     * @param  CreateTestingReportRequest  $request  Validated request data
101     *
102     * @response 201 {
103     *   "result": {
104     *     "id": "507f1f77bcf86cd799439011",
105     *     "name": "Chrome v1.2.3 Staging QA",
106     *     "version_tested": "1.2.3",
107     *     "environment": "staging",
108     *     "artifact_id": "chrome-staging-1.2.3",
109     *     "tester_name": "Jane Doe",
110     *     "created_by": "507f1f77bcf86cd799439012",
111     *     "status": "in_progress",
112     *     "domains": [...],
113     *     "created_at": "2026-03-13T00:00:00.000000Z",
114     *     "updated_at": "2026-03-13T00:00:00.000000Z"
115     *   }
116     * }
117     *
118     * @throws ValidationException When a report already exists for this user/artifact
119     */
120    public function store(CreateTestingReportRequest $request): JsonResponse
121    {
122        try {
123            $report = $this->service->createReport(
124                $request->validated(),
125                (string) $request->user()->id
126            );
127
128            return response()->json(new ExtensionTestingReportResource($report), 201);
129        } catch (ValidationException $e) {
130            return response()->json(['error' => $e->errors()], 422);
131        }
132    }
133
134    /**
135     * Update an existing extension testing report
136     *
137     * Only the report owner can update. Partial updates are supported.
138     *
139     * @param  UpdateTestingReportRequest  $request  Validated request data
140     * @param  string  $id  The report ID
141     *
142     * @response 200 {
143     *   "result": {
144     *     "id": "507f1f77bcf86cd799439011",
145     *     "status": "completed",
146     *     "domains": [...]
147     *   }
148     * }
149     *
150     * @throws AuthorizationException When user is not the report owner
151     */
152    public function update(UpdateTestingReportRequest $request, string $id): JsonResponse
153    {
154        try {
155            $report = $this->service->updateReport(
156                $id,
157                $request->validated(),
158                (string) $request->user()->id
159            );
160
161            return response()->json(new ExtensionTestingReportResource($report));
162        } catch (AuthorizationException $e) {
163            return response()->json(['error' => $e->getMessage()], 403);
164        } catch (HttpException $e) {
165            return response()->json(['error' => $e->getMessage()], $e->getStatusCode());
166        }
167    }
168
169    /**
170     * Delete an extension testing report
171     *
172     * Only the report owner can delete their report.
173     *
174     * @param  DeleteTestingReportRequest  $request  Validated admin request
175     * @param  string  $id  The report ID
176     *
177     * @response 200 {"result": {"message": "Report deleted successfully."}}
178     *
179     * @throws AuthorizationException When user is not the report owner
180     */
181    public function destroy(DeleteTestingReportRequest $request, string $id): JsonResponse
182    {
183        try {
184            $this->service->deleteReport($id, (string) $request->user()->id);
185
186            return response()->json(['message' => 'Report deleted successfully.']);
187        } catch (AuthorizationException $e) {
188            return response()->json(['error' => $e->getMessage()], 403);
189        } catch (HttpException $e) {
190            return response()->json(['error' => $e->getMessage()], $e->getStatusCode());
191        }
192    }
193}