'a@b.com', 'age' => '20']) ->rule('email', 'email') ->rule('age', 'digit'); $this->assertTrue($v->check()); $this->assertSame([], $v->errors()); } public function testRequiredFailsOnEmpty(): void { $v = Validation::factory(['name' => ''])->rule('name', 'required'); $this->assertFalse($v->check()); $this->assertArrayHasKey('name', $v->errors()); } public function testRequiredFailsOnMissingKey(): void { $v = Validation::factory([])->rule('name', 'required'); $this->assertFalse($v->check()); $this->assertArrayHasKey('name', $v->errors()); } public function testEmptyOptionalFieldSkipsOtherRules(): void { // Поле пустое, но required нет — email-правило не должно срабатывать. $v = Validation::factory(['email' => ''])->rule('email', 'email'); $this->assertTrue($v->check()); $this->assertSame([], $v->errors()); } public function testStopsAtFirstErrorPerField(): void { // required и email оба провалились бы, но сообщение должно быть от required. $v = Validation::factory(['email' => '']) ->rule('email', 'required') ->rule('email', 'email'); $this->assertFalse($v->check()); $this->assertStringContainsString('обязательно', $v->error('email')); } public function testEmailRule(): void { $bad = Validation::factory(['email' => 'not-an-email'])->rule('email', 'email'); $this->assertFalse($bad->check()); $ok = Validation::factory(['email' => 'user@example.com'])->rule('email', 'email'); $this->assertTrue($ok->check()); } public function testUrlRule(): void { $bad = Validation::factory(['site' => 'example'])->rule('site', 'url'); $this->assertFalse($bad->check()); $ok = Validation::factory(['site' => 'https://example.com'])->rule('site', 'url'); $this->assertTrue($ok->check()); } public function testNumericRule(): void { $this->assertTrue(Validation::factory(['n' => '3.14'])->rule('n', 'numeric')->check()); $this->assertFalse(Validation::factory(['n' => '12abc'])->rule('n', 'numeric')->check()); } public function testDigitRule(): void { $this->assertTrue(Validation::factory(['n' => '42'])->rule('n', 'digit')->check()); $this->assertFalse(Validation::factory(['n' => '3.14'])->rule('n', 'digit')->check()); } public function testMinLengthRule(): void { $this->assertFalse(Validation::factory(['p' => '123'])->rule('p', 'min_length', [8])->check()); $this->assertTrue(Validation::factory(['p' => '12345678'])->rule('p', 'min_length', [8])->check()); } public function testMaxLengthRule(): void { $this->assertFalse(Validation::factory(['p' => '123456'])->rule('p', 'max_length', [4])->check()); $this->assertTrue(Validation::factory(['p' => '1234'])->rule('p', 'max_length', [4])->check()); } public function testExactLengthRule(): void { $this->assertTrue(Validation::factory(['code' => '1234'])->rule('code', 'exact_length', [4])->check()); $this->assertFalse(Validation::factory(['code' => '12345'])->rule('code', 'exact_length', [4])->check()); } public function testMinLengthCountsMultibyte(): void { // 4 кириллических символа — должно проходить min_length(4). $this->assertTrue(Validation::factory(['p' => 'тест'])->rule('p', 'min_length', [4])->check()); } public function testMatchesRule(): void { $ok = Validation::factory(['pass' => 'secret', 'confirm' => 'secret']) ->rule('confirm', 'matches', ['pass']); $this->assertTrue($ok->check()); $bad = Validation::factory(['pass' => 'secret', 'confirm' => 'other']) ->rule('confirm', 'matches', ['pass']); $this->assertFalse($bad->check()); } public function testInRule(): void { $ok = Validation::factory(['role' => 'admin']) ->rule('role', 'in', [['admin', 'user']]); $this->assertTrue($ok->check()); $bad = Validation::factory(['role' => 'root']) ->rule('role', 'in', [['admin', 'user']]); $this->assertFalse($bad->check()); } public function testRegexRule(): void { $ok = Validation::factory(['slug' => 'hello-world']) ->rule('slug', 'regex', ['/^[a-z\-]+$/']); $this->assertTrue($ok->check()); $bad = Validation::factory(['slug' => 'Hello World']) ->rule('slug', 'regex', ['/^[a-z\-]+$/']); $this->assertFalse($bad->check()); } public function testLabelAppearsInMessage(): void { $v = Validation::factory(['email' => 'bad']) ->label('email', 'E-mail') ->rule('email', 'email'); $v->check(); $this->assertStringContainsString('E-mail', $v->error('email')); } public function testMatchesMessageUsesParamLabel(): void { $v = Validation::factory(['pass' => 'a', 'confirm' => 'b']) ->label('pass', 'Пароль') ->rule('confirm', 'matches', ['pass']); $v->check(); $this->assertStringContainsString('Пароль', $v->error('confirm')); } public function testParamPlaceholderInMessage(): void { $v = Validation::factory(['p' => '1'])->rule('p', 'min_length', [8]); $v->check(); $this->assertStringContainsString('8', $v->error('p')); } public function testErrorReturnsNullForValidField(): void { $v = Validation::factory(['email' => 'a@b.com'])->rule('email', 'email'); $v->check(); $this->assertNull($v->error('email')); } public function testMultipleFieldsCollectErrors(): void { $v = Validation::factory(['email' => 'bad', 'age' => 'x']) ->rule('email', 'email') ->rule('age', 'digit'); $this->assertFalse($v->check()); $this->assertCount(2, $v->errors()); } public function testFluentReturnsStatic(): void { $v = Validation::factory([]); $this->assertSame($v, $v->rule('a', 'required')); $this->assertSame($v, $v->label('a', 'A')); } public function testUnknownRuleThrows(): void { $this->expectException(MyException::class); Validation::factory(['a' => 'x'])->rule('a', 'no_such_rule')->check(); } public function testCheckIsRepeatableAndResetsErrors(): void { $v = Validation::factory(['email' => 'bad'])->rule('email', 'email'); $this->assertFalse($v->check()); $this->assertCount(1, $v->errors()); // Повторный вызов не накапливает ошибки. $this->assertFalse($v->check()); $this->assertCount(1, $v->errors()); } }