2026-08-24 21:22:44 +03:30
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace Tests\Unit;
|
|
|
|
|
|
|
|
|
|
use App\Services\ConfigService;
|
|
|
|
|
use Carbon\Carbon;
|
|
|
|
|
use Mockery;
|
|
|
|
|
use Tests\TestCase;
|
|
|
|
|
|
|
|
|
|
class ConfigServiceTradingHoursTest extends TestCase
|
|
|
|
|
{
|
|
|
|
|
protected function tearDown(): void
|
|
|
|
|
{
|
|
|
|
|
Mockery::close();
|
|
|
|
|
parent::tearDown();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @param array<string, mixed> $values */
|
|
|
|
|
private function cfg(array $values): ConfigService
|
|
|
|
|
{
|
|
|
|
|
$cfg = Mockery::mock(ConfigService::class)->makePartial();
|
|
|
|
|
$cfg->shouldReceive('get')->andReturnUsing(function (string $key, $default = null) use ($values) {
|
|
|
|
|
return array_key_exists($key, $values) ? $values[$key] : $default;
|
|
|
|
|
});
|
|
|
|
|
$cfg->shouldReceive('getString')->andReturnUsing(function (string $key, string $default = '') use ($values) {
|
|
|
|
|
$value = $values[$key] ?? $default;
|
|
|
|
|
|
|
|
|
|
return trim((string) $value);
|
|
|
|
|
});
|
|
|
|
|
$cfg->shouldReceive('getBool')->andReturnUsing(function (string $key, bool $default = false) use ($values) {
|
|
|
|
|
if (! array_key_exists($key, $values)) {
|
|
|
|
|
return $default;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return filter_var($values[$key], FILTER_VALIDATE_BOOLEAN);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return $cfg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_per_day_schedule_closes_before_global_end_day(): void
|
|
|
|
|
{
|
|
|
|
|
$weekly = json_encode([
|
|
|
|
|
'mode' => 'per_day',
|
|
|
|
|
'days' => [
|
|
|
|
|
'sat' => ['enabled' => true, 'start' => '08:30:00', 'end' => '21:30:00'],
|
|
|
|
|
'sun' => ['enabled' => true, 'start' => '08:30:00', 'end' => '21:30:00'],
|
|
|
|
|
'mon' => ['enabled' => true, 'start' => '08:30:00', 'end' => '21:00:00'],
|
|
|
|
|
'tue' => ['enabled' => true, 'start' => '08:30:00', 'end' => '21:30:00'],
|
|
|
|
|
'wed' => ['enabled' => true, 'start' => '08:30:00', 'end' => '21:30:00'],
|
|
|
|
|
'thu' => ['enabled' => true, 'start' => '08:30:00', 'end' => '21:30:00'],
|
|
|
|
|
'fri' => ['enabled' => true, 'start' => '08:30:00', 'end' => '21:30:00'],
|
|
|
|
|
],
|
|
|
|
|
], JSON_UNESCAPED_UNICODE);
|
|
|
|
|
|
|
|
|
|
$cfg = $this->cfg([
|
|
|
|
|
'close_price' => 'true',
|
|
|
|
|
'startDay' => '08:30',
|
|
|
|
|
'endDay' => '21:30',
|
|
|
|
|
'trading_hours_by_day' => $weekly,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$monday2115 = Carbon::create(2026, 8, 24, 21, 15, 0);
|
|
|
|
|
$monday2059 = Carbon::create(2026, 8, 24, 20, 59, 0);
|
|
|
|
|
|
|
|
|
|
$this->assertTrue($cfg->isOutsideTradingHours($monday2115));
|
|
|
|
|
$this->assertFalse($cfg->isOutsideTradingHours($monday2059));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_disabled_day_is_closed_all_day(): void
|
|
|
|
|
{
|
|
|
|
|
$weekly = json_encode([
|
|
|
|
|
'mode' => 'per_day',
|
|
|
|
|
'days' => [
|
|
|
|
|
'fri' => ['enabled' => false, 'start' => '08:30:00', 'end' => '21:30:00'],
|
|
|
|
|
],
|
|
|
|
|
], JSON_UNESCAPED_UNICODE);
|
|
|
|
|
|
|
|
|
|
$cfg = $this->cfg([
|
|
|
|
|
'close_price' => 'true',
|
|
|
|
|
'startDay' => '08:30',
|
|
|
|
|
'endDay' => '21:30',
|
|
|
|
|
'trading_hours_by_day' => $weekly,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$fridayNoon = Carbon::create(2026, 8, 28, 12, 0, 0);
|
|
|
|
|
|
|
|
|
|
$this->assertTrue($cfg->isOutsideTradingHours($fridayNoon));
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-25 02:29:00 +03:30
|
|
|
public function test_apply_global_trading_hours_marks_closed_without_hiding_status_when_home_price_off(): void
|
2026-08-24 21:22:44 +03:30
|
|
|
{
|
|
|
|
|
$weekly = json_encode([
|
|
|
|
|
'mode' => 'per_day',
|
|
|
|
|
'days' => [
|
|
|
|
|
'mon' => ['enabled' => true, 'start' => '08:30:00', 'end' => '21:00:00'],
|
|
|
|
|
],
|
|
|
|
|
], JSON_UNESCAPED_UNICODE);
|
|
|
|
|
|
|
|
|
|
$cfg = $this->cfg([
|
|
|
|
|
'close_price' => 'true',
|
|
|
|
|
'show_home_price_when_closed' => 'false',
|
2026-08-25 02:29:00 +03:30
|
|
|
'show_last_price' => 'false',
|
2026-08-24 21:22:44 +03:30
|
|
|
'startDay' => '08:30',
|
|
|
|
|
'endDay' => '21:30',
|
|
|
|
|
'trading_hours_by_day' => $weekly,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$now = Carbon::create(2026, 8, 24, 21, 15, 0);
|
|
|
|
|
Carbon::setTestNow($now);
|
|
|
|
|
|
2026-08-25 02:29:00 +03:30
|
|
|
$result = $cfg->applyHomePriceDisplayState([
|
2026-08-24 21:22:44 +03:30
|
|
|
'sell' => 100,
|
|
|
|
|
'buy' => 110,
|
|
|
|
|
'price' => 105,
|
|
|
|
|
'closed' => false,
|
|
|
|
|
'status' => 'open',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$this->assertTrue($result['closed']);
|
|
|
|
|
$this->assertSame('closed', $result['status']);
|
|
|
|
|
|
|
|
|
|
Carbon::setTestNow();
|
|
|
|
|
}
|
2026-08-25 02:29:00 +03:30
|
|
|
|
|
|
|
|
public function test_outside_hours_shows_last_price_when_home_price_when_closed_is_on(): void
|
|
|
|
|
{
|
|
|
|
|
$cfg = $this->cfg([
|
|
|
|
|
'close_price' => 'true',
|
|
|
|
|
'show_home_price_when_closed' => 'true',
|
|
|
|
|
'show_last_price' => 'false',
|
|
|
|
|
'startDay' => '11:00',
|
|
|
|
|
'endDay' => '20:30',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$now = Carbon::create(2026, 8, 25, 2, 3, 0);
|
|
|
|
|
Carbon::setTestNow($now);
|
|
|
|
|
|
|
|
|
|
$result = $cfg->applyHomePriceDisplayState([
|
|
|
|
|
'sell' => 168000000,
|
|
|
|
|
'buy' => 168798112,
|
|
|
|
|
'price' => 168798112,
|
|
|
|
|
'closed' => false,
|
|
|
|
|
'status' => 'open',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$this->assertTrue($result['closed']);
|
|
|
|
|
$this->assertSame('open', $result['status']);
|
|
|
|
|
$this->assertSame(168798112, $result['buy']);
|
|
|
|
|
|
|
|
|
|
Carbon::setTestNow();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_close_price_off_with_no_quote_is_no_quote(): void
|
|
|
|
|
{
|
|
|
|
|
$cfg = $this->cfg([
|
|
|
|
|
'close_price' => 'false',
|
|
|
|
|
'show_last_price' => 'false',
|
|
|
|
|
'show_home_price_when_closed' => 'false',
|
|
|
|
|
'startDay' => '11:00',
|
|
|
|
|
'endDay' => '20:30',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$result = $cfg->applyHomePriceDisplayState([
|
|
|
|
|
'sell' => 0,
|
|
|
|
|
'buy' => 0,
|
|
|
|
|
'price' => 0,
|
|
|
|
|
'closed' => false,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$this->assertFalse($result['closed']);
|
|
|
|
|
$this->assertSame('no_quote', $result['status']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_inside_hours_with_quote_is_open(): void
|
|
|
|
|
{
|
|
|
|
|
$cfg = $this->cfg([
|
|
|
|
|
'close_price' => 'true',
|
|
|
|
|
'show_last_price' => 'false',
|
|
|
|
|
'show_home_price_when_closed' => 'false',
|
|
|
|
|
'startDay' => '11:00',
|
|
|
|
|
'endDay' => '20:30',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$now = Carbon::create(2026, 8, 25, 12, 0, 0);
|
|
|
|
|
Carbon::setTestNow($now);
|
|
|
|
|
|
|
|
|
|
$result = $cfg->applyHomePriceDisplayState([
|
|
|
|
|
'sell' => 100,
|
|
|
|
|
'buy' => 110,
|
|
|
|
|
'price' => 105,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$this->assertFalse($result['closed']);
|
|
|
|
|
$this->assertSame('open', $result['status']);
|
|
|
|
|
|
|
|
|
|
Carbon::setTestNow();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_trading_hours_setting_keys(): void
|
|
|
|
|
{
|
|
|
|
|
$cfg = new ConfigService();
|
|
|
|
|
|
|
|
|
|
$this->assertTrue($cfg->isTradingHoursSettingKey('close_price'));
|
|
|
|
|
$this->assertTrue($cfg->isTradingHoursSettingKey('trading_hours_by_day'));
|
|
|
|
|
$this->assertTrue($cfg->isTradingHoursSettingKey('show_home_price_when_closed'));
|
|
|
|
|
$this->assertFalse($cfg->isTradingHoursSettingKey('name_fa'));
|
|
|
|
|
}
|
2026-08-24 21:22:44 +03:30
|
|
|
}
|