mourningdove/t/rename.t

757 lines
24 KiB
Perl
Raw Permalink Normal View History

2026-05-24 01:03:05 +00:00
# t/rename.t
#
# Test user renaming.
#
# Authors:
# Afuna <coder.dw@afunamatata.com>
#
# Copyright (c) 2013-2014 by Dreamwidth Studios, LLC.
#
# This program is free software; you may redistribute it and/or modify it under
# the same terms as Perl itself. For a copy of the license, please reference
# 'perldoc perlartistic' or 'perldoc perlgpl'.
#
use strict;
use warnings;
use Test::More tests => 153;
BEGIN { $LJ::_T_CONFIG = 1; require "$ENV{LJHOME}/cgi-bin/ljlib.pl"; }
use LJ::Test qw( temp_user temp_comm );
use DW::User::Rename;
use DW::RenameToken;
my $create_users = sub {
my %opts = @_;
my $fromu = temp_user();
my $tou = temp_user();
unless ( $opts{match} ) {
my %from_defaults = (
status => 'N',
email => 'from@testemail',
password => 'from',
);
LJ::update_user( $fromu, { %from_defaults, %{ $opts{from_details} || {} } } );
my %to_defaults = (
status => 'N',
email => 'to@testemail',
password => 'to',
);
LJ::update_user( $tou, { %to_defaults, %{ $opts{to_details} || {} } } );
}
$fromu = LJ::load_userid( $fromu->userid ) if $opts{from_details};
$tou = LJ::load_userid( $tou->userid ) if $opts{to_details};
if ( $opts{validated} ) {
LJ::update_user( $fromu, { status => 'A' } );
LJ::update_user( $tou, { status => 'A' } );
}
return ( $fromu, $tou );
};
sub new_token { return DW::RenameToken->create_token( ownerid => $_[0]->id ) }
note("-- personal-to-unregistered, no redirect");
{
my $u = temp_user();
my $fromuid = $u->userid;
my $fromusername = $u->username;
my $tousername = $fromusername . "_renameto";
ok( !LJ::load_user($tousername), "Username '$tousername' is unregistered" );
ok( $u->can_rename_to($tousername), "'" . $u->user . "' can rename to '$tousername'" );
ok(
$u->rename( $tousername, token => new_token($u), redirect => 0 ),
"Rename fromu to a valid unregistered username, no redirect"
);
$u = LJ::load_userid( $u->userid );
is( $u->userid, $fromuid, "Id '#$fromuid' remains the same after rename." );
is( $u->user, $tousername, "fromu is now named '$tousername'" );
}
note("-- personal-to-unregistered, with redirect");
{
my $u = temp_user();
my $fromuid = $u->userid;
my $fromusername = $u->user;
my $tousername = $fromusername . "_renameto";
ok( !LJ::load_user($tousername), "Username '$tousername' is unregistered" );
ok( $u->can_rename_to($tousername), "'" . $u->user . "' can rename to '$tousername'" );
ok(
$u->rename( $tousername, token => new_token($u), redirect => 1 ),
"Rename fromu to a valid unregistered username, with redirect"
);
$u = LJ::load_userid( $u->userid );
is( $u->userid, $fromuid, "Id '#$fromuid' remains the same after rename." );
is( $u->user, $tousername, "fromu is now named '$tousername'" );
my $orig_u = LJ::load_user($fromusername);
ok( $orig_u->is_renamed, "Yup, renamed" );
ok( $orig_u->is_redirect, "Chose to redirect this rename" );
is( $orig_u->get_renamed_user->user,
$tousername, "Confirm redirect from $fromusername to $tousername" );
}
note("-- user-to-user, no redirect");
{
my ( $fromu, $tou ) = $create_users->( match => 1, validated => 1 );
my $fromuid = $fromu->userid;
my $touid = $tou->userid;
my $tousername = $tou->user;
ok( $fromu->rename( $tousername, token => new_token($fromu), redirect => 0 ),
"Rename fromu to existing user $tousername" );
$fromu = LJ::load_userid( $fromu->userid );
$tou = LJ::load_userid( $tou->userid );
is( $fromu->user, $tousername, "Rename fromu to tou, which is under the control of fromu" );
my $ex_user = substr( $tousername, 0, 10 );
like( $tou->user, qr/^ex_$ex_user/, "Moved out of the way." );
is( $fromu->userid, $fromuid, "Id of fromu remains the same after rename." );
is( $tou->userid, $touid, "Id of tou remains the same after rename." );
}
note("-- user-to-user, with redirect");
{
my ( $fromu, $tou ) = $create_users->( match => 1, validated => 1 );
my $fromuid = $fromu->userid;
my $fromusername = $fromu->username;
my $touid = $tou->userid;
my $tousername = $tou->user;
ok( $fromu->rename( $tousername, token => new_token($fromu), redirect => 1 ),
"Rename fromu to existing user $tousername" );
$fromu = LJ::load_userid( $fromu->userid );
$tou = LJ::load_userid( $tou->userid );
is( $fromu->user, $tousername, "Rename fromu to tou, which is under the control of fromu" );
my $ex_user = substr( $tousername, 0, 10 );
like( $tou->user, qr/^ex_$ex_user/, "Moved out of the way." );
is( $fromu->userid, $fromuid, "Id of fromu remains the same after rename." );
is( $tou->userid, $touid, "Id of tou remains the same after rename." );
my $orig_u = LJ::load_user($fromusername);
ok( $orig_u->is_renamed, "Yup, renamed" );
ok( $orig_u->is_redirect, "Chose to redirect this rename" );
is( $orig_u->get_renamed_user->user,
$tousername, "Confirm redirect from $fromusername to $tousername" );
}
note("-- rename opts: deleting relationships");
{
my ($u) = temp_user();
my $tousername = $u->user . "_renameto";
my $watcher = temp_user();
my $truster = temp_user();
my $watched = temp_user();
my $trusted = temp_user();
my $comm = temp_comm();
$watcher->add_edge( $u, watch => { nonotify => 1 } );
$truster->add_edge( $u, trust => { nonotify => 1 } );
$u->add_edge( $watched, watch => { nonotify => 1 } );
$u->add_edge( $trusted, trust => { nonotify => 1 } );
$u->add_edge( $comm, watch => { nonotify => 1 } );
ok( $watcher->watches($u), "User has a watcher." );
ok( $truster->trusts($u), "User has a truster." );
ok( $u->watches($watched), "User watches someone." );
ok( $u->trusts($trusted), "User trusts someone." );
ok( $u->watches($comm), "User watches a comm." );
# no arguments means nothing was deleted
$u->apply_rename_opts();
ok( $watcher->watches($u), "User has a watcher." );
ok( $truster->trusts($u), "User has a truster." );
ok( $u->watches($watched), "User watches someone." );
ok( $u->trusts($trusted), "User trusts someone." );
ok( $u->watches($comm), "User watches a comm." );
$u->apply_rename_opts( del => { del_watched_by => 1 } );
ok( !$watcher->watches($u), "User has no watcher." );
ok( $truster->trusts($u), "User has a truster." );
ok( $u->watches($watched), "User watches someone." );
ok( $u->trusts($trusted), "User trusts someone." );
ok( $u->watches($comm), "User watches a comm." );
$watcher->add_edge( $u, watch => { nonotify => 1 } );
$u->apply_rename_opts( del => { del_trusted_by => 1 } );
ok( $watcher->watches($u), "User has a watcher." );
ok( !$truster->trusts($u), "User has no truster." );
ok( $u->watches($watched), "User watches someone." );
ok( $u->trusts($trusted), "User trusts someone." );
ok( $u->watches($comm), "User watches a comm." );
$truster->add_edge( $u, trust => { nonotify => 1 } );
$u->apply_rename_opts( del => { del_watched => 1 } );
ok( $watcher->watches($u), "User has a watcher." );
ok( $truster->trusts($u), "User has a truster." );
ok( !$u->watches($watched), "User does not watch anyone." );
ok( $u->trusts($trusted), "User trusts someone." );
ok( $u->watches($comm), "User watches a comm." );
$u->add_edge( $watched, watch => { nonotify => 1 } );
$u->apply_rename_opts( del => { del_trusted => 1 } );
ok( $watcher->watches($u), "User has a watcher." );
ok( $truster->trusts($u), "User has a truster." );
ok( $u->watches($watched), "User watches someone." );
ok( !$u->trusts($watched), "User does not trust anyone." );
ok( $u->watches($comm), "User watches a comm." );
$u->add_edge( $trusted, trust => { nonotify => 1 } );
$u->apply_rename_opts( del => { del_communities => 1 } );
ok( $watcher->watches($u), "User has a watcher." );
ok( $truster->trusts($u), "User has a truster." );
ok( $u->watches($watched), "User watches someone." );
ok( $u->trusts($trusted), "User trusts someone." );
ok( !$u->watches($comm), "User does not watch a comm." );
ok(
$u->rename(
$tousername,
token => new_token($u),
redirect => 0,
del_trusted_by => 1,
del_watched_by => 1
),
"Rename, break watchers and trusters"
);
ok( !$watcher->watches($u), "User has no watcher." );
ok( !$truster->trusts($u), "User has no truster." );
ok( $u->watches($watched), "User watches someone." );
ok( $u->trusts($trusted), "User trusts someone." );
ok( !$u->watches($comm), "User does not watch a comm." );
}
note("-- rename opts: breaking email redirection");
TODO: {
local $TODO = "-- rename opts: breaking email redirection";
}
note("-- personal-to-personal, authorization");
{
my ( $fromu, $tou, $tousername );
my %rename_cond = (
status => 'A',
password => 'rename',
email => 'rename@testemail',
);
( $fromu, $tou ) = $create_users->(
from_details => { %rename_cond, email => 'from@testemail' },
to_details => { %rename_cond, email => 'to@testemail' }
);
$tousername = $tou->user;
ok( !$fromu->can_rename_to($tousername),
"Cannot rename fromu to existing user $tousername (because: email)" );
( $fromu, $tou ) = $create_users->(
from_details => { %rename_cond, status => 'N' },
to_details => { %rename_cond, status => 'N' }
);
$tousername = $tou->user;
ok( !$fromu->can_rename_to($tousername),
"Cannot rename fromu to existing user $tousername (because: validation)" );
( $fromu, $tou ) = $create_users->(
from_details => {%rename_cond},
to_details => { %rename_cond, status => 'N' }
);
$tousername = $tou->user;
ok( $fromu->can_rename_to($tousername),
"Can rename fromu to existing user $tousername (at least one user is validated)" );
ok( $fromu->rename( $tousername, token => new_token($fromu) ),
"Renamed fromu to existing user $tousername" );
}
{
my ( $fromu, $tou ) = $create_users->();
my $tousername = $tou->user;
ok( $fromu->can_rename_to( $tousername, force => 1 ),
"Can force rename fromu to existing user $tousername not under their control" );
ok( $fromu->rename( $tousername, token => new_token($fromu), force => 1 ),
"Renamed fromu to existing user $tousername" );
}
TODO: {
local $TODO =
"rename to linked usernames, once we allow one account to control multiple usernames";
}
note("-- user status special casing");
{
my ( $fromu, $tou ) = $create_users->();
my $fromusername = $fromu->username;
my $tousername = $tou->user;
$tou->update_self(
{
clusterid => 0,
statusvis => 'X',
raw => "statusvisdate=NOW()"
}
);
LJ::start_request();
$tou = LJ::load_user( $tousername, 1 );
$fromu = LJ::load_user( $fromusername, 1 );
ok( $fromu->can_rename_to($tousername), "Can always rename to expunged users." );
ok( $fromu->rename( $tousername, token => new_token($fromu) ),
"Rename to expunged user $tousername" );
$fromu = LJ::load_userid( $fromu->userid, 1 );
$tou = LJ::load_userid( $tou->userid, 1 );
is( $fromu->user, $tousername, "Rename fromu to tou, which is under the control of fromu" );
my $ex_user = substr( $tousername, 0, 10 );
like( $tou->user, qr/^ex_$ex_user/, "Moved out of the way." );
}
{
my ( $fromu, $tou ) = $create_users->();
my $fromusername = $fromu->username;
my $tousername = $tou->user;
$tou->set_statusvis("D");
ok( !$fromu->can_rename_to($tousername), "Cannot rename to (nonmatching) deleted users." );
}
{
my ( $fromu, $tou, $tousername );
( $fromu, $tou ) = $create_users->( validated => 1 );
$tousername = $tou->user;
$tou->set_statusvis("S");
ok( !$fromu->can_rename_to($tousername), "Cannot rename to nonmatching suspended users." );
( $fromu, $tou ) = $create_users->( match => 1, validated => 1 );
$tousername = $tou->user;
$tou->set_statusvis("S");
ok( !$fromu->can_rename_to($tousername), "Cannot rename to matching suspended users." );
$tou->set_statusvis("L");
ok( !$fromu->can_rename_to($tousername), "Cannot rename to matching locked users." );
$tou->set_statusvis("M");
ok( !$fromu->can_rename_to($tousername), "Cannot rename to matching memorial users." );
$tou->set_statusvis("O");
ok( !$fromu->can_rename_to($tousername), "Cannot rename to matching read-only users." );
$tou->set_statusvis("R");
ok( !$fromu->can_rename_to($tousername),
"Cannot rename to matching renamed and redirecting users." );
$tou->set_statusvis("V");
ok( $fromu->can_rename_to($tousername), "(reset status)" );
$fromu->set_statusvis("S");
ok( !$fromu->can_rename_to($tousername), "Cannot rename from suspended users." );
}
note("-- username issues");
{
my $fromu = temp_user();
my $fromusername = $fromu->user;
# taken from htdocs/inc/reserved-usernames. Production site may have more
# but these are good enough for testing
my @reserved_names = qw( dw_test ex_test ext_test s_test _test test__test );
foreach my $name (@reserved_names) {
ok( !$fromu->can_rename_to( $name . $fromusername, token => new_token($fromu) ),
"Cannot rename to reserved username '$name'" );
}
# reserved usernames can be force-renamed to
foreach my $name (@reserved_names) {
ok(
$fromu->can_rename_to( $name . $fromusername, token => new_token($fromu), force => 1 ),
"Forced rename to reserved username '$name$fromusername'"
);
}
ok( !$fromu->can_rename_to( $fromu->username, token => new_token($fromu) ),
"Cannot rename to own name" );
}
{
my $fromu = temp_user();
my $fromusername = $fromu->user;
my @invalid_usernames = qw( a.b a!b a\x{123}b );
push @invalid_usernames, "x" x 30;
foreach my $name (@invalid_usernames) {
ok(
!$fromu->rename( $name, token => new_token($fromu) ),
"Cannot rename to invalid username '$name'"
);
}
# invalid usernames cannot be force-renamed to
foreach my $name (@invalid_usernames) {
ok( !$fromu->rename( $name, token => new_token($fromu), force => 1 ),
"Cannot force rename to invalid username '$name'" );
}
}
{
my $fromu = temp_user();
my $tousername = $fromu->user . "-abc";
ok( $fromu->rename( $tousername, token => new_token($fromu) ), "Rename does canonicalization" );
$fromu = LJ::load_userid( $fromu->userid );
is( $fromu->user, LJ::canonical_username($tousername), "Canonicalize away hyphens" );
}
note("-- community-to-unregistered");
{
my $admin = temp_user();
my $fromu = temp_comm();
my $oldusername = $fromu->username;
my $tousername = $fromu->username . "_renameto";
ok( !$admin->can_manage($fromu), "User cannot manage community fromu." );
ok(
!$fromu->can_rename_to( $tousername, user => $admin ),
"Cannot rename community to $tousername (not admin)"
);
LJ::set_rel( $fromu, $admin, "A" );
# FIXME: we shouldn't need to do this!
delete $LJ::REQ_CACHE_REL{ $fromu->userid . "-" . $admin->userid . "-A" };
ok( $admin->can_manage($fromu), "User can manage fromu." );
ok( !LJ::load_user($tousername), "Username '$tousername' is unregistered" );
ok( $fromu->can_rename_to( $tousername, user => $admin ), "Can rename to $tousername" );
ok( $fromu->rename( $tousername, token => new_token($admin), user => $admin ),
"Renamed community to $tousername" );
LJ::update_user( $admin, { status => 'A' } );
ok( $admin->is_validated, "Admin was validated so could rename." );
LJ::update_user( $admin, { status => 'N' } );
ok(
!$admin->is_validated && !$fromu->can_rename_to( $tousername . "_rename", user => $admin ),
"Admin no longer validated; can no longer rename"
);
ok( !$fromu->can_rename_to($tousername),
"Cannot rename community without providing a user doing the renaming" );
my $member = temp_user();
$member->join_community($fromu);
ok( !$fromu->can_rename_to( $oldusername, user => $admin ),
"Cannot rename a community with members" );
$member->leave_community($fromu);
ok(
$fromu->can_rename_to( $oldusername, user => $admin ),
"Can rename community again, no members."
);
}
note("-- community-to-personal");
{
my ( $admin, $tou ) = $create_users->( validated => 1 );
my $fromu = temp_comm();
my $tousername = $tou->user;
# make admin of fromu
LJ::set_rel( $fromu, $admin, "A" );
delete $LJ::REQ_CACHE_REL{ $fromu->userid . "-" . $admin->userid . "-A" };
ok(
!$fromu->can_rename_to( $tousername, user => $admin ),
"Cannot rename fromu to existing user $tousername (tou is a personal journal not under admin's control)"
);
( $admin, $tou ) = $create_users->( match => 1, validated => 1 );
$tousername = $tou->user;
# make admin of fromu
LJ::set_rel( $fromu, $admin, "A" );
delete $LJ::REQ_CACHE_REL{ $fromu->userid . "-" . $admin->userid . "-A" };
ok(
$fromu->can_rename_to( $tousername, user => $admin ),
$admin->user
. " can rename community fromu to existing user $tousername (tou is a personal journal under admin's control)"
);
ok(
$fromu->rename( $tousername, token => new_token($admin), user => $admin ),
$admin->user . " renamed community fromu to existing community $tousername"
);
}
note("-- personal-to-community");
{
my $fromu = temp_user();
LJ::update_user( $fromu, { status => 'A' } );
my $tou = temp_comm();
my $tousername = $tou->user;
# make admin of tou
LJ::set_rel( $tou, $fromu, "A" );
delete $LJ::REQ_CACHE_REL{ $tou->userid . "-" . $fromu->userid . "-A" };
my $member = temp_user();
$member->join_community($tou);
ok( !$fromu->can_rename_to( $tousername, user => $fromu, verbose => 1 ),
"Cannot rename a community with members" );
$member->leave_community($tou);
ok( $fromu->can_rename_to($tousername),
"Can rename to a community under your own control if it has no members." );
ok(
$fromu->rename( $tousername, token => new_token($fromu) ),
"Renamed personal journal fromu to existing community."
);
}
note("-- openid and feeds");
{
my $u = temp_user();
LJ::update_user( $u, { journaltype => 'I' } );
ok( !$u->can_rename_to( $u->user . "_rename" ), "Cannot rename OpenID accounts" );
LJ::update_user( $u, { journaltype => 'F' } );
ok( !$u->can_rename_to( $u->user . "_rename" ), "Cannot rename feed accounts" );
}
note("-- rename token ownership ignored");
{
my $u = temp_user();
my $fromusername = $u->user;
my $tousername = $fromusername . "_renameto";
ok( $u->rename( $tousername, token => new_token( temp_user() ) ),
"Check that rename token ownership is ignored" );
}
note("-- two username swap (personal to personal)");
{
my ( $u1, $u2 ) = $create_users->( match => 1, validated => 1 );
my $u1id = $u1->userid;
my $u2id = $u2->userid;
my $u1sername = $u1->user;
my $u2sername = $u2->user;
ok( $u1sername ne $u2sername, "Not the same username" );
my $token = new_token($u1);
ok(
!$u1->swap_usernames(
$u2, tokens => [ $token, $token ]
),
"Can't swap, token is the same"
);
ok(
$u1->swap_usernames(
$u2, tokens => [ new_token($u1), new_token($u1) ]
),
"Swap usernames"
);
$u1 = LJ::load_userid( $u1->userid );
$u2 = LJ::load_userid( $u2->userid );
is( $u1->user, $u2sername, "Swap usernames of u1 and u2" );
is( $u2->user, $u1sername, "Swap usernames of u2 and u1" );
is( $u1->userid, $u1id, "Id of u1 remains the same after rename." );
is( $u2->userid, $u2id, "Id of u2 remains the same after rename." );
}
note("-- two username swap (personal to personal), one token owned by each user");
{
my ( $u1, $u2 ) = $create_users->( match => 1, validated => 1 );
ok(
$u1->swap_usernames(
$u2, tokens => [ new_token($u1), new_token($u2) ]
),
"Swap usernames with one token owned by each account"
);
}
note("-- two username swap (one user is suspended)");
{
my ( $u1, $u2 ) = $create_users->( match => 1, validated => 1 );
$u2->set_statusvis("S");
my $u1id = $u1->userid;
my $u2id = $u2->userid;
my $u1sername = $u1->user;
my $u2sername = $u2->user;
ok( $u1sername ne $u2sername, "Not the same username" );
ok(
!$u1->swap_usernames(
$u2, tokens => [ new_token($u1), new_token($u1) ]
),
"Cannot swap usernames."
);
$u1 = LJ::load_userid( $u1->userid );
$u2 = LJ::load_userid( $u2->userid );
is( $u1->user, $u1sername, "No swap" );
is( $u2->user, $u2sername, "No swap" );
}
note("-- two username swap personal <=> community ");
{
my $u = temp_user();
LJ::update_user( $u, { status => 'A' } );
my $comm = temp_comm();
my $uname = $u->user;
my $commname = $comm->user;
ok(
!$u->swap_usernames(
$comm, tokens => [ new_token($u), new_token($u) ]
),
"Cannot swap personal and community usernames (not an admin)"
);
# make admin of u
LJ::set_rel( $comm, $u, "A" );
delete $LJ::REQ_CACHE_REL{ $comm->userid . "-" . $u->userid . "-A" };
ok(
$u->swap_usernames(
$comm, tokens => [ new_token($u), new_token($u) ],
),
"Swap personal and community usernames"
);
is( $u->user, $commname, "Swap usernames u => comm" );
is( $comm->user, $uname, "Swap usernames comm => u" );
}
note("-- two username swap personal <=> community (with malice)");
{
my ( $u, $u2 ) = $create_users->( validate => 1 );
my $comm = temp_comm();
my $uname = $u->user;
my $commname = $comm->user;
# make admin of u
LJ::set_rel( $comm, $u, "A" );
delete $LJ::REQ_CACHE_REL{ $comm->userid . "-" . $u->userid . "-A" };
LJ::set_rel( $comm, $u2, "A" );
delete $LJ::REQ_CACHE_REL{ $comm->userid . "-" . $u2->userid . "-A" };
ok(
!$u->swap_usernames(
$u2, tokens => [ new_token($u), new_token($u) ],
),
"Swap usernames with someone not under your control"
);
ok(
!$comm->swap_usernames(
$u2,
user => $u,
tokens => [ new_token($u), new_token($u) ],
),
"Swapping the username of a co-admin"
);
}
note("-- two username swap community <=> personal");
{
my $u = temp_user();
LJ::update_user( $u, { status => 'A' } );
my $comm = temp_comm();
my $uname = $u->user;
my $commname = $comm->user;
ok(
!$comm->swap_usernames(
$u, tokens => [ new_token($u), new_token($u) ]
),
"Cannot swap personal and community usernames (not an admin)"
);
# make admin of u
LJ::set_rel( $comm, $u, "A" );
delete $LJ::REQ_CACHE_REL{ $comm->userid . "-" . $u->userid . "-A" };
ok(
!$comm->swap_usernames(
$u, tokens => [ new_token($u), new_token($u) ]
),
"Cannot swap community and personal when acting on the community"
);
}
note("-- two username swap (community and community)");
{
my $admin = temp_user();
LJ::update_user( $admin, { status => 'A' } );
my $c1 = temp_comm();
my $c2 = temp_comm();
LJ::set_rel( $c1, $admin, "A" );
delete $LJ::REQ_CACHE_REL{ $c1->userid . "-" . $admin->userid . "-A" };
LJ::set_rel( $c2, $admin, "A" );
delete $LJ::REQ_CACHE_REL{ $c2->userid . "-" . $admin->userid . "-A" };
my $c1sername = $c1->user;
my $c2sername = $c2->user;
ok( $c1sername ne $c2sername, "Not the same username" );
ok(
$c1->swap_usernames(
$c2,
user => $admin,
tokens => [ new_token($admin), new_token($admin) ],
),
"Swap community usernames"
);
is( $c1->user, $c2sername, "Swap usernames of c1 and c2" );
is( $c2->user, $c1sername, "Swap usernames of c2 and c1" );
}