validator = new FieldSchemaValidator(); } private function laserSchema(): array { return $this->validator->normalizeSchema([ ['key' => 'energy', 'label' => 'انرژی', 'type' => 'select', 'required' => true, 'options' => [7, 8, 18]], ['key' => 'pulse', 'label' => 'پالس', 'type' => 'select', 'required' => true, 'options' => [3, 5]], ['key' => 'shots', 'label' => 'شات', 'type' => 'number', 'required' => true], ['key' => 'comment', 'label' => 'توضیح', 'type' => 'text'], ]); } public function testSchemaIsNormalizedAndSortedByOrder(): void { $schema = $this->validator->normalizeSchema([ ['key' => 'shots', 'label' => 'شات', 'type' => 'number', 'sort_order' => 2], ['key' => 'energy', 'label' => 'انرژی', 'type' => 'select', 'sort_order' => 0, 'options' => [7, 18]], ]); self::assertSame(['energy', 'shots'], array_column($schema, 'key')); self::assertFalse($schema[0]['required']); } public function testEmptySchemaBecomesNull(): void { self::assertNull($this->validator->normalizeSchema([])); self::assertNull($this->validator->normalizeSchema(null)); } public function testUnknownFieldTypeIsRejected(): void { $this->expectException(AppException::class); $this->validator->normalizeSchema([['key' => 'x', 'label' => 'ایکس', 'type' => 'colour']]); } public function testSelectFieldWithoutOptionsIsRejected(): void { $this->expectException(AppException::class); $this->validator->normalizeSchema([['key' => 'energy', 'label' => 'انرژی', 'type' => 'select']]); } public function testDuplicateKeyIsRejected(): void { $this->expectException(AppException::class); $this->validator->normalizeSchema([ ['key' => 'energy', 'label' => 'انرژی', 'type' => 'number'], ['key' => 'energy', 'label' => 'دوباره', 'type' => 'number'], ]); } public function testInvalidKeyIsRejected(): void { $this->expectException(AppException::class); $this->validator->normalizeSchema([['key' => 'Energy Level', 'label' => 'انرژی', 'type' => 'number']]); } public function testValidValuesPassAndAreCast(): void { $values = $this->validator->validateValues($this->laserSchema(), [ 'energy' => '18', 'pulse' => 3, 'shots' => '212', 'comment' => ' خوب پیش رفت ', ]); self::assertSame(18, $values['energy']); self::assertSame(3, $values['pulse']); self::assertSame(212, $values['shots']); self::assertSame('خوب پیش رفت', $values['comment']); } /** مقداری که هیچ فیلدی نشانش نمی‌دهد یعنی اپراتور فکر می‌کند چیزی ثبت کرده که دیده نمی‌شود. */ public function testUnknownKeyIsRejected(): void { $this->expectException(AppException::class); $this->validator->validateValues($this->laserSchema(), [ 'energy' => 18, 'pulse' => 3, 'shots' => 10, 'wavelength' => 808, ]); } public function testValueOutsideOptionsIsRejected(): void { $this->expectException(AppException::class); $this->validator->validateValues($this->laserSchema(), [ 'energy' => 99, 'pulse' => 3, 'shots' => 10, ]); } public function testMissingRequiredValueIsRejected(): void { $this->expectException(AppException::class); $this->validator->validateValues($this->laserSchema(), ['energy' => 18, 'pulse' => 3]); } public function testOptionalFieldMayBeOmitted(): void { $values = $this->validator->validateValues($this->laserSchema(), [ 'energy' => 18, 'pulse' => 3, 'shots' => 212, ]); self::assertArrayNotHasKey('comment', $values); } public function testNonNumericValueForNumberFieldIsRejected(): void { $this->expectException(AppException::class); $this->validator->validateValues($this->laserSchema(), [ 'energy' => 18, 'pulse' => 3, 'shots' => 'خیلی', ]); } /** اتاق و پرسنل فرمی ندارند — فرستادن مقدار برایشان خطاست، نه نادیده گرفته شدن. */ public function testValuesForATypeWithoutASchemaAreRejected(): void { $this->expectException(AppException::class); $this->validator->validateValues(null, ['energy' => 18]); } public function testNoSchemaAndNoValuesIsFine(): void { self::assertNull($this->validator->validateValues(null, [])); self::assertNull($this->validator->validateValues(null, null)); } }