OneBot: Leantime 改造版源码(BOM/Univer 表格/AI 接管/品牌替换等)
This commit is contained in:
175
tests/Unit/app/Domain/Reports/Models/ReportPeriodTest.php
Normal file
175
tests/Unit/app/Domain/Reports/Models/ReportPeriodTest.php
Normal file
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\App\Domain\Reports\Models;
|
||||
|
||||
use Carbon\CarbonImmutable;
|
||||
use Leantime\Core\Configuration\Environment;
|
||||
use Leantime\Core\Language;
|
||||
use Leantime\Core\Support\CarbonMacros;
|
||||
use Leantime\Core\Support\DateTimeHelper;
|
||||
use Leantime\Domain\Reports\Models\ReportPeriod;
|
||||
use Unit\TestCase;
|
||||
|
||||
class ReportPeriodTest extends TestCase
|
||||
{
|
||||
use \Codeception\Test\Feature\Stub;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$environmentMock = $this->make(Environment::class, [
|
||||
'defaultTimezone' => 'America/Los_Angeles',
|
||||
'language' => 'en-US',
|
||||
]);
|
||||
app()->instance(Environment::class, $environmentMock);
|
||||
|
||||
$languageMock = $this->createMock(Language::class);
|
||||
$languageMock->method('__')->willReturnCallback(function ($index) {
|
||||
$map = [
|
||||
'language.dateformat' => 'm/d/Y',
|
||||
'language.timeformat' => 'h:i A',
|
||||
];
|
||||
|
||||
return $map[$index] ?? null;
|
||||
});
|
||||
app()->instance(Language::class, $languageMock);
|
||||
|
||||
// User calendar in LA (UTC-7 in summer) so quarter boundaries shift against UTC.
|
||||
CarbonImmutable::mixin(new CarbonMacros(
|
||||
'America/Los_Angeles',
|
||||
'en_US',
|
||||
'm/d/Y',
|
||||
'h:i A'
|
||||
));
|
||||
|
||||
app()->instance(DateTimeHelper::class, new DateTimeHelper);
|
||||
|
||||
CarbonImmutable::setTestNow(CarbonImmutable::parse('2026-07-08 12:00:00', 'UTC'));
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
CarbonImmutable::setTestNow();
|
||||
app()->forgetInstance(DateTimeHelper::class);
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function test_this_quarter_resolves_user_calendar_quarter_in_utc(): void
|
||||
{
|
||||
$period = ReportPeriod::thisQuarter();
|
||||
|
||||
// Q3 2026 in LA: Jul 1 00:00 PDT = Jul 1 07:00 UTC, Sep 30 23:59:59 PDT = Oct 1 06:59:59 UTC.
|
||||
$this->assertSame('2026-07-01 07:00:00', $period->fromDbString());
|
||||
$this->assertSame('2026-10-01 06:59:59', $period->toDbString());
|
||||
$this->assertSame(ReportPeriod::PRESET_THIS_QUARTER, $period->preset);
|
||||
}
|
||||
|
||||
public function test_last_and_next_quarter_presets(): void
|
||||
{
|
||||
$lastQuarter = ReportPeriod::lastQuarter();
|
||||
// Q2 2026 in LA starts Apr 1 00:00 PDT = Apr 1 07:00 UTC.
|
||||
$this->assertSame('2026-04-01 07:00:00', $lastQuarter->fromDbString());
|
||||
$this->assertSame('2026-07-01 06:59:59', $lastQuarter->toDbString());
|
||||
|
||||
$nextQuarter = ReportPeriod::nextQuarter();
|
||||
// Q4 2026 in LA starts Oct 1 00:00 PDT = Oct 1 07:00 UTC.
|
||||
$this->assertSame('2026-10-01 07:00:00', $nextQuarter->fromDbString());
|
||||
// Dec 31 23:59:59 PST (UTC-8) = Jan 1 07:59:59 UTC.
|
||||
$this->assertSame('2027-01-01 07:59:59', $nextQuarter->toDbString());
|
||||
}
|
||||
|
||||
public function test_quarter_follows_user_timezone_across_utc_quarter_boundary(): void
|
||||
{
|
||||
// Jul 1 03:00 UTC is still Jun 30 in LA — the user's "this quarter" is Q2, not Q3.
|
||||
CarbonImmutable::setTestNow(CarbonImmutable::parse('2026-07-01 03:00:00', 'UTC'));
|
||||
|
||||
$period = ReportPeriod::thisQuarter();
|
||||
|
||||
$this->assertSame('2026-04-01 07:00:00', $period->fromDbString());
|
||||
$this->assertSame('2026-07-01 06:59:59', $period->toDbString());
|
||||
}
|
||||
|
||||
public function test_from_request_parses_presets_and_custom_ranges(): void
|
||||
{
|
||||
$preset = ReportPeriod::fromRequest(['preset' => 'lastQuarter']);
|
||||
$this->assertSame(ReportPeriod::PRESET_LAST_QUARTER, $preset->preset);
|
||||
|
||||
$custom = ReportPeriod::fromRequest(['preset' => 'custom', 'from' => '04/01/2026', 'to' => '06/30/2026']);
|
||||
$this->assertSame(ReportPeriod::PRESET_CUSTOM, $custom->preset);
|
||||
$this->assertSame('2026-04-01 07:00:00', $custom->fromDbString());
|
||||
// End of day Jun 30 PDT.
|
||||
$this->assertSame('2026-07-01 06:59:59', $custom->toDbString());
|
||||
}
|
||||
|
||||
public function test_from_request_falls_back_to_this_quarter(): void
|
||||
{
|
||||
$this->assertSame(ReportPeriod::PRESET_THIS_QUARTER, ReportPeriod::fromRequest([])->preset);
|
||||
$this->assertSame(
|
||||
ReportPeriod::PRESET_THIS_QUARTER,
|
||||
ReportPeriod::fromRequest(['preset' => 'custom', 'from' => 'not-a-date', 'to' => '06/30/2026'])->preset
|
||||
);
|
||||
// Inverted range is rejected.
|
||||
$this->assertSame(
|
||||
ReportPeriod::PRESET_THIS_QUARTER,
|
||||
ReportPeriod::fromRequest(['preset' => 'custom', 'from' => '06/30/2026', 'to' => '04/01/2026'])->preset
|
||||
);
|
||||
}
|
||||
|
||||
public function test_prior_period_of_quarter_preset_is_previous_quarter(): void
|
||||
{
|
||||
$prior = ReportPeriod::thisQuarter()->priorPeriod();
|
||||
|
||||
$this->assertSame('2026-04-01 07:00:00', $prior->fromDbString());
|
||||
$this->assertSame('2026-07-01 06:59:59', $prior->toDbString());
|
||||
}
|
||||
|
||||
public function test_prior_period_of_custom_range_is_same_length_before(): void
|
||||
{
|
||||
$custom = ReportPeriod::fromRequest(['preset' => 'custom', 'from' => '06/21/2026', 'to' => '06/30/2026']);
|
||||
$prior = $custom->priorPeriod();
|
||||
|
||||
// Ten-day window directly preceding Jun 21–Jun 30.
|
||||
$this->assertSame('2026-06-11 07:00:00', $prior->fromDbString());
|
||||
$this->assertSame('2026-06-21 06:59:59', $prior->toDbString());
|
||||
}
|
||||
|
||||
public function test_upcoming_horizon_extends_two_quarters_past_period_end(): void
|
||||
{
|
||||
$horizon = ReportPeriod::thisQuarter()->upcomingHorizon();
|
||||
|
||||
// Two quarters past Q3 2026 = end of Q1 2027 (Mar 31 23:59:59 PDT = Apr 1 06:59:59 UTC).
|
||||
$this->assertSame('2027-04-01 06:59:59', $horizon->format('Y-m-d H:i:s'));
|
||||
}
|
||||
|
||||
public function test_contains_is_inclusive_of_bounds(): void
|
||||
{
|
||||
$period = ReportPeriod::thisQuarter();
|
||||
|
||||
$this->assertTrue($period->contains($period->from));
|
||||
$this->assertTrue($period->contains($period->to));
|
||||
$this->assertFalse($period->contains($period->from->subSecond()));
|
||||
$this->assertFalse($period->contains($period->to->addSecond()));
|
||||
}
|
||||
|
||||
public function test_query_string_round_trips(): void
|
||||
{
|
||||
$this->assertSame('preset=thisQuarter', ReportPeriod::thisQuarter()->toQueryString());
|
||||
|
||||
$custom = ReportPeriod::fromRequest(['preset' => 'custom', 'from' => '04/01/2026', 'to' => '06/30/2026']);
|
||||
parse_str($custom->toQueryString(), $params);
|
||||
$roundTripped = ReportPeriod::fromRequest($params);
|
||||
|
||||
$this->assertSame($custom->fromDbString(), $roundTripped->fromDbString());
|
||||
$this->assertSame($custom->toDbString(), $roundTripped->toDbString());
|
||||
}
|
||||
|
||||
public function test_label_carries_quarter_shorthand_for_full_quarters(): void
|
||||
{
|
||||
$this->assertStringStartsWith('Q3 2026 · ', ReportPeriod::thisQuarter()->label());
|
||||
|
||||
$custom = ReportPeriod::fromRequest(['preset' => 'custom', 'from' => '06/21/2026', 'to' => '06/30/2026']);
|
||||
$this->assertStringNotContainsString('Q2', $custom->label());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user