minor refactor to simplify removing test users

This commit is contained in:
Spine
2025-05-07 09:11:01 +00:00
parent 044c330a6c
commit 7c0ecff7fa
10 changed files with 97 additions and 118 deletions

View File

@@ -592,8 +592,8 @@ class Artist extends BaseObject implements CollageEntry {
stem = VALUES(stem),
name = VALUES(name),
user_id = VALUES(user_id)
", $discogs->id(), $this->id, (int)($this->homonymCount() == 0),
$discogs->sequence(), $discogs->stem(), $discogs->name(), $this->updateUser->id()
", $discogs->id, $this->id, (int)($this->homonymCount() == 0),
$discogs->sequence(), $discogs->stem(), $discogs->name(), $this->updateUser->id
);
return self::$db->affected_rows();
}
@@ -618,18 +618,17 @@ class Artist extends BaseObject implements CollageEntry {
self::$db->begin_transaction();
// Get the ids of the objects that need to be flushed
$oldId = $old->id();
$oldName = $old->name();
self::$db->prepared_query("
SELECT UserID FROM bookmarks_artists WHERE ArtistID = ?
", $oldId
", $old->id
);
$bookmarkList = self::$db->collect(0, false);
self::$db->prepared_query("
SELECT ca.CollageID
FROM collages_artists AS ca
WHERE ca.ArtistID = ?
", $oldId
", $old->id
);
$artistCollageList = self::$db->collect(0, false);
self::$db->prepared_query("
@@ -637,7 +636,7 @@ class Artist extends BaseObject implements CollageEntry {
FROM torrents_artists ta
INNER JOIN artists_alias aa ON (ta.AliasID = aa.AliasID)
WHERE aa.ArtistID = ?
", $oldId
", $old->id
);
$groupList = self::$db->collect(0, false);
self::$db->prepared_query("
@@ -645,7 +644,7 @@ class Artist extends BaseObject implements CollageEntry {
FROM requests_artists ra
INNER JOIN artists_alias aa ON (ra.AliasID = aa.AliasID)
WHERE aa.ArtistID = ?
", $oldId
", $old->id
);
$requestList = self::$db->collect(0, false);
@@ -656,7 +655,7 @@ class Artist extends BaseObject implements CollageEntry {
INNER JOIN torrents_artists ta USING (GroupID)
INNER JOIN artists_alias aa ON (ta.AliasID = aa.AliasID)
WHERE aa.ArtistID = ?
", $oldId
", $old->id
);
$collageList = self::$db->collect(0, false);
@@ -664,28 +663,28 @@ class Artist extends BaseObject implements CollageEntry {
// if it does not yet exists there. Delete any remaining old ids
// as the new id is already present in the target object.
// In Postgresql this will be handled by a merge statement.
$newId = $this->id();
$newId = $this->id;
self::$db->prepared_query("
UPDATE bookmarks_artists
LEFT JOIN (SELECT UserID FROM bookmarks_artists WHERE ArtistID = ?) X USING (UserID)
SET ArtistID = ?
WHERE ArtistID = ? AND X.UserID IS NULL;
", $newId, $newId, $oldId
", $newId, $newId, $old->id
);
self::$db->prepared_query("
DELETE FROM bookmarks_artists WHERE ArtistID = ?
", $oldId
", $old->id
);
self::$db->prepared_query("
UPDATE collages_artists Old
LEFT JOIN (SELECT CollageID from collages_artists where ArtistID = ?) New using (CollageID)
SET Old.ArtistID = ?
WHERE Old.ArtistID = ? AND New.CollageID IS NULL
", $newId, $newId, $oldId
", $newId, $newId, $old->id
);
self::$db->prepared_query("
DELETE FROM collages_artists WHERE ArtistID = ?
", $oldId
", $old->id
);
// Merge all of this artist's aliases with the new artist
@@ -693,7 +692,7 @@ class Artist extends BaseObject implements CollageEntry {
UPDATE artists_alias SET
ArtistID = ?
WHERE ArtistID = ?
", $newId, $oldId
", $newId, $old->id
);
if ($redirect) {
@@ -722,7 +721,7 @@ class Artist extends BaseObject implements CollageEntry {
);
}
$commMan->merge('artist', $oldId, $newId);
$commMan->merge('artist', $old->id, $newId);
// Cache clearing
self::$cache->delete_multi([array_map(fn ($id) => "notify_artists_$id", $bookmarkList)]);
@@ -740,16 +739,16 @@ class Artist extends BaseObject implements CollageEntry {
// Delete the old artist
self::$db->prepared_query("
DELETE FROM artists_group WHERE ArtistID = ?
", $oldId
", $old->id
);
$affected = self::$db->affected_rows();
self::$db->commit();
$this->logger()->general(
"The artist $oldId ($oldName) was made into a "
"The artist {$old->id} ($oldName) was made into a "
. ($redirect ? "" : "non-")
. "redirecting alias of artist $newId ({$this->name()}) by user {$user->label()}"
);
self::$cache->delete_value("zz_a_$oldId");
self::$cache->delete_value("zz_a_{$old->id}");
$this->flush();
$old->flush();
return $affected;

View File

@@ -22,6 +22,11 @@ class ErrorLog extends BaseManager {
array $request,
array $errorList,
): \Gazelle\ErrorLog {
$len = strlen($trace);
if ($len > 4000) {
$clipped = $len - 4000;
$trace = substr($trace, 0, 4000) . "[...$clipped chars clipped]";
}
$id = $this->pg()->scalar("
merge into error_log using (
select ? as uri,
@@ -51,7 +56,7 @@ class ErrorLog extends BaseManager {
}
/**
* Get an eror log based on its ID
* Get an error log based on its ID
*/
public function findById(int $id): ?\Gazelle\ErrorLog {
$errorId = (int)$this->pg()->scalar("
@@ -62,7 +67,7 @@ class ErrorLog extends BaseManager {
}
/**
* Get an eror log based on its digest
* Get an error log based on its digest
*/
public function findByDigest(string $trace, array $errorList): ?\Gazelle\ErrorLog {
$id = (int)$this->pg()->scalar("

View File

@@ -344,21 +344,24 @@ class User extends BaseObject {
* Twig will use these pieces to construct the markup for their avatar.
*/
public function avatarComponentList(User $viewed): array {
$viewedId = $viewed->id();
if (!isset($this->avatarCache[$viewedId])) {
if (!isset($this->avatarCache[$viewed->id])) {
$donor = new User\Donor($viewed);
$this->avatarCache[$viewedId] = [
$this->avatarCache[$viewed->id] = [
'image' => match ($this->avatarMode()) {
AvatarDisplay::show => $viewed->avatar() ?: USER_DEFAULT_AVATAR,
AvatarDisplay::fallbackSynthetic => $viewed->avatar() ?: (new User\SyntheticAvatar($this))->avatar($viewed->username()),
AvatarDisplay::forceSynthetic => (new User\SyntheticAvatar($this))->avatar($viewed->username()),
AvatarDisplay::none => USER_DEFAULT_AVATAR, /** @phpstan-ignore-line */
AvatarDisplay::show
=> $viewed->avatar() ?: USER_DEFAULT_AVATAR,
AvatarDisplay::fallbackSynthetic
=> $viewed->avatar() ?: (new User\SyntheticAvatar($this))->avatar($viewed->username()),
AvatarDisplay::forceSynthetic
=> (new User\SyntheticAvatar($this))->avatar($viewed->username()),
AvatarDisplay::none /** @phpstan-ignore-line */
=> USER_DEFAULT_AVATAR,
},
'hover' => $donor->avatarHover(),
'text' => $donor->avatarHoverText(),
];
}
return $this->avatarCache[$viewedId];
return $this->avatarCache[$viewed->id];
}
public function banDate(): ?string {
@@ -384,7 +387,7 @@ class User extends BaseObject {
* This method returns a hash of the current modified date
* and state of the audit trail. This is used to verify that
* a staff member is not operating on an out-of-date version
* of a user.
* of a user profile page.
*/
public function checkpoint(): string {
return signature(
@@ -717,7 +720,7 @@ class User extends BaseObject {
* returns PARANOIA_HIDE, PARANOIA_OVERRIDDEN, PARANOIA_ALLOWED
*/
public function propertyVisible(User $viewer, string $property): int {
if ($this->id === $viewer->id()) {
if ($this->id === $viewer->id) {
return PARANOIA_ALLOWED;
}
@@ -852,7 +855,7 @@ class User extends BaseObject {
);
$this->lastRead = self::$db->to_pair('TopicID', 'PostID', false);
}
return $this->lastRead[$thread->id()] ?? 0;
return $this->lastRead[$thread->id] ?? 0;
}
/**
@@ -1660,7 +1663,7 @@ class User extends BaseObject {
}
$this->tokenCache = $tokenCache;
}
return isset($this->tokenCache[$torrent->id()]);
return isset($this->tokenCache[$torrent->id]);
}
/**
@@ -1860,7 +1863,7 @@ class User extends BaseObject {
WHERE uid = ?
AND fid = ?
LIMIT 1;
", $this->id, $torrent->id()
", $this->id, $torrent->id
);
}
@@ -1973,6 +1976,22 @@ class User extends BaseObject {
$username = $this->username();
// Many, but not all, of the associated user tables will drop their entries via foreign key cascades.
// But some won't. If this call fails, you will need to decide what to do about the tables in question.
DB::DB()->prepared_query("
DELETE FROM user_read_forum WHERE user_id = ?
", $this->id
);
self::$db->prepared_query("
DELETE FROM users_stats_daily WHERE UserID = ?
", $this->id
);
self::$db->prepared_query("
DELETE FROM users_stats_monthly WHERE UserID = ?
", $this->id
);
self::$db->prepared_query("
DELETE FROM users_stats_yearly WHERE UserID = ?
", $this->id
);
$affected = parent::remove();
self::$cache->delete_multi([
sprintf(Manager\User::ID_KEY, $this->id),

View File

@@ -107,7 +107,7 @@ class UserCreator extends Base {
'auth_key'
];
$mainArgs = [
(int)$inviter?->id(),
(int)$inviter?->id,
$this->username,
current($this->email),
$this->passHash,
@@ -150,28 +150,6 @@ class UserCreator extends Base {
", $this->id
);
if ($inviter) {
(new Manager\InviteSource())->resolveInviteSource($this->inviteKey, $user);
$inviter->stats()->increment('invited_total');
$user->externalProfile()->modifyProfile($inviterReason);
self::$db->prepared_query("
DELETE FROM invites WHERE InviteKey = ?
", $this->inviteKey
);
}
if (isset($this->inviteKey)) {
self::$db->prepared_query("
UPDATE referral_users SET
UserID = ?,
Active = 1,
Joined = now(),
InviteKey = ''
WHERE InviteKey = ?
", $this->id, $this->inviteKey
);
}
// Log the one or two email addresses known to be associated with the user.
// Each additional previous email address is staggered one second back in the past.
$past = count($this->email);
@@ -220,6 +198,28 @@ class UserCreator extends Base {
", $this->id, "{$attr}-pop");
}
if ($inviter) {
(new Manager\InviteSource())->resolveInviteSource($this->inviteKey, $user);
$inviter->stats()->increment('invited_total');
$user->externalProfile()->modifyProfile($inviterReason);
self::$db->prepared_query("
DELETE FROM invites WHERE InviteKey = ?
", $this->inviteKey
);
}
if (isset($this->inviteKey)) {
self::$db->prepared_query("
UPDATE referral_users SET
Active = 1,
InviteKey = '',
Joined = now(),
UserID = ?
WHERE InviteKey = ?
", $this->id, $this->inviteKey
);
}
self::$db->commit();
(new Tracker())->addUser($user);

View File

@@ -7,7 +7,7 @@ class Discogs extends \Gazelle\Base {
protected array $info;
public function __construct(
protected int $id,
public readonly int $id,
?int $sequence = null,
?string $name = null,
?string $stem = null,

View File

@@ -1,11 +1,5 @@
parameters:
ignoreErrors:
-
message: '#^Strict comparison using \=\=\= between false and false will always evaluate to true\.$#'
identifier: identical.alwaysTrue
count: 1
path: ../app/DB/Mysql.php
-
message: '#^Match expression does not handle remaining value\: string$#'
identifier: match.unhandled
@@ -18,36 +12,6 @@ parameters:
count: 1
path: ../app/Feed.php
-
message: '#^Parameter \#1 \$array of function array_keys expects array, array\|false given\.$#'
identifier: argument.type
count: 2
path: ../app/Search/Torrent.php
-
message: '#^Parameter \#1 \$array of function array_slice expects array, array\|false given\.$#'
identifier: argument.type
count: 1
path: ../app/Search/Torrent.php
-
message: '#^Parameter \#1 \$value of function count expects array\|Countable, array\|false given\.$#'
identifier: argument.type
count: 1
path: ../app/Search/Torrent.php
-
message: '#^If condition is always false\.$#'
identifier: if.alwaysFalse
count: 1
path: ../app/Torrent/Report.php
-
message: '#^Method Gazelle\\Torrent\\Report\:\:torrent\(\) should return Gazelle\\TorrentAbstract\|null but returns Gazelle\\TorrentAbstract\|false\|null\.$#'
identifier: return.type
count: 1
path: ../app/Torrent/Report.php
-
message: '#^Method Gazelle\\Util\\Proxy\:\:fetch\(\) has no return type specified\.$#'
identifier: missingType.return

View File

@@ -582,19 +582,25 @@ class ArtistTest extends TestCase {
$artist = $manager->create('phpunit.' . randomString(12));
$this->artistIdList[] = $artist->id;
$id = -100000 + random_int(1, 100000);
$id = (int)DB::DB()->scalar("
SELECT 1+coalesce(max(artist_discogs_id), 0) FROM artist_discogs
");
$name = 'discogs phpunit ' . randomString(10);
$discogs = new Util\Discogs(
id: $id,
stem: 'discogs phpunit',
name: 'discogs phpunit',
stem: $name,
name: $name,
sequence: 2,
);
$this->assertEquals($id, $discogs->id(), 'artist-discogs-id');
$this->assertEquals('discogs phpunit', $discogs->name(), 'artist-discogs-name');
$this->assertEquals('discogs phpunit', $discogs->stem(), 'artist-discogs-stem');
$this->assertEquals($id, $discogs->id, 'artist-discogs-id');
$this->assertEquals($name, $discogs->name(), 'artist-discogs-name');
$this->assertEquals($name, $discogs->stem(), 'artist-discogs-stem');
$this->assertEquals(2, $discogs->sequence(), 'artist-discogs-sequence');
$artist->setField('discogs', $discogs)->setUpdateUser($this->user)->modify();
$this->assertEquals('discogs phpunit', $artist->discogs()->name(), 'artist-self-discogs-name');
$this->assertTrue(
$artist->setField('discogs', $discogs)->setUpdateUser($this->user)->modify(),
'artist-add-discogs'
);
$this->assertEquals($name, $artist->discogs()->name(), 'artist-self-discogs-name');
$this->assertEquals(1, $artist->removeDiscogsRelation(), 'artist-discogs-remove');
}
}

View File

@@ -34,7 +34,7 @@ class UserCreateTest extends TestCase {
'user-create-staff-notes'
);
$this->assertTrue($this->user->isUnconfirmed(), 'user-create-unconfirmed');
$this->assertStringStartsWith(
$this->assertStringContainsString(
'/static/styles/apollostage/style.css?v=',
(new User\Stylesheet($this->user))->cssUrl(),
'user-create-stylesheet'

View File

@@ -17,14 +17,6 @@ class UserTest extends TestCase {
public function tearDown(): void {
if (isset($this->user)) {
DB::DB()->prepared_query("
DELETE FROM user_read_forum WHERE user_id = ?
", $this->user->id
);
DB::DB()->prepared_query("
DELETE FROM users_stats_daily WHERE UserID = ?
", $this->user->id
);
$this->user->remove();
}
}

View File

@@ -12,13 +12,7 @@ class UsersTest extends TestCase {
public function tearDown(): void {
if (isset($this->userList)) {
foreach ($this->userList as $user) {
if (isset($user)) {
DB::DB()->prepared_query("
DELETE FROM users_stats_daily WHERE UserID = ?
", $user->id
);
$user->remove();
}
$user?->remove();
}
}
}