Parse and validate AT Protocol Lexicons with DTO generation for Laravel
at dev 8.9 kB view raw
1<?php 2 3namespace SocialDept\AtpSchema\Tests\Unit\Validation; 4 5use Orchestra\Testbench\TestCase; 6use SocialDept\AtpSchema\Validation\ValidationError; 7use SocialDept\AtpSchema\Validation\ValidationErrorFormatter; 8 9class ValidationErrorFormatterTest extends TestCase 10{ 11 protected ValidationErrorFormatter $formatter; 12 13 protected function setUp(): void 14 { 15 parent::setUp(); 16 17 $this->formatter = new ValidationErrorFormatter(); 18 } 19 20 public function test_it_formats_for_laravel(): void 21 { 22 $errors = [ 23 ValidationError::make('name', 'Name is required'), 24 ValidationError::make('email', 'Email is invalid'), 25 ]; 26 27 $formatted = $this->formatter->formatForLaravel($errors); 28 29 $this->assertEquals([ 30 'name' => ['Name is required'], 31 'email' => ['Email is invalid'], 32 ], $formatted); 33 } 34 35 public function test_it_groups_multiple_errors_per_field(): void 36 { 37 $errors = [ 38 ValidationError::make('name', 'Name is required'), 39 ValidationError::make('name', 'Name must be a string'), 40 ]; 41 42 $formatted = $this->formatter->formatForLaravel($errors); 43 44 $this->assertEquals([ 45 'name' => ['Name is required', 'Name must be a string'], 46 ], $formatted); 47 } 48 49 public function test_it_converts_field_paths(): void 50 { 51 $errors = [ 52 ValidationError::make('$.user.name', 'Invalid name'), 53 ValidationError::make('$.items[0]', 'Invalid item'), 54 ]; 55 56 $formatted = $this->formatter->formatForLaravel($errors); 57 58 $this->assertArrayHasKey('user.name', $formatted); 59 $this->assertArrayHasKey('items.0', $formatted); 60 } 61 62 public function test_it_handles_root_field(): void 63 { 64 $errors = [ 65 ValidationError::make('$', 'Invalid data'), 66 ]; 67 68 $formatted = $this->formatter->formatForLaravel($errors); 69 70 $this->assertArrayHasKey('_root', $formatted); 71 } 72 73 public function test_it_formats_as_messages(): void 74 { 75 $errors = [ 76 ValidationError::make('name', 'Name is required'), 77 ValidationError::make('email', 'Email is invalid'), 78 ]; 79 80 $messages = $this->formatter->formatAsMessages($errors); 81 82 $this->assertEquals(['Name is required', 'Email is invalid'], $messages); 83 } 84 85 public function test_it_formats_with_fields(): void 86 { 87 $errors = [ 88 ValidationError::make('name', 'Name is required'), 89 ValidationError::make('email', 'Email is invalid'), 90 ]; 91 92 $messages = $this->formatter->formatWithFields($errors); 93 94 $this->assertEquals([ 95 'name: Name is required', 96 'email: Email is invalid', 97 ], $messages); 98 } 99 100 public function test_it_formats_detailed(): void 101 { 102 $errors = [ 103 ValidationError::withContext('age', 'Too high', 'max', 100, 150), 104 ]; 105 106 $detailed = $this->formatter->formatDetailed($errors); 107 108 $this->assertEquals([ 109 [ 110 'field' => 'age', 111 'message' => 'Too high', 112 'rule' => 'max', 113 'expected' => 100, 114 'actual' => 150, 115 ], 116 ], $detailed); 117 } 118 119 public function test_it_groups_by_field(): void 120 { 121 $errors = [ 122 ValidationError::make('name', 'Required'), 123 ValidationError::make('name', 'Must be string'), 124 ValidationError::make('email', 'Invalid'), 125 ]; 126 127 $grouped = $this->formatter->groupByField($errors); 128 129 $this->assertCount(2, $grouped); 130 $this->assertCount(2, $grouped['name']); 131 $this->assertCount(1, $grouped['email']); 132 } 133 134 public function test_it_formats_single_error(): void 135 { 136 $error = ValidationError::withContext('age', 'Too high', 'max', 100, 150); 137 138 $formatted = $this->formatter->formatError($error); 139 140 $this->assertStringContainsString('Too high', $formatted); 141 $this->assertStringContainsString('Rule: max', $formatted); 142 $this->assertStringContainsString('Expected: 100', $formatted); 143 $this->assertStringContainsString('Got: 150', $formatted); 144 } 145 146 public function test_it_formats_error_without_context(): void 147 { 148 $error = ValidationError::make('name', 'Required'); 149 150 $formatted = $this->formatter->formatError($error); 151 152 $this->assertEquals('Required', $formatted); 153 } 154 155 public function test_it_creates_summary_for_no_errors(): void 156 { 157 $summary = $this->formatter->createSummary([]); 158 159 $this->assertEquals('No validation errors', $summary); 160 } 161 162 public function test_it_creates_summary_for_single_error(): void 163 { 164 $errors = [ 165 ValidationError::make('name', 'Name is required'), 166 ]; 167 168 $summary = $this->formatter->createSummary($errors); 169 170 $this->assertEquals('Validation failed: Name is required', $summary); 171 } 172 173 public function test_it_creates_summary_for_multiple_errors(): void 174 { 175 $errors = [ 176 ValidationError::make('name', 'Required'), 177 ValidationError::make('email', 'Invalid'), 178 ]; 179 180 $summary = $this->formatter->createSummary($errors); 181 182 $this->assertStringContainsString('2 errors', $summary); 183 $this->assertStringContainsString('2 fields', $summary); 184 } 185 186 public function test_it_creates_summary_for_multiple_errors_same_field(): void 187 { 188 $errors = [ 189 ValidationError::make('name', 'Required'), 190 ValidationError::make('name', 'Must be string'), 191 ]; 192 193 $summary = $this->formatter->createSummary($errors); 194 195 $this->assertStringContainsString('2 errors', $summary); 196 $this->assertStringContainsString('1 fields', $summary); 197 } 198 199 public function test_it_converts_to_json(): void 200 { 201 $errors = [ 202 ValidationError::withRule('name', 'Required', 'required'), 203 ]; 204 205 $json = $this->formatter->toJson($errors); 206 $decoded = json_decode($json, true); 207 208 $this->assertCount(1, $decoded); 209 $this->assertEquals('name', $decoded[0]['field']); 210 $this->assertEquals('Required', $decoded[0]['message']); 211 } 212 213 public function test_it_converts_to_pretty_json(): void 214 { 215 $errors = [ 216 ValidationError::make('name', 'Required'), 217 ]; 218 219 $json = $this->formatter->toPrettyJson($errors); 220 221 $this->assertStringContainsString("\n", $json); 222 $this->assertStringContainsString(' ', $json); 223 } 224 225 public function test_it_formats_null_value(): void 226 { 227 $error = ValidationError::withContext('field', 'message', 'type', 'string', null); 228 229 $formatted = $this->formatter->formatError($error); 230 231 $this->assertStringContainsString('Got: null', $formatted); 232 } 233 234 public function test_it_formats_boolean_values(): void 235 { 236 $error = ValidationError::withContext('field', 'message', 'type', true, false); 237 238 $formatted = $this->formatter->formatError($error); 239 240 $this->assertStringContainsString('Expected: true', $formatted); 241 $this->assertStringContainsString('Got: false', $formatted); 242 } 243 244 public function test_it_formats_array_value(): void 245 { 246 $error = ValidationError::withContext('field', 'message', 'type', 'string', [1, 2, 3]); 247 248 $formatted = $this->formatter->formatError($error); 249 250 $this->assertStringContainsString('Got: array(3)', $formatted); 251 } 252 253 public function test_it_formats_long_string(): void 254 { 255 $longString = str_repeat('a', 100); 256 $error = ValidationError::withContext('field', 'message', 'type', 'short', $longString); 257 258 $formatted = $this->formatter->formatError($error); 259 260 $this->assertStringContainsString('...', $formatted); 261 } 262 263 public function test_it_handles_empty_errors_array(): void 264 { 265 $formatted = $this->formatter->formatForLaravel([]); 266 267 $this->assertEmpty($formatted); 268 } 269 270 public function test_it_formats_nested_field_paths(): void 271 { 272 $errors = [ 273 ValidationError::make('$.user.profile.bio', 'Too long'), 274 ]; 275 276 $formatted = $this->formatter->formatForLaravel($errors); 277 278 $this->assertArrayHasKey('user.profile.bio', $formatted); 279 } 280 281 public function test_it_formats_array_index_paths(): void 282 { 283 $errors = [ 284 ValidationError::make('$.items[0].name', 'Required'), 285 ValidationError::make('$.items[1].name', 'Required'), 286 ]; 287 288 $formatted = $this->formatter->formatForLaravel($errors); 289 290 $this->assertArrayHasKey('items.0.name', $formatted); 291 $this->assertArrayHasKey('items.1.name', $formatted); 292 } 293}