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,99 @@
<?php
namespace Unit\app\Domain\Users\Services;
use Illuminate\Support\Facades\RateLimiter;
use Leantime\Core\Language as LanguageCore;
use Leantime\Core\Support\Avatarcreator;
use Leantime\Core\UI\Theme as ThemeCore;
use Leantime\Domain\Auth\Services\Auth as AuthService;
use Leantime\Domain\Clients\Repositories\Clients as ClientRepository;
use Leantime\Domain\Files\Services\Files;
use Leantime\Domain\Projects\Repositories\Projects as ProjectRepository;
use Leantime\Domain\Projects\Services\Projects as ProjectService;
use Leantime\Domain\Setting\Services\Setting as SettingService;
use Leantime\Domain\Users\Repositories\Users as UserRepository;
use Leantime\Domain\Users\Services\Users;
use Unit\TestCase;
/**
* Regression guard for the invite-spam rate limit. When an inviter exceeds the per-user cap,
* createUserInvite() must short-circuit and never reach the DB insert — proving the limiter is
* the real backstop for every entry point (web, JSON-RPC, resend all funnel through here).
*/
class InviteRateLimitTest extends TestCase
{
private const INVITER_ID = 4242;
protected function setUp(): void
{
parent::setUp();
// The array cache store persists within a single test run, so clear the keys this test
// touches to keep it independent of ordering and of any prior limiter state.
foreach ($this->limiterKeys() as $key) {
RateLimiter::clear($key);
}
}
protected function tearDown(): void
{
foreach ($this->limiterKeys() as $key) {
RateLimiter::clear($key);
}
parent::tearDown();
}
public function test_create_user_invite_returns_false_and_skips_db_when_user_cap_exceeded(): void
{
session(['userdata' => ['id' => self::INVITER_ID, 'name' => 'Inviter', 'mail' => 'inviter@example.com']]);
// Exhaust the per-user hourly cap (default 10) on the exact key the service computes.
[$userKey] = $this->limiterKeys();
for ($i = 0; $i < 10; $i++) {
RateLimiter::hit($userKey, 3600);
}
// The DB layer must never be touched once the cap is hit.
$userRepo = $this->createMock(UserRepository::class);
$userRepo->expects($this->never())->method('addUser');
$service = new Users(
$userRepo,
$this->createMock(LanguageCore::class),
$this->createMock(ProjectRepository::class),
$this->createMock(ClientRepository::class),
$this->createMock(AuthService::class),
$this->createMock(Files::class),
$this->createMock(Avatarcreator::class),
$this->createMock(SettingService::class),
$this->createMock(ThemeCore::class),
$this->createMock(ProjectService::class),
);
$result = $service->createUserInvite([
'user' => 'newuser@example.com',
'firstname' => 'New',
'lastname' => 'User',
'role' => '20',
]);
$this->assertFalse($result, 'createUserInvite must return false once the invite cap is exceeded');
}
/**
* The user + tenant limiter keys, computed exactly as Users::invitesRateLimited() does.
*
* @return array{0: string, 1: string}
*/
private function limiterKeys(): array
{
$scope = defined('BASE_URL') ? BASE_URL : 'default';
return [
'invites:'.$scope.':user:'.self::INVITER_ID,
'invites:'.$scope.':tenant',
];
}
}