OneBot: Leantime 改造版源码(BOM/Univer 表格/AI 接管/品牌替换等)

This commit is contained in:
wangruiguo
2026-09-03 18:49:20 +08:00
commit d647428529
3501 changed files with 1988906 additions and 0 deletions

View 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 21Jun 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());
}
}