- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
196 lines
6.3 KiB
PHP
196 lines
6.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Framework\Http\Status;
|
|
|
|
describe('Batch Endpoint Integration', function () {
|
|
it('handles valid batch request', function () {
|
|
$requestData = [
|
|
'operations' => [
|
|
[
|
|
'componentId' => 'counter:demo',
|
|
'method' => 'increment',
|
|
'params' => ['amount' => 5],
|
|
'operationId' => 'op-1',
|
|
],
|
|
[
|
|
'componentId' => 'stats:user',
|
|
'method' => 'refresh',
|
|
'operationId' => 'op-2',
|
|
],
|
|
],
|
|
];
|
|
|
|
$response = $this->post('/live-component/batch', $requestData);
|
|
|
|
expect($response->status)->toBe(Status::OK);
|
|
|
|
$data = $response->jsonData;
|
|
expect($data)->toHaveKey('results');
|
|
expect($data)->toHaveKey('total_operations');
|
|
expect($data)->toHaveKey('success_count');
|
|
expect($data)->toHaveKey('failure_count');
|
|
|
|
expect($data['total_operations'])->toBe(2);
|
|
expect($data['results'])->toHaveCount(2);
|
|
});
|
|
|
|
it('returns error for invalid batch request format', function () {
|
|
$requestData = [
|
|
'invalid' => 'format',
|
|
];
|
|
|
|
$response = $this->post('/live-component/batch', $requestData);
|
|
|
|
expect($response->status)->toBe(Status::BAD_REQUEST);
|
|
|
|
$data = $response->jsonData;
|
|
expect($data)->toHaveKey('error');
|
|
expect($data)->toHaveKey('error_code');
|
|
expect($data['error_code'])->toBe('INVALID_BATCH_REQUEST');
|
|
});
|
|
|
|
it('returns error for empty operations', function () {
|
|
$requestData = [
|
|
'operations' => [],
|
|
];
|
|
|
|
$response = $this->post('/live-component/batch', $requestData);
|
|
|
|
expect($response->status)->toBe(Status::BAD_REQUEST);
|
|
expect($response->jsonData['error_code'])->toBe('INVALID_BATCH_REQUEST');
|
|
});
|
|
|
|
it('returns error for too many operations', function () {
|
|
$operations = [];
|
|
for ($i = 0; $i < 51; $i++) {
|
|
$operations[] = [
|
|
'componentId' => "component:$i",
|
|
'method' => 'action',
|
|
];
|
|
}
|
|
|
|
$requestData = ['operations' => $operations];
|
|
|
|
$response = $this->post('/live-component/batch', $requestData);
|
|
|
|
expect($response->status)->toBe(Status::BAD_REQUEST);
|
|
expect($response->jsonData['error'])->toContain('cannot exceed 50 operations');
|
|
});
|
|
|
|
it('handles partial failures gracefully', function () {
|
|
$requestData = [
|
|
'operations' => [
|
|
[
|
|
'componentId' => 'counter:demo',
|
|
'method' => 'increment',
|
|
'operationId' => 'op-1',
|
|
],
|
|
[
|
|
'componentId' => 'invalid:component',
|
|
'method' => 'action',
|
|
'operationId' => 'op-2',
|
|
],
|
|
[
|
|
'componentId' => 'stats:user',
|
|
'method' => 'refresh',
|
|
'operationId' => 'op-3',
|
|
],
|
|
],
|
|
];
|
|
|
|
$response = $this->post('/live-component/batch', $requestData);
|
|
|
|
expect($response->status)->toBe(Status::OK);
|
|
|
|
$data = $response->jsonData;
|
|
expect($data['total_operations'])->toBe(3);
|
|
expect($data['success_count'])->toBeGreaterThanOrEqual(1);
|
|
expect($data['failure_count'])->toBeGreaterThanOrEqual(1);
|
|
|
|
// Check that results preserve operation IDs
|
|
$operationIds = array_column($data['results'], 'operationId');
|
|
expect($operationIds)->toContain('op-1');
|
|
expect($operationIds)->toContain('op-2');
|
|
expect($operationIds)->toContain('op-3');
|
|
});
|
|
|
|
it('supports fragment-based updates in batch', function () {
|
|
$requestData = [
|
|
'operations' => [
|
|
[
|
|
'componentId' => 'counter:demo',
|
|
'method' => 'increment',
|
|
'fragments' => ['counter-display'],
|
|
'operationId' => 'op-1',
|
|
],
|
|
],
|
|
];
|
|
|
|
$response = $this->post('/live-component/batch', $requestData);
|
|
|
|
expect($response->status)->toBe(Status::OK);
|
|
|
|
$data = $response->jsonData;
|
|
$result = $data['results'][0];
|
|
|
|
if ($result['success']) {
|
|
// If fragments available, should have fragments instead of html
|
|
if (isset($result['fragments'])) {
|
|
expect($result['fragments'])->toBeArray();
|
|
expect($result)->not->toHaveKey('html');
|
|
}
|
|
}
|
|
});
|
|
|
|
it('preserves state and events in batch response', function () {
|
|
$requestData = [
|
|
'operations' => [
|
|
[
|
|
'componentId' => 'counter:demo',
|
|
'method' => 'increment',
|
|
'params' => ['amount' => 5],
|
|
'operationId' => 'op-1',
|
|
],
|
|
],
|
|
];
|
|
|
|
$response = $this->post('/live-component/batch', $requestData);
|
|
|
|
expect($response->status)->toBe(Status::OK);
|
|
|
|
$result = $response->jsonData['results'][0];
|
|
|
|
if ($result['success']) {
|
|
expect($result)->toHaveKey('state');
|
|
expect($result)->toHaveKey('events');
|
|
expect($result['state'])->toBeArray();
|
|
expect($result['events'])->toBeArray();
|
|
}
|
|
});
|
|
|
|
it('handles concurrent batch requests independently', function () {
|
|
$request1 = [
|
|
'operations' => [
|
|
['componentId' => 'counter:demo', 'method' => 'increment', 'operationId' => 'req1-op1'],
|
|
],
|
|
];
|
|
|
|
$request2 = [
|
|
'operations' => [
|
|
['componentId' => 'stats:user', 'method' => 'refresh', 'operationId' => 'req2-op1'],
|
|
],
|
|
];
|
|
|
|
$response1 = $this->post('/live-component/batch', $request1);
|
|
$response2 = $this->post('/live-component/batch', $request2);
|
|
|
|
expect($response1->status)->toBe(Status::OK);
|
|
expect($response2->status)->toBe(Status::OK);
|
|
|
|
expect($response1->jsonData['results'][0]['operationId'])->toBe('req1-op1');
|
|
expect($response2->jsonData['results'][0]['operationId'])->toBe('req2-op1');
|
|
});
|
|
});
|