'Urlop', 'reply_frequency' => '120', 'body' => "Dzień dobry\r\nWracam jutro", 'start_value' => '2026-06-10T09:37', 'end_value' => '2026-06-24T17:12', 'enabled' => '1', ], $settings); assert_same('Urlop', $rule['subject_prefix']); assert_false($rule['reply_every_message']); assert_same(120, $rule['repeat_minutes']); assert_same("Dzień dobry\nWracam jutro", $rule['body']); assert_same('exact_hour', $rule['schedule_mode']); assert_same('2026-06-10T09:37', $rule['start_value']); assert_true($rule['start_ts'] < $rule['end_ts']); }); test('rule validator accepts da period schedule when backend is da', function (): void { $settingsPath = TEST_TMP . '/validator-da.conf'; test_write($settingsPath, "GLOBAL_AUTORESPONDER_BACKEND=da\n"); $settings = Settings::load($settingsPath); $rule = RuleValidator::validate([ 'subject_prefix' => 'Urlop', 'reply_frequency' => 'every', 'body' => 'Body', 'start_value' => '2026-06-10|morning', 'end_value' => '2026-06-24|evening', 'enabled' => '1', ], $settings); assert_same('directadmin_period', $rule['schedule_mode']); assert_true($rule['reply_every_message']); assert_same(0, $rule['repeat_minutes']); }); test('rule validator rejects header injection and reversed dates', function (): void { $settings = Settings::load(TEST_TMP . '/missing.conf'); try { RuleValidator::validate([ 'subject_prefix' => "Bad\nSubject", 'body' => 'Body', 'start_value' => '2026-06-24T17:00', 'end_value' => '2026-06-10T09:00', ], $settings); } catch (InvalidArgumentException $e) { assert_contains('Przedrostek tematu', $e->getMessage()); return; } throw new RuntimeException('Expected validation failure'); }); test('rule validator rejects unsupported response frequency', function (): void { $settings = Settings::load(TEST_TMP . '/missing-frequency.conf'); try { RuleValidator::validate([ 'subject_prefix' => 'Urlop', 'reply_frequency' => '45', 'body' => 'Body', 'start_value' => '2026-06-10|morning', 'end_value' => '2026-06-24|evening', ], $settings); } catch (InvalidArgumentException $e) { assert_contains('Częstość odpowiedzi', $e->getMessage()); return; } throw new RuntimeException('Expected invalid frequency to fail'); }); test('rule validator rejects start date before current date', function (): void { $settingsPath = TEST_TMP . '/validator-past.conf'; test_write($settingsPath, "GLOBAL_AUTORESPONDER_TIMEZONE=Europe/Warsaw\nGLOBAL_AUTORESPONDER_BACKEND=exim\n"); $settings = Settings::load($settingsPath); $tz = new DateTimeZone($settings->timezone()); $yesterday = (new DateTimeImmutable('yesterday', $tz))->format('Y-m-d'); $tomorrow = (new DateTimeImmutable('tomorrow', $tz))->format('Y-m-d'); try { RuleValidator::validate([ 'subject_prefix' => 'Urlop', 'reply_frequency' => '1440', 'body' => 'Body', 'start_value' => $yesterday . 'T09:00', 'end_value' => $tomorrow . 'T17:00', ], $settings); } catch (InvalidArgumentException $e) { assert_contains('wcześniejsza niż aktualna', $e->getMessage()); return; } throw new RuntimeException('Expected past start date to fail'); });