lockState(hasLinks: false, isActive: true, pausedAt: null); expect($r['locked'])->toBeFalse(); expect($r['unlock_at'])->toBeNull(); expect($r['projected'])->toBeFalse(); }); it('active + links → locked, unlock_at projected from now', function (): void { $now = CarbonImmutable::parse('2026-06-22 16:00:00', 'Europe/Moscow'); // до 21:00 $r = ls()->lockState(hasLinks: true, isActive: true, pausedAt: null, now: $now); expect($r['locked'])->toBeTrue(); expect($r['projected'])->toBeTrue(); // pause-now(16:00) → next21=22.06 21:00 → +24h = 23.06 21:00 expect($r['unlock_at']->toIso8601String())->toBe('2026-06-23T21:00:00+03:00'); }); it('paused within grace → locked, firm unlock_at from paused_at', function (): void { $paused = CarbonImmutable::parse('2026-06-22 16:00:00', 'Europe/Moscow'); $now = CarbonImmutable::parse('2026-06-22 18:00:00', 'Europe/Moscow'); $r = ls()->lockState(hasLinks: true, isActive: false, pausedAt: $paused, now: $now); expect($r['locked'])->toBeTrue(); expect($r['projected'])->toBeFalse(); expect($r['unlock_at']->toIso8601String())->toBe('2026-06-23T21:00:00+03:00'); }); it('paused after grace → not locked', function (): void { $paused = CarbonImmutable::parse('2026-06-20 16:00:00', 'Europe/Moscow'); $now = CarbonImmutable::parse('2026-06-22 18:00:00', 'Europe/Moscow'); // > grace(21.06 21:00) $r = ls()->lockState(hasLinks: true, isActive: false, pausedAt: $paused, now: $now); expect($r['locked'])->toBeFalse(); expect($r['unlock_at'])->toBeNull(); }); it('paused but paused_at null → not locked', function (): void { $r = ls()->lockState(hasLinks: true, isActive: false, pausedAt: null); expect($r['locked'])->toBeFalse(); });