2023-07-27 05:31:52 +00:00
|
|
|
|
/*
|
2024-02-13 15:59:27 +00:00
|
|
|
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
2023-07-27 05:31:52 +00:00
|
|
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
*/
|
|
|
|
|
|
2023-04-12 04:20:16 +00:00
|
|
|
|
process.env.NODE_ENV = 'test';
|
|
|
|
|
|
|
|
|
|
import * as assert from 'assert';
|
|
|
|
|
import { inspect } from 'node:util';
|
2024-03-03 11:15:35 +00:00
|
|
|
|
import { api, post, role, signup, successfulApiCall, uploadFile } from '../utils.js';
|
2023-04-12 04:20:16 +00:00
|
|
|
|
import type * as misskey from 'misskey-js';
|
2024-09-28 00:55:21 +00:00
|
|
|
|
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
2023-04-12 04:20:16 +00:00
|
|
|
|
|
|
|
|
|
describe('ユーザー', () => {
|
|
|
|
|
// エンティティとしてのユーザーを主眼においたテストを記述する
|
|
|
|
|
// (Userを返すエンドポイントとUserエンティティを書き換えるエンドポイントをテストする)
|
|
|
|
|
|
|
|
|
|
const stripUndefined = <T extends { [key: string]: any }, >(orig: T): Partial<T> => {
|
|
|
|
|
return Object.entries({ ...orig })
|
|
|
|
|
.filter(([, value]) => value !== undefined)
|
|
|
|
|
.reduce((obj: Partial<T>, [key, value]) => {
|
|
|
|
|
obj[key as keyof T] = value;
|
|
|
|
|
return obj;
|
|
|
|
|
}, {});
|
|
|
|
|
};
|
|
|
|
|
|
2024-03-03 11:15:35 +00:00
|
|
|
|
const show = async (id: string, me = root): Promise<misskey.entities.UserDetailed> => {
|
|
|
|
|
return successfulApiCall({ endpoint: 'users/show', parameters: { userId: id }, user: me });
|
2023-04-12 04:20:16 +00:00
|
|
|
|
};
|
|
|
|
|
|
2023-04-13 16:10:36 +00:00
|
|
|
|
// UserLiteのキーが過不足なく入っている?
|
2024-03-03 11:15:35 +00:00
|
|
|
|
const userLite = (user: misskey.entities.UserLite): Partial<misskey.entities.UserLite> => {
|
2023-04-12 04:20:16 +00:00
|
|
|
|
return stripUndefined({
|
|
|
|
|
id: user.id,
|
|
|
|
|
name: user.name,
|
|
|
|
|
username: user.username,
|
|
|
|
|
host: user.host,
|
|
|
|
|
avatarUrl: user.avatarUrl,
|
|
|
|
|
avatarBlurhash: user.avatarBlurhash,
|
2023-10-21 09:38:07 +00:00
|
|
|
|
avatarDecorations: user.avatarDecorations,
|
2023-04-12 04:20:16 +00:00
|
|
|
|
isBot: user.isBot,
|
|
|
|
|
isCat: user.isCat,
|
2023-10-04 02:35:58 +00:00
|
|
|
|
speakAsCat: user.speakAsCat,
|
2023-04-12 04:20:16 +00:00
|
|
|
|
instance: user.instance,
|
|
|
|
|
emojis: user.emojis,
|
|
|
|
|
onlineStatus: user.onlineStatus,
|
|
|
|
|
badgeRoles: user.badgeRoles,
|
|
|
|
|
|
|
|
|
|
// BUG isAdmin/isModeratorはUserLiteではなくMeDetailedOnlyに含まれる。
|
|
|
|
|
isAdmin: undefined,
|
|
|
|
|
isModerator: undefined,
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2023-04-13 16:10:36 +00:00
|
|
|
|
// UserDetailedNotMeのキーが過不足なく入っている?
|
2024-03-03 11:15:35 +00:00
|
|
|
|
const userDetailedNotMe = (user: misskey.entities.SignupResponse): Partial<misskey.entities.UserDetailedNotMe> => {
|
2023-04-12 04:20:16 +00:00
|
|
|
|
return stripUndefined({
|
|
|
|
|
...userLite(user),
|
|
|
|
|
url: user.url,
|
|
|
|
|
uri: user.uri,
|
enhance: account migration (#10592)
* copy block and mute then create follow and unfollow jobs
* copy block and mute and update lists when detecting an account has moved
* no need to care promise orders
* refactor updating actor and target
* automatically accept if a locked account had accepted an old account
* fix exception format
* prevent the old account from calling some endpoints
* do not unfollow when moving
* adjust following and follower counts
* check movedToUri when receiving a follow request
* skip if no need to adjust
* Revert "disable account migration"
This reverts commit 2321214c98591bcfe1385c1ab5bf0ff7b471ae1d.
* fix translation specifier
* fix checking alsoKnownAs and uri
* fix updating account
* fix refollowing locked account
* decrease followersCount if followed by the old account
* adjust following and followers counts when unfollowing
* fix copying mutings
* prohibit moved account from moving again
* fix move service
* allow app creation after moving
* fix lint
* remove unnecessary field
* fix cache update
* add e2e test
* add e2e test of accepting the new account automatically
* force follow if any error happens
* remove unnecessary joins
* use Array.map instead of for const of
* ユーザーリストの移行は追加のみを行う
* nanka iroiro
* fix misskey-js?
* :v:
* 移行を行ったアカウントからのフォローリクエストの自動許可を調整
* newUriを外に出す
* newUriを外に出す2
* clean up
* fix newUri
* prevent moving if the destination account has already moved
* set alsoKnownAs via /i/update
* fix database initialization
* add return type
* prohibit updating alsoKnownAs after moving
* skip to add to alsoKnownAs if toUrl is known
* skip adding to the list if it already has
* use Acct.parse instead
* rename error code
* :art:
* 制限を5から10に緩和
* movedTo(Uri), alsoKnownAsはユーザーidを返すように
* test api res
* fix
* 元アカウントはミュートし続ける
* :art:
* unfollow
* fix
* getUserUriをUserEntityServiceに
* ?
* job!
* :art:
* instance => server
* accountMovedShort, forbiddenBecauseYouAreMigrated
* accountMovedShort
* fix test
* import, pin禁止
* 実績を凍結する
* clean up
* :v:
* change message
* ブロック, フォロー, ミュート, リストのインポートファイルの制限を32MiBに
* Revert "ブロック, フォロー, ミュート, リストのインポートファイルの制限を32MiBに"
This reverts commit 3bd7be35d8aa455cb01ae58f8172a71a50485db1.
* validateAlsoKnownAs
* 移行後2時間以内はインポート可能なファイルサイズを拡大
* clean up
* どうせactorをupdatePersonで更新するならupdatePersonしか移行処理を発行しないことにする
* handle error?
* リモートからの移行処理の条件を是正
* log, port
* fix
* fix
* enhance(dev): non-production環境でhttpサーバー間でもユーザー、ノートの連合が可能なように
* refactor (use checkHttps)
* MISSKEY_WEBFINGER_USE_HTTP
* Environment Variable readme
* NEVER USE IN PRODUCTION
* fix punyHost
* fix indent
* fix
* experimental
---------
Co-authored-by: tamaina <tamaina@hotmail.co.jp>
Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2023-04-29 15:09:29 +00:00
|
|
|
|
movedTo: user.movedTo,
|
2023-04-12 04:20:16 +00:00
|
|
|
|
alsoKnownAs: user.alsoKnownAs,
|
|
|
|
|
createdAt: user.createdAt,
|
|
|
|
|
updatedAt: user.updatedAt,
|
|
|
|
|
lastFetchedAt: user.lastFetchedAt,
|
|
|
|
|
bannerUrl: user.bannerUrl,
|
|
|
|
|
bannerBlurhash: user.bannerBlurhash,
|
2023-10-06 00:32:09 +00:00
|
|
|
|
backgroundUrl: user.backgroundUrl,
|
|
|
|
|
backgroundBlurhash: user.backgroundBlurhash,
|
2023-04-12 04:20:16 +00:00
|
|
|
|
isLocked: user.isLocked,
|
|
|
|
|
isSilenced: user.isSilenced,
|
|
|
|
|
isSuspended: user.isSuspended,
|
|
|
|
|
description: user.description,
|
|
|
|
|
location: user.location,
|
|
|
|
|
birthday: user.birthday,
|
|
|
|
|
lang: user.lang,
|
|
|
|
|
fields: user.fields,
|
2023-09-21 02:58:51 +00:00
|
|
|
|
verifiedLinks: user.verifiedLinks,
|
2023-04-12 04:20:16 +00:00
|
|
|
|
followersCount: user.followersCount,
|
|
|
|
|
followingCount: user.followingCount,
|
|
|
|
|
notesCount: user.notesCount,
|
|
|
|
|
pinnedNoteIds: user.pinnedNoteIds,
|
|
|
|
|
pinnedNotes: user.pinnedNotes,
|
|
|
|
|
pinnedPageId: user.pinnedPageId,
|
|
|
|
|
pinnedPage: user.pinnedPage,
|
|
|
|
|
publicReactions: user.publicReactions,
|
2023-12-18 11:59:20 +00:00
|
|
|
|
followingVisibility: user.followingVisibility,
|
|
|
|
|
followersVisibility: user.followersVisibility,
|
2023-04-12 04:20:16 +00:00
|
|
|
|
roles: user.roles,
|
2023-04-13 16:10:36 +00:00
|
|
|
|
memo: user.memo,
|
2023-04-12 04:20:16 +00:00
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2023-04-13 16:10:36 +00:00
|
|
|
|
// Relations関連のキーが過不足なく入っている?
|
2024-03-03 11:15:35 +00:00
|
|
|
|
const userDetailedNotMeWithRelations = (user: misskey.entities.SignupResponse): Partial<misskey.entities.UserDetailedNotMe> => {
|
2023-04-12 04:20:16 +00:00
|
|
|
|
return stripUndefined({
|
|
|
|
|
...userDetailedNotMe(user),
|
|
|
|
|
isFollowing: user.isFollowing ?? false,
|
|
|
|
|
isFollowed: user.isFollowed ?? false,
|
|
|
|
|
hasPendingFollowRequestFromYou: user.hasPendingFollowRequestFromYou ?? false,
|
|
|
|
|
hasPendingFollowRequestToYou: user.hasPendingFollowRequestToYou ?? false,
|
|
|
|
|
isBlocking: user.isBlocking ?? false,
|
|
|
|
|
isBlocked: user.isBlocked ?? false,
|
|
|
|
|
isMuted: user.isMuted ?? false,
|
|
|
|
|
isRenoteMuted: user.isRenoteMuted ?? false,
|
2023-09-21 10:39:42 +00:00
|
|
|
|
notify: user.notify ?? 'none',
|
2023-10-03 11:26:11 +00:00
|
|
|
|
withReplies: user.withReplies ?? false,
|
2024-09-28 00:55:21 +00:00
|
|
|
|
followedMessage: user.isFollowing ? (user.followedMessage ?? null) : undefined,
|
2023-04-12 04:20:16 +00:00
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2023-04-13 16:10:36 +00:00
|
|
|
|
// MeDetailedのキーが過不足なく入っている?
|
2024-03-03 11:15:35 +00:00
|
|
|
|
const meDetailed = (user: misskey.entities.SignupResponse, security = false): Partial<misskey.entities.MeDetailed> => {
|
2023-04-12 04:20:16 +00:00
|
|
|
|
return stripUndefined({
|
|
|
|
|
...userDetailedNotMe(user),
|
|
|
|
|
avatarId: user.avatarId,
|
|
|
|
|
bannerId: user.bannerId,
|
2024-09-28 00:55:21 +00:00
|
|
|
|
followedMessage: user.followedMessage,
|
2023-04-12 04:20:16 +00:00
|
|
|
|
isModerator: user.isModerator,
|
|
|
|
|
isAdmin: user.isAdmin,
|
|
|
|
|
injectFeaturedNote: user.injectFeaturedNote,
|
|
|
|
|
receiveAnnouncementEmail: user.receiveAnnouncementEmail,
|
|
|
|
|
alwaysMarkNsfw: user.alwaysMarkNsfw,
|
|
|
|
|
autoSensitive: user.autoSensitive,
|
|
|
|
|
carefulBot: user.carefulBot,
|
|
|
|
|
autoAcceptFollowed: user.autoAcceptFollowed,
|
|
|
|
|
noCrawle: user.noCrawle,
|
2023-05-11 07:22:46 +00:00
|
|
|
|
preventAiLearning: user.preventAiLearning,
|
2023-04-12 04:20:16 +00:00
|
|
|
|
isExplorable: user.isExplorable,
|
|
|
|
|
isDeleted: user.isDeleted,
|
2023-08-28 09:25:31 +00:00
|
|
|
|
twoFactorBackupCodesStock: user.twoFactorBackupCodesStock,
|
2023-04-12 04:20:16 +00:00
|
|
|
|
hideOnlineStatus: user.hideOnlineStatus,
|
|
|
|
|
hasUnreadSpecifiedNotes: user.hasUnreadSpecifiedNotes,
|
|
|
|
|
hasUnreadMentions: user.hasUnreadMentions,
|
|
|
|
|
hasUnreadAnnouncement: user.hasUnreadAnnouncement,
|
|
|
|
|
hasUnreadAntenna: user.hasUnreadAntenna,
|
|
|
|
|
hasUnreadChannel: user.hasUnreadChannel,
|
|
|
|
|
hasUnreadNotification: user.hasUnreadNotification,
|
2023-11-01 04:34:05 +00:00
|
|
|
|
unreadNotificationsCount: user.unreadNotificationsCount,
|
2023-04-12 04:20:16 +00:00
|
|
|
|
hasPendingReceivedFollowRequest: user.hasPendingReceivedFollowRequest,
|
2024-10-06 11:12:13 +00:00
|
|
|
|
hasPendingSentFollowRequest: user.hasPendingSentFollowRequest,
|
2023-08-13 11:12:29 +00:00
|
|
|
|
unreadAnnouncements: user.unreadAnnouncements,
|
2023-04-12 04:20:16 +00:00
|
|
|
|
mutedWords: user.mutedWords,
|
2023-11-23 09:56:20 +00:00
|
|
|
|
hardMutedWords: user.hardMutedWords,
|
2023-04-12 04:20:16 +00:00
|
|
|
|
mutedInstances: user.mutedInstances,
|
2024-03-03 11:15:35 +00:00
|
|
|
|
// @ts-expect-error 後方互換性
|
2023-09-29 23:56:37 +00:00
|
|
|
|
mutingNotificationTypes: user.mutingNotificationTypes,
|
2023-09-29 02:29:54 +00:00
|
|
|
|
notificationRecieveConfig: user.notificationRecieveConfig,
|
2023-04-12 04:20:16 +00:00
|
|
|
|
emailNotificationTypes: user.emailNotificationTypes,
|
2023-06-24 23:34:18 +00:00
|
|
|
|
achievements: user.achievements,
|
2023-04-12 04:20:16 +00:00
|
|
|
|
loggedInDays: user.loggedInDays,
|
|
|
|
|
policies: user.policies,
|
2024-10-04 06:23:33 +00:00
|
|
|
|
twoFactorEnabled: user.twoFactorEnabled,
|
|
|
|
|
usePasswordLessLogin: user.usePasswordLessLogin,
|
|
|
|
|
securityKeys: user.securityKeys,
|
2023-04-12 04:20:16 +00:00
|
|
|
|
...(security ? {
|
|
|
|
|
email: user.email,
|
|
|
|
|
emailVerified: user.emailVerified,
|
|
|
|
|
securityKeysList: user.securityKeysList,
|
|
|
|
|
} : {}),
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2024-03-03 11:15:35 +00:00
|
|
|
|
let root: misskey.entities.SignupResponse;
|
|
|
|
|
let alice: misskey.entities.SignupResponse;
|
2023-04-12 04:20:16 +00:00
|
|
|
|
let aliceNote: misskey.entities.Note;
|
2024-03-03 11:15:35 +00:00
|
|
|
|
|
|
|
|
|
let bob: misskey.entities.SignupResponse;
|
|
|
|
|
|
|
|
|
|
// NOTE: これがないと落ちる(bob の updatedAt が null になってしまうため?)
|
|
|
|
|
let bobNote: misskey.entities.Note; // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
|
|
|
|
|
|
|
|
let carol: misskey.entities.SignupResponse;
|
|
|
|
|
|
|
|
|
|
let usersReplying: misskey.entities.SignupResponse[];
|
|
|
|
|
|
|
|
|
|
let userNoNote: misskey.entities.SignupResponse;
|
|
|
|
|
let userNotExplorable: misskey.entities.SignupResponse;
|
|
|
|
|
let userLocking: misskey.entities.SignupResponse;
|
|
|
|
|
let userAdmin: misskey.entities.SignupResponse;
|
|
|
|
|
let roleAdmin: misskey.entities.Role;
|
|
|
|
|
let userModerator: misskey.entities.SignupResponse;
|
|
|
|
|
let roleModerator: misskey.entities.Role;
|
|
|
|
|
let userRolePublic: misskey.entities.SignupResponse;
|
|
|
|
|
let rolePublic: misskey.entities.Role;
|
|
|
|
|
let userRoleBadge: misskey.entities.SignupResponse;
|
|
|
|
|
let roleBadge: misskey.entities.Role;
|
|
|
|
|
let userSilenced: misskey.entities.SignupResponse;
|
|
|
|
|
let roleSilenced: misskey.entities.Role;
|
|
|
|
|
let userSuspended: misskey.entities.SignupResponse;
|
|
|
|
|
let userDeletedBySelf: misskey.entities.SignupResponse;
|
|
|
|
|
let userDeletedByAdmin: misskey.entities.SignupResponse;
|
|
|
|
|
let userFollowingAlice: misskey.entities.SignupResponse;
|
|
|
|
|
let userFollowedByAlice: misskey.entities.SignupResponse;
|
|
|
|
|
let userBlockingAlice: misskey.entities.SignupResponse;
|
|
|
|
|
let userBlockedByAlice: misskey.entities.SignupResponse;
|
|
|
|
|
let userMutingAlice: misskey.entities.SignupResponse;
|
|
|
|
|
let userMutedByAlice: misskey.entities.SignupResponse;
|
|
|
|
|
let userRnMutingAlice: misskey.entities.SignupResponse;
|
|
|
|
|
let userRnMutedByAlice: misskey.entities.SignupResponse;
|
|
|
|
|
let userFollowRequesting: misskey.entities.SignupResponse;
|
|
|
|
|
let userFollowRequested: misskey.entities.SignupResponse;
|
2023-04-12 04:20:16 +00:00
|
|
|
|
|
|
|
|
|
beforeAll(async () => {
|
2023-05-07 03:53:41 +00:00
|
|
|
|
root = await signup({ username: 'root' });
|
|
|
|
|
alice = await signup({ username: 'alice' });
|
2024-03-03 11:15:35 +00:00
|
|
|
|
aliceNote = await post(alice, { text: 'test' });
|
2023-04-12 04:20:16 +00:00
|
|
|
|
bob = await signup({ username: 'bob' });
|
2024-03-03 11:15:35 +00:00
|
|
|
|
bobNote = await post(bob, { text: 'test' });
|
2023-04-12 04:20:16 +00:00
|
|
|
|
carol = await signup({ username: 'carol' });
|
|
|
|
|
|
|
|
|
|
// @alice -> @replyingへのリプライ。Promise.allで一気に作るとtimeoutしてしまうのでreduceで一つ一つawaitする
|
|
|
|
|
usersReplying = await [...Array(10)].map((_, i) => i).reduce(async (acc, i) => {
|
|
|
|
|
const u = await signup({ username: `replying${i}` });
|
|
|
|
|
for (let j = 0; j < 10 - i; j++) {
|
2023-06-24 23:34:18 +00:00
|
|
|
|
const p = await post(u, { text: `test${j}` });
|
2023-04-12 04:20:16 +00:00
|
|
|
|
await post(alice, { text: `@${u.username} test${j}`, replyId: p.id });
|
|
|
|
|
}
|
2023-06-24 23:34:18 +00:00
|
|
|
|
|
2023-04-12 04:20:16 +00:00
|
|
|
|
return (await acc).concat(u);
|
2024-03-03 11:15:35 +00:00
|
|
|
|
}, Promise.resolve([] as misskey.entities.SignupResponse[]));
|
2023-04-12 04:20:16 +00:00
|
|
|
|
|
|
|
|
|
userNoNote = await signup({ username: 'userNoNote' });
|
|
|
|
|
userNotExplorable = await signup({ username: 'userNotExplorable' });
|
|
|
|
|
await post(userNotExplorable, { text: 'test' });
|
|
|
|
|
await api('i/update', { isExplorable: false }, userNotExplorable);
|
|
|
|
|
userLocking = await signup({ username: 'userLocking' });
|
|
|
|
|
await post(userLocking, { text: 'test' });
|
|
|
|
|
await api('i/update', { isLocked: true }, userLocking);
|
|
|
|
|
userAdmin = await signup({ username: 'userAdmin' });
|
|
|
|
|
roleAdmin = await role(root, { isAdministrator: true, name: 'Admin Role' });
|
|
|
|
|
await api('admin/roles/assign', { userId: userAdmin.id, roleId: roleAdmin.id }, root);
|
|
|
|
|
userModerator = await signup({ username: 'userModerator' });
|
|
|
|
|
roleModerator = await role(root, { isModerator: true, name: 'Moderator Role' });
|
|
|
|
|
await api('admin/roles/assign', { userId: userModerator.id, roleId: roleModerator.id }, root);
|
|
|
|
|
userRolePublic = await signup({ username: 'userRolePublic' });
|
|
|
|
|
rolePublic = await role(root, { isPublic: true, name: 'Public Role' });
|
|
|
|
|
await api('admin/roles/assign', { userId: userRolePublic.id, roleId: rolePublic.id }, root);
|
|
|
|
|
userRoleBadge = await signup({ username: 'userRoleBadge' });
|
2024-07-14 00:26:25 +00:00
|
|
|
|
roleBadge = await role(root, { asBadge: true, name: 'Badge Role', isPublic: true });
|
2023-04-12 04:20:16 +00:00
|
|
|
|
await api('admin/roles/assign', { userId: userRoleBadge.id, roleId: roleBadge.id }, root);
|
|
|
|
|
userSilenced = await signup({ username: 'userSilenced' });
|
|
|
|
|
await post(userSilenced, { text: 'test' });
|
|
|
|
|
roleSilenced = await role(root, {}, { canPublicNote: { priority: 0, useDefault: false, value: false } });
|
|
|
|
|
await api('admin/roles/assign', { userId: userSilenced.id, roleId: roleSilenced.id }, root);
|
|
|
|
|
userSuspended = await signup({ username: 'userSuspended' });
|
|
|
|
|
await post(userSuspended, { text: 'test' });
|
|
|
|
|
await successfulApiCall({ endpoint: 'i/update', parameters: { description: '#user_testuserSuspended' }, user: userSuspended });
|
|
|
|
|
await api('admin/suspend-user', { userId: userSuspended.id }, root);
|
|
|
|
|
userDeletedBySelf = await signup({ username: 'userDeletedBySelf', password: 'userDeletedBySelf' });
|
|
|
|
|
await post(userDeletedBySelf, { text: 'test' });
|
|
|
|
|
await api('i/delete-account', { password: 'userDeletedBySelf' }, userDeletedBySelf);
|
|
|
|
|
userDeletedByAdmin = await signup({ username: 'userDeletedByAdmin' });
|
|
|
|
|
await post(userDeletedByAdmin, { text: 'test' });
|
|
|
|
|
await api('admin/delete-account', { userId: userDeletedByAdmin.id }, root);
|
|
|
|
|
userFollowingAlice = await signup({ username: 'userFollowingAlice' });
|
|
|
|
|
await post(userFollowingAlice, { text: 'test' });
|
|
|
|
|
await api('following/create', { userId: alice.id }, userFollowingAlice);
|
|
|
|
|
userFollowedByAlice = await signup({ username: 'userFollowedByAlice' });
|
|
|
|
|
await post(userFollowedByAlice, { text: 'test' });
|
|
|
|
|
await api('following/create', { userId: userFollowedByAlice.id }, alice);
|
|
|
|
|
userBlockingAlice = await signup({ username: 'userBlockingAlice' });
|
|
|
|
|
await post(userBlockingAlice, { text: 'test' });
|
|
|
|
|
await api('blocking/create', { userId: alice.id }, userBlockingAlice);
|
|
|
|
|
userBlockedByAlice = await signup({ username: 'userBlockedByAlice' });
|
|
|
|
|
await post(userBlockedByAlice, { text: 'test' });
|
|
|
|
|
await api('blocking/create', { userId: userBlockedByAlice.id }, alice);
|
|
|
|
|
userMutingAlice = await signup({ username: 'userMutingAlice' });
|
|
|
|
|
await post(userMutingAlice, { text: 'test' });
|
|
|
|
|
await api('mute/create', { userId: alice.id }, userMutingAlice);
|
|
|
|
|
userMutedByAlice = await signup({ username: 'userMutedByAlice' });
|
|
|
|
|
await post(userMutedByAlice, { text: 'test' });
|
|
|
|
|
await api('mute/create', { userId: userMutedByAlice.id }, alice);
|
|
|
|
|
userRnMutingAlice = await signup({ username: 'userRnMutingAlice' });
|
|
|
|
|
await post(userRnMutingAlice, { text: 'test' });
|
|
|
|
|
await api('renote-mute/create', { userId: alice.id }, userRnMutingAlice);
|
|
|
|
|
userRnMutedByAlice = await signup({ username: 'userRnMutedByAlice' });
|
|
|
|
|
await post(userRnMutedByAlice, { text: 'test' });
|
|
|
|
|
await api('renote-mute/create', { userId: userRnMutedByAlice.id }, alice);
|
|
|
|
|
userFollowRequesting = await signup({ username: 'userFollowRequesting' });
|
|
|
|
|
await post(userFollowRequesting, { text: 'test' });
|
|
|
|
|
userFollowRequested = userLocking;
|
|
|
|
|
await api('following/create', { userId: userFollowRequested.id }, userFollowRequesting);
|
|
|
|
|
}, 1000 * 60 * 10);
|
|
|
|
|
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
|
alice = {
|
|
|
|
|
...alice,
|
2024-03-03 11:15:35 +00:00
|
|
|
|
...await successfulApiCall({ endpoint: 'i', parameters: {}, user: alice }),
|
2023-04-12 04:20:16 +00:00
|
|
|
|
};
|
|
|
|
|
aliceNote = await successfulApiCall({ endpoint: 'notes/show', parameters: { noteId: aliceNote.id }, user: alice });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
//#region サインアップ(signup)
|
|
|
|
|
|
|
|
|
|
test('が作れる。(作りたての状態で自分のユーザー情報が取れる)', async () => {
|
|
|
|
|
// SignupApiService.ts
|
|
|
|
|
const response = await successfulApiCall({
|
|
|
|
|
endpoint: 'signup',
|
|
|
|
|
parameters: { username: 'zoe', password: 'password' },
|
|
|
|
|
user: undefined,
|
2024-03-03 11:15:35 +00:00
|
|
|
|
}) as unknown as misskey.entities.SignupResponse; // BUG MeDetailedに足りないキーがある
|
2023-04-12 04:20:16 +00:00
|
|
|
|
|
|
|
|
|
// signupの時はtokenが含まれる特別なMeDetailedが返ってくる
|
|
|
|
|
assert.match(response.token, /[a-zA-Z0-9]{16}/);
|
|
|
|
|
|
|
|
|
|
// UserLite
|
|
|
|
|
assert.match(response.id, /[0-9a-z]{10}/);
|
|
|
|
|
assert.strictEqual(response.name, null);
|
|
|
|
|
assert.strictEqual(response.username, 'zoe');
|
|
|
|
|
assert.strictEqual(response.host, null);
|
2024-03-03 11:15:35 +00:00
|
|
|
|
response.avatarUrl && assert.match(response.avatarUrl, /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/);
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.strictEqual(response.avatarBlurhash, null);
|
2023-10-21 09:38:07 +00:00
|
|
|
|
assert.deepStrictEqual(response.avatarDecorations, []);
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.strictEqual(response.isBot, false);
|
|
|
|
|
assert.strictEqual(response.isCat, false);
|
2023-10-04 02:35:58 +00:00
|
|
|
|
assert.strictEqual(response.speakAsCat, false);
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.strictEqual(response.instance, undefined);
|
|
|
|
|
assert.deepStrictEqual(response.emojis, {});
|
|
|
|
|
assert.strictEqual(response.onlineStatus, 'unknown');
|
|
|
|
|
assert.deepStrictEqual(response.badgeRoles, []);
|
|
|
|
|
// UserDetailedNotMeOnly
|
|
|
|
|
assert.strictEqual(response.url, null);
|
|
|
|
|
assert.strictEqual(response.uri, null);
|
enhance: account migration (#10592)
* copy block and mute then create follow and unfollow jobs
* copy block and mute and update lists when detecting an account has moved
* no need to care promise orders
* refactor updating actor and target
* automatically accept if a locked account had accepted an old account
* fix exception format
* prevent the old account from calling some endpoints
* do not unfollow when moving
* adjust following and follower counts
* check movedToUri when receiving a follow request
* skip if no need to adjust
* Revert "disable account migration"
This reverts commit 2321214c98591bcfe1385c1ab5bf0ff7b471ae1d.
* fix translation specifier
* fix checking alsoKnownAs and uri
* fix updating account
* fix refollowing locked account
* decrease followersCount if followed by the old account
* adjust following and followers counts when unfollowing
* fix copying mutings
* prohibit moved account from moving again
* fix move service
* allow app creation after moving
* fix lint
* remove unnecessary field
* fix cache update
* add e2e test
* add e2e test of accepting the new account automatically
* force follow if any error happens
* remove unnecessary joins
* use Array.map instead of for const of
* ユーザーリストの移行は追加のみを行う
* nanka iroiro
* fix misskey-js?
* :v:
* 移行を行ったアカウントからのフォローリクエストの自動許可を調整
* newUriを外に出す
* newUriを外に出す2
* clean up
* fix newUri
* prevent moving if the destination account has already moved
* set alsoKnownAs via /i/update
* fix database initialization
* add return type
* prohibit updating alsoKnownAs after moving
* skip to add to alsoKnownAs if toUrl is known
* skip adding to the list if it already has
* use Acct.parse instead
* rename error code
* :art:
* 制限を5から10に緩和
* movedTo(Uri), alsoKnownAsはユーザーidを返すように
* test api res
* fix
* 元アカウントはミュートし続ける
* :art:
* unfollow
* fix
* getUserUriをUserEntityServiceに
* ?
* job!
* :art:
* instance => server
* accountMovedShort, forbiddenBecauseYouAreMigrated
* accountMovedShort
* fix test
* import, pin禁止
* 実績を凍結する
* clean up
* :v:
* change message
* ブロック, フォロー, ミュート, リストのインポートファイルの制限を32MiBに
* Revert "ブロック, フォロー, ミュート, リストのインポートファイルの制限を32MiBに"
This reverts commit 3bd7be35d8aa455cb01ae58f8172a71a50485db1.
* validateAlsoKnownAs
* 移行後2時間以内はインポート可能なファイルサイズを拡大
* clean up
* どうせactorをupdatePersonで更新するならupdatePersonしか移行処理を発行しないことにする
* handle error?
* リモートからの移行処理の条件を是正
* log, port
* fix
* fix
* enhance(dev): non-production環境でhttpサーバー間でもユーザー、ノートの連合が可能なように
* refactor (use checkHttps)
* MISSKEY_WEBFINGER_USE_HTTP
* Environment Variable readme
* NEVER USE IN PRODUCTION
* fix punyHost
* fix indent
* fix
* experimental
---------
Co-authored-by: tamaina <tamaina@hotmail.co.jp>
Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2023-04-29 15:09:29 +00:00
|
|
|
|
assert.strictEqual(response.movedTo, null);
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.strictEqual(response.alsoKnownAs, null);
|
|
|
|
|
assert.strictEqual(response.createdAt, new Date(response.createdAt).toISOString());
|
|
|
|
|
assert.strictEqual(response.updatedAt, null);
|
|
|
|
|
assert.strictEqual(response.lastFetchedAt, null);
|
|
|
|
|
assert.strictEqual(response.bannerUrl, null);
|
|
|
|
|
assert.strictEqual(response.bannerBlurhash, null);
|
2023-10-06 00:32:09 +00:00
|
|
|
|
assert.strictEqual(response.backgroundUrl, null);
|
|
|
|
|
assert.strictEqual(response.backgroundBlurhash, null);
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.strictEqual(response.isLocked, false);
|
|
|
|
|
assert.strictEqual(response.isSilenced, false);
|
|
|
|
|
assert.strictEqual(response.isSuspended, false);
|
|
|
|
|
assert.strictEqual(response.description, null);
|
|
|
|
|
assert.strictEqual(response.location, null);
|
|
|
|
|
assert.strictEqual(response.birthday, null);
|
|
|
|
|
assert.strictEqual(response.lang, null);
|
|
|
|
|
assert.deepStrictEqual(response.fields, []);
|
2023-09-21 02:58:51 +00:00
|
|
|
|
assert.deepStrictEqual(response.verifiedLinks, []);
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.strictEqual(response.followersCount, 0);
|
|
|
|
|
assert.strictEqual(response.followingCount, 0);
|
|
|
|
|
assert.strictEqual(response.notesCount, 0);
|
|
|
|
|
assert.deepStrictEqual(response.pinnedNoteIds, []);
|
|
|
|
|
assert.deepStrictEqual(response.pinnedNotes, []);
|
|
|
|
|
assert.strictEqual(response.pinnedPageId, null);
|
|
|
|
|
assert.strictEqual(response.pinnedPage, null);
|
2023-05-10 06:59:34 +00:00
|
|
|
|
assert.strictEqual(response.publicReactions, true);
|
2023-12-18 11:59:20 +00:00
|
|
|
|
assert.strictEqual(response.followingVisibility, 'public');
|
|
|
|
|
assert.strictEqual(response.followersVisibility, 'public');
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.deepStrictEqual(response.roles, []);
|
2023-04-13 16:10:36 +00:00
|
|
|
|
assert.strictEqual(response.memo, null);
|
2023-06-24 23:34:18 +00:00
|
|
|
|
|
2023-04-12 04:20:16 +00:00
|
|
|
|
// MeDetailedOnly
|
|
|
|
|
assert.strictEqual(response.avatarId, null);
|
|
|
|
|
assert.strictEqual(response.bannerId, null);
|
2024-09-28 00:55:21 +00:00
|
|
|
|
assert.strictEqual(response.followedMessage, null);
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.strictEqual(response.isModerator, false);
|
|
|
|
|
assert.strictEqual(response.isAdmin, false);
|
|
|
|
|
assert.strictEqual(response.injectFeaturedNote, true);
|
|
|
|
|
assert.strictEqual(response.receiveAnnouncementEmail, true);
|
|
|
|
|
assert.strictEqual(response.alwaysMarkNsfw, false);
|
|
|
|
|
assert.strictEqual(response.autoSensitive, false);
|
|
|
|
|
assert.strictEqual(response.carefulBot, false);
|
|
|
|
|
assert.strictEqual(response.autoAcceptFollowed, true);
|
|
|
|
|
assert.strictEqual(response.noCrawle, false);
|
2023-05-11 07:22:46 +00:00
|
|
|
|
assert.strictEqual(response.preventAiLearning, true);
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.strictEqual(response.isExplorable, true);
|
|
|
|
|
assert.strictEqual(response.isDeleted, false);
|
2023-08-28 09:25:31 +00:00
|
|
|
|
assert.strictEqual(response.twoFactorBackupCodesStock, 'none');
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.strictEqual(response.hideOnlineStatus, false);
|
|
|
|
|
assert.strictEqual(response.hasUnreadSpecifiedNotes, false);
|
|
|
|
|
assert.strictEqual(response.hasUnreadMentions, false);
|
|
|
|
|
assert.strictEqual(response.hasUnreadAnnouncement, false);
|
|
|
|
|
assert.strictEqual(response.hasUnreadAntenna, false);
|
|
|
|
|
assert.strictEqual(response.hasUnreadChannel, false);
|
|
|
|
|
assert.strictEqual(response.hasUnreadNotification, false);
|
2023-11-01 04:34:05 +00:00
|
|
|
|
assert.strictEqual(response.unreadNotificationsCount, 0);
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.strictEqual(response.hasPendingReceivedFollowRequest, false);
|
2024-10-06 11:12:13 +00:00
|
|
|
|
assert.strictEqual(response.hasPendingSentFollowRequest, false);
|
2023-08-13 11:12:29 +00:00
|
|
|
|
assert.deepStrictEqual(response.unreadAnnouncements, []);
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.deepStrictEqual(response.mutedWords, []);
|
|
|
|
|
assert.deepStrictEqual(response.mutedInstances, []);
|
2024-03-03 11:15:35 +00:00
|
|
|
|
// @ts-expect-error 後方互換のため
|
2023-09-29 23:56:37 +00:00
|
|
|
|
assert.deepStrictEqual(response.mutingNotificationTypes, []);
|
2023-09-29 02:29:54 +00:00
|
|
|
|
assert.deepStrictEqual(response.notificationRecieveConfig, {});
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.deepStrictEqual(response.emailNotificationTypes, ['follow', 'receiveFollowRequest']);
|
|
|
|
|
assert.deepStrictEqual(response.achievements, []);
|
|
|
|
|
assert.deepStrictEqual(response.loggedInDays, 0);
|
2023-06-24 23:34:18 +00:00
|
|
|
|
assert.deepStrictEqual(response.policies, DEFAULT_POLICIES);
|
2024-10-04 06:23:33 +00:00
|
|
|
|
assert.strictEqual(response.twoFactorEnabled, false);
|
|
|
|
|
assert.strictEqual(response.usePasswordLessLogin, false);
|
|
|
|
|
assert.strictEqual(response.securityKeys, false);
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.notStrictEqual(response.email, undefined);
|
|
|
|
|
assert.strictEqual(response.emailVerified, false);
|
|
|
|
|
assert.deepStrictEqual(response.securityKeysList, []);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
//#endregion
|
|
|
|
|
//#region 自分の情報(i)
|
|
|
|
|
|
2023-04-13 16:10:36 +00:00
|
|
|
|
test('を読み取ることができること(自分)、キーが過不足なく入っていること。', async () => {
|
2023-04-12 04:20:16 +00:00
|
|
|
|
const response = await successfulApiCall({
|
|
|
|
|
endpoint: 'i',
|
|
|
|
|
parameters: {},
|
|
|
|
|
user: userNoNote,
|
|
|
|
|
});
|
|
|
|
|
const expected = meDetailed(userNoNote, true);
|
|
|
|
|
expected.loggedInDays = 1; // iはloggedInDaysを更新する
|
|
|
|
|
assert.deepStrictEqual(response, expected);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
//#endregion
|
|
|
|
|
//#region 自分の情報の更新(i/update)
|
|
|
|
|
|
|
|
|
|
test.each([
|
2024-03-03 11:15:35 +00:00
|
|
|
|
{ parameters: () => ({ name: null }) },
|
|
|
|
|
{ parameters: () => ({ name: 'x'.repeat(50) }) },
|
|
|
|
|
{ parameters: () => ({ name: 'x' }) },
|
|
|
|
|
{ parameters: () => ({ name: 'My name' }) },
|
|
|
|
|
{ parameters: () => ({ description: null }) },
|
|
|
|
|
{ parameters: () => ({ description: 'x'.repeat(1500) }) },
|
|
|
|
|
{ parameters: () => ({ description: 'x' }) },
|
|
|
|
|
{ parameters: () => ({ description: 'My description' }) },
|
2024-09-28 00:55:21 +00:00
|
|
|
|
{ parameters: () => ({ followedMessage: null }) },
|
|
|
|
|
{ parameters: () => ({ followedMessage: 'Thank you' }) },
|
2024-03-03 11:15:35 +00:00
|
|
|
|
{ parameters: () => ({ location: null }) },
|
|
|
|
|
{ parameters: () => ({ location: 'x'.repeat(50) }) },
|
|
|
|
|
{ parameters: () => ({ location: 'x' }) },
|
|
|
|
|
{ parameters: () => ({ location: 'My location' }) },
|
|
|
|
|
{ parameters: () => ({ birthday: '0000-00-00' }) },
|
|
|
|
|
{ parameters: () => ({ birthday: '9999-99-99' }) },
|
|
|
|
|
{ parameters: () => ({ lang: 'en-US' as const }) },
|
|
|
|
|
{ parameters: () => ({ fields: [] }) },
|
|
|
|
|
{ parameters: () => ({ fields: [{ name: 'x', value: 'x' }] }) },
|
|
|
|
|
{ parameters: () => ({ fields: [{ name: 'x'.repeat(3000), value: 'x'.repeat(3000) }] }) }, // BUG? fieldには制限がない
|
|
|
|
|
{ parameters: () => ({ fields: Array(16).fill({ name: 'x', value: 'y' }) }) },
|
|
|
|
|
{ parameters: () => ({ isLocked: true }) },
|
|
|
|
|
{ parameters: () => ({ isLocked: false }) },
|
|
|
|
|
{ parameters: () => ({ isExplorable: false }) },
|
|
|
|
|
{ parameters: () => ({ isExplorable: true }) },
|
|
|
|
|
{ parameters: () => ({ hideOnlineStatus: true }) },
|
|
|
|
|
{ parameters: () => ({ hideOnlineStatus: false }) },
|
|
|
|
|
{ parameters: () => ({ publicReactions: false }) },
|
|
|
|
|
{ parameters: () => ({ publicReactions: true }) },
|
|
|
|
|
{ parameters: () => ({ autoAcceptFollowed: true }) },
|
|
|
|
|
{ parameters: () => ({ autoAcceptFollowed: false }) },
|
|
|
|
|
{ parameters: () => ({ noCrawle: true }) },
|
|
|
|
|
{ parameters: () => ({ noCrawle: false }) },
|
|
|
|
|
{ parameters: () => ({ preventAiLearning: false }) },
|
|
|
|
|
{ parameters: () => ({ preventAiLearning: true }) },
|
|
|
|
|
{ parameters: () => ({ isBot: true }) },
|
|
|
|
|
{ parameters: () => ({ isBot: false }) },
|
|
|
|
|
{ parameters: () => ({ isCat: true }) },
|
|
|
|
|
{ parameters: () => ({ isCat: false }) },
|
|
|
|
|
{ parameters: () => ({ injectFeaturedNote: true }) },
|
|
|
|
|
{ parameters: () => ({ injectFeaturedNote: false }) },
|
|
|
|
|
{ parameters: () => ({ receiveAnnouncementEmail: true }) },
|
|
|
|
|
{ parameters: () => ({ receiveAnnouncementEmail: false }) },
|
|
|
|
|
{ parameters: () => ({ alwaysMarkNsfw: true }) },
|
|
|
|
|
{ parameters: () => ({ alwaysMarkNsfw: false }) },
|
|
|
|
|
{ parameters: () => ({ autoSensitive: true }) },
|
|
|
|
|
{ parameters: () => ({ autoSensitive: false }) },
|
|
|
|
|
{ parameters: () => ({ followingVisibility: 'private' as const }) },
|
|
|
|
|
{ parameters: () => ({ followingVisibility: 'followers' as const }) },
|
|
|
|
|
{ parameters: () => ({ followingVisibility: 'public' as const }) },
|
|
|
|
|
{ parameters: () => ({ followersVisibility: 'private' as const }) },
|
|
|
|
|
{ parameters: () => ({ followersVisibility: 'followers' as const }) },
|
|
|
|
|
{ parameters: () => ({ followersVisibility: 'public' as const }) },
|
|
|
|
|
{ parameters: () => ({ mutedWords: Array(19).fill(['xxxxx']) }) },
|
|
|
|
|
{ parameters: () => ({ mutedWords: [['x'.repeat(194)]] }) },
|
|
|
|
|
{ parameters: () => ({ mutedWords: [] }) },
|
|
|
|
|
{ parameters: () => ({ mutedInstances: ['xxxx.xxxxx'] }) },
|
|
|
|
|
{ parameters: () => ({ mutedInstances: [] }) },
|
|
|
|
|
{ parameters: () => ({ notificationRecieveConfig: { mention: { type: 'following' } } }) },
|
|
|
|
|
{ parameters: () => ({ notificationRecieveConfig: {} }) },
|
|
|
|
|
{ parameters: () => ({ emailNotificationTypes: ['mention', 'reply', 'quote', 'follow', 'receiveFollowRequest'] }) },
|
|
|
|
|
{ parameters: () => ({ emailNotificationTypes: [] }) },
|
2023-04-12 04:20:16 +00:00
|
|
|
|
] as const)('を書き換えることができる($#)', async ({ parameters }) => {
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'i/update', parameters: parameters(), user: alice });
|
|
|
|
|
const expected = { ...meDetailed(alice, true), ...parameters() };
|
|
|
|
|
assert.deepStrictEqual(response, expected, inspect(parameters()));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('を書き換えることができる(Avatar)', async () => {
|
|
|
|
|
const aliceFile = (await uploadFile(alice)).body;
|
2024-03-03 11:15:35 +00:00
|
|
|
|
const parameters = { avatarId: aliceFile!.id };
|
2023-04-12 04:20:16 +00:00
|
|
|
|
const response = await successfulApiCall({ endpoint: 'i/update', parameters: parameters, user: alice });
|
|
|
|
|
assert.match(response.avatarUrl ?? '.', /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/);
|
|
|
|
|
assert.match(response.avatarBlurhash ?? '.', /[ -~]{54}/);
|
2023-06-24 23:34:18 +00:00
|
|
|
|
const expected = {
|
|
|
|
|
...meDetailed(alice, true),
|
2024-03-03 11:15:35 +00:00
|
|
|
|
avatarId: aliceFile!.id,
|
2023-04-12 04:20:16 +00:00
|
|
|
|
avatarBlurhash: response.avatarBlurhash,
|
|
|
|
|
avatarUrl: response.avatarUrl,
|
|
|
|
|
};
|
|
|
|
|
assert.deepStrictEqual(response, expected, inspect(parameters));
|
|
|
|
|
|
|
|
|
|
const parameters2 = { avatarId: null };
|
|
|
|
|
const response2 = await successfulApiCall({ endpoint: 'i/update', parameters: parameters2, user: alice });
|
2023-06-24 23:34:18 +00:00
|
|
|
|
const expected2 = {
|
|
|
|
|
...meDetailed(alice, true),
|
2023-04-12 04:20:16 +00:00
|
|
|
|
avatarId: null,
|
|
|
|
|
avatarBlurhash: null,
|
|
|
|
|
avatarUrl: alice.avatarUrl, // 解除した場合、identiconになる
|
|
|
|
|
};
|
|
|
|
|
assert.deepStrictEqual(response2, expected2, inspect(parameters));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('を書き換えることができる(Banner)', async () => {
|
|
|
|
|
const aliceFile = (await uploadFile(alice)).body;
|
2024-03-03 11:15:35 +00:00
|
|
|
|
const parameters = { bannerId: aliceFile!.id };
|
2023-04-12 04:20:16 +00:00
|
|
|
|
const response = await successfulApiCall({ endpoint: 'i/update', parameters: parameters, user: alice });
|
|
|
|
|
assert.match(response.bannerUrl ?? '.', /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/);
|
|
|
|
|
assert.match(response.bannerBlurhash ?? '.', /[ -~]{54}/);
|
2023-06-24 23:34:18 +00:00
|
|
|
|
const expected = {
|
|
|
|
|
...meDetailed(alice, true),
|
2024-03-03 11:15:35 +00:00
|
|
|
|
bannerId: aliceFile!.id,
|
2023-04-12 04:20:16 +00:00
|
|
|
|
bannerBlurhash: response.bannerBlurhash,
|
|
|
|
|
bannerUrl: response.bannerUrl,
|
|
|
|
|
};
|
|
|
|
|
assert.deepStrictEqual(response, expected, inspect(parameters));
|
|
|
|
|
|
|
|
|
|
const parameters2 = { bannerId: null };
|
|
|
|
|
const response2 = await successfulApiCall({ endpoint: 'i/update', parameters: parameters2, user: alice });
|
2023-06-24 23:34:18 +00:00
|
|
|
|
const expected2 = {
|
|
|
|
|
...meDetailed(alice, true),
|
2023-04-12 04:20:16 +00:00
|
|
|
|
bannerId: null,
|
|
|
|
|
bannerBlurhash: null,
|
|
|
|
|
bannerUrl: null,
|
|
|
|
|
};
|
|
|
|
|
assert.deepStrictEqual(response2, expected2, inspect(parameters));
|
|
|
|
|
});
|
|
|
|
|
|
2023-10-06 00:32:09 +00:00
|
|
|
|
test('を書き換えることができる(Background)', async () => {
|
|
|
|
|
const aliceFile = (await uploadFile(alice)).body;
|
2024-04-11 13:13:58 +00:00
|
|
|
|
const parameters = { bannerId: aliceFile!.id };
|
2023-10-06 00:32:09 +00:00
|
|
|
|
const response = await successfulApiCall({ endpoint: 'i/update', parameters: parameters, user: alice });
|
|
|
|
|
assert.match(response.backgroundUrl ?? '.', /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/);
|
|
|
|
|
assert.match(response.backgroundBlurhash ?? '.', /[ -~]{54}/);
|
|
|
|
|
const expected = {
|
|
|
|
|
...meDetailed(alice, true),
|
2024-04-11 13:13:58 +00:00
|
|
|
|
backgroundId: aliceFile!.id,
|
|
|
|
|
backgroundBlurhash: response.backgroundBlurhash,
|
2023-10-06 00:32:09 +00:00
|
|
|
|
backgroundUrl: response.backgroundUrl,
|
|
|
|
|
};
|
|
|
|
|
assert.deepStrictEqual(response, expected, inspect(parameters));
|
|
|
|
|
|
|
|
|
|
const parameters2 = { backgroundId: null };
|
|
|
|
|
const response2 = await successfulApiCall({ endpoint: 'i/update', parameters: parameters2, user: alice });
|
|
|
|
|
const expected2 = {
|
|
|
|
|
...meDetailed(alice, true),
|
|
|
|
|
backgroundId: null,
|
|
|
|
|
backgroundBlurhash: null,
|
|
|
|
|
backgroundUrl: null,
|
|
|
|
|
};
|
|
|
|
|
assert.deepStrictEqual(response2, expected2, inspect(parameters));
|
|
|
|
|
});
|
|
|
|
|
|
2023-04-12 04:20:16 +00:00
|
|
|
|
//#endregion
|
|
|
|
|
//#region 自分の情報の更新(i/pin, i/unpin)
|
|
|
|
|
|
|
|
|
|
test('を書き換えることができる(ピン止めノート)', async () => {
|
|
|
|
|
const parameters = { noteId: aliceNote.id };
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'i/pin', parameters, user: alice });
|
|
|
|
|
const expected = { ...meDetailed(alice, false), pinnedNoteIds: [aliceNote.id], pinnedNotes: [aliceNote] };
|
|
|
|
|
assert.deepStrictEqual(response, expected);
|
2023-06-24 23:34:18 +00:00
|
|
|
|
|
2023-04-12 04:20:16 +00:00
|
|
|
|
const response2 = await successfulApiCall({ endpoint: 'i/unpin', parameters, user: alice });
|
|
|
|
|
const expected2 = meDetailed(alice, false);
|
|
|
|
|
assert.deepStrictEqual(response2, expected2);
|
|
|
|
|
});
|
|
|
|
|
|
2023-04-13 16:10:36 +00:00
|
|
|
|
//#endregion
|
|
|
|
|
//#region メモの更新(users/update-memo)
|
|
|
|
|
|
|
|
|
|
test.each([
|
|
|
|
|
{ label: '最大長', memo: 'x'.repeat(2048) },
|
|
|
|
|
{ label: '空文字', memo: '', expects: null },
|
|
|
|
|
{ label: 'null', memo: null },
|
|
|
|
|
])('を書き換えることができる(メモを$labelに)', async ({ memo, expects }) => {
|
2023-05-07 08:38:06 +00:00
|
|
|
|
const expected = { ...await show(bob.id, alice), memo: expects === undefined ? memo : expects };
|
2023-04-13 16:10:36 +00:00
|
|
|
|
const parameters = { userId: bob.id, memo };
|
|
|
|
|
await successfulApiCall({ endpoint: 'users/update-memo', parameters, user: alice });
|
2023-05-07 08:38:06 +00:00
|
|
|
|
const response = await show(bob.id, alice);
|
2023-04-13 16:10:36 +00:00
|
|
|
|
assert.deepStrictEqual(response, expected);
|
|
|
|
|
});
|
|
|
|
|
|
2023-04-12 04:20:16 +00:00
|
|
|
|
//#endregion
|
|
|
|
|
//#region ユーザー(users)
|
|
|
|
|
|
|
|
|
|
test.each([
|
2024-03-03 11:15:35 +00:00
|
|
|
|
{ label: 'ID昇順', parameters: { limit: 5 }, selector: (u: misskey.entities.UserLite): string => u.id },
|
|
|
|
|
{ label: 'フォロワー昇順', parameters: { sort: '+follower' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.followersCount) },
|
|
|
|
|
{ label: 'フォロワー降順', parameters: { sort: '-follower' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.followersCount) },
|
|
|
|
|
{ label: '登録日時昇順', parameters: { sort: '+createdAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => u.createdAt },
|
|
|
|
|
{ label: '登録日時降順', parameters: { sort: '-createdAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => u.createdAt },
|
|
|
|
|
{ label: '投稿日時昇順', parameters: { sort: '+updatedAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.updatedAt) },
|
|
|
|
|
{ label: '投稿日時降順', parameters: { sort: '-updatedAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.updatedAt) },
|
2023-04-12 04:20:16 +00:00
|
|
|
|
] as const)('をリスト形式で取得することができる($label)', async ({ parameters, selector }) => {
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'users', parameters, user: alice });
|
|
|
|
|
|
|
|
|
|
// 結果の並びを事前にアサートするのは困難なので返ってきたidに対応するユーザーが返っており、ソート順が正しいことだけを検証する
|
2023-05-07 08:38:06 +00:00
|
|
|
|
const users = await Promise.all(response.map(u => show(u.id, alice)));
|
2023-04-12 04:20:16 +00:00
|
|
|
|
const expected = users.sort((x, y) => {
|
|
|
|
|
const index = (selector(x) < selector(y)) ? -1 : (selector(x) > selector(y)) ? 1 : 0;
|
|
|
|
|
return index * (parameters.sort?.startsWith('+') ? -1 : 1);
|
|
|
|
|
});
|
|
|
|
|
assert.deepStrictEqual(response, expected);
|
|
|
|
|
});
|
|
|
|
|
test.each([
|
2024-03-03 11:15:35 +00:00
|
|
|
|
{ label: '「見つけやすくする」がOFFのユーザーが含まれない', user: () => userNotExplorable, excluded: true },
|
|
|
|
|
{ label: 'ミュートユーザーが含まれない', user: () => userMutedByAlice, excluded: true },
|
|
|
|
|
{ label: 'ブロックされているユーザーが含まれない', user: () => userBlockedByAlice, excluded: true },
|
|
|
|
|
{ label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice, excluded: true },
|
|
|
|
|
{ label: '承認制ユーザーが含まれる', user: () => userLocking },
|
|
|
|
|
{ label: 'サイレンスユーザーが含まれる', user: () => userSilenced },
|
|
|
|
|
{ label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true },
|
|
|
|
|
{ label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf },
|
|
|
|
|
{ label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin },
|
2023-04-12 04:20:16 +00:00
|
|
|
|
] as const)('をリスト形式で取得することができ、結果に$label', async ({ user, excluded }) => {
|
|
|
|
|
const parameters = { limit: 100 };
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'users', parameters, user: alice });
|
2023-05-07 08:38:06 +00:00
|
|
|
|
const expected = (excluded ?? false) ? [] : [await show(user().id, alice)];
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.deepStrictEqual(response.filter((u) => u.id === user().id), expected);
|
|
|
|
|
});
|
|
|
|
|
test.todo('をリスト形式で取得することができる(リモート, hostname指定)');
|
|
|
|
|
test.todo('をリスト形式で取得することができる(pagenation)');
|
2023-06-24 23:34:18 +00:00
|
|
|
|
|
2023-04-12 04:20:16 +00:00
|
|
|
|
//#endregion
|
|
|
|
|
//#region ユーザー情報(users/show)
|
|
|
|
|
|
|
|
|
|
test.each([
|
2024-03-03 11:15:35 +00:00
|
|
|
|
{ label: 'ID指定で自分自身を', parameters: () => ({ userId: alice.id }), user: () => alice, type: meDetailed },
|
|
|
|
|
{ label: 'ID指定で他人を', parameters: () => ({ userId: alice.id }), user: () => bob, type: userDetailedNotMeWithRelations },
|
|
|
|
|
{ label: 'ID指定かつ未認証', parameters: () => ({ userId: alice.id }), user: undefined, type: userDetailedNotMe },
|
|
|
|
|
{ label: '@指定で自分自身を', parameters: () => ({ username: alice.username }), user: () => alice, type: meDetailed },
|
|
|
|
|
{ label: '@指定で他人を', parameters: () => ({ username: alice.username }), user: () => bob, type: userDetailedNotMeWithRelations },
|
|
|
|
|
{ label: '@指定かつ未認証', parameters: () => ({ username: alice.username }), user: undefined, type: userDetailedNotMe },
|
2023-04-12 04:20:16 +00:00
|
|
|
|
] as const)('を取得することができる($label)', async ({ parameters, user, type }) => {
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'users/show', parameters: parameters(), user: user?.() });
|
|
|
|
|
const expected = type(alice);
|
|
|
|
|
assert.deepStrictEqual(response, expected);
|
|
|
|
|
});
|
|
|
|
|
test.each([
|
2024-03-03 11:15:35 +00:00
|
|
|
|
{ label: 'Administratorになっている', user: () => userAdmin, me: () => userAdmin, selector: (user: misskey.entities.MeDetailed) => user.isAdmin },
|
|
|
|
|
{ label: '自分以外から見たときはAdministratorか判定できない', user: () => userAdmin, selector: (user: misskey.entities.UserDetailedNotMe) => user.isAdmin, expected: () => undefined },
|
|
|
|
|
{ label: 'Moderatorになっている', user: () => userModerator, me: () => userModerator, selector: (user: misskey.entities.MeDetailed) => user.isModerator },
|
|
|
|
|
{ label: '自分以外から見たときはModeratorか判定できない', user: () => userModerator, selector: (user: misskey.entities.UserDetailedNotMe) => user.isModerator, expected: () => undefined },
|
2024-10-04 06:23:33 +00:00
|
|
|
|
{ label: '自分から見た場合に二要素認証関連のプロパティがセットされている', user: () => alice, me: () => alice, selector: (user: misskey.entities.MeDetailed) => user.twoFactorEnabled, expected: () => false },
|
|
|
|
|
{ label: '自分以外から見た場合に二要素認証関連のプロパティがセットされていない', user: () => alice, me: () => bob, selector: (user: misskey.entities.UserDetailedNotMe) => user.twoFactorEnabled, expected: () => undefined },
|
|
|
|
|
{ label: 'モデレーターから見た場合に二要素認証関連のプロパティがセットされている', user: () => alice, me: () => userModerator, selector: (user: misskey.entities.UserDetailedNotMe) => user.twoFactorEnabled, expected: () => false },
|
2024-03-03 11:15:35 +00:00
|
|
|
|
{ label: 'サイレンスになっている', user: () => userSilenced, selector: (user: misskey.entities.UserDetailed) => user.isSilenced },
|
|
|
|
|
// FIXME: 落ちる
|
|
|
|
|
//{ label: 'サスペンドになっている', user: () => userSuspended, selector: (user: misskey.entities.UserDetailed) => user.isSuspended },
|
|
|
|
|
{ label: '削除済みになっている', user: () => userDeletedBySelf, me: () => userDeletedBySelf, selector: (user: misskey.entities.MeDetailed) => user.isDeleted },
|
|
|
|
|
// @ts-expect-error UserDetailedNotMe doesn't include isDeleted
|
|
|
|
|
{ label: '自分以外から見たときは削除済みか判定できない', user: () => userDeletedBySelf, selector: (user: misskey.entities.UserDetailedNotMe) => user.isDeleted, expected: () => undefined },
|
|
|
|
|
{ label: '削除済み(byAdmin)になっている', user: () => userDeletedByAdmin, me: () => userDeletedByAdmin, selector: (user: misskey.entities.MeDetailed) => user.isDeleted },
|
|
|
|
|
// @ts-expect-error UserDetailedNotMe doesn't include isDeleted
|
|
|
|
|
{ label: '自分以外から見たときは削除済み(byAdmin)か判定できない', user: () => userDeletedByAdmin, selector: (user: misskey.entities.UserDetailedNotMe) => user.isDeleted, expected: () => undefined },
|
|
|
|
|
{ label: 'フォロー中になっている', user: () => userFollowedByAlice, selector: (user: misskey.entities.UserDetailed) => user.isFollowing },
|
|
|
|
|
{ label: 'フォローされている', user: () => userFollowingAlice, selector: (user: misskey.entities.UserDetailed) => user.isFollowed },
|
|
|
|
|
{ label: 'ブロック中になっている', user: () => userBlockedByAlice, selector: (user: misskey.entities.UserDetailed) => user.isBlocking },
|
|
|
|
|
{ label: 'ブロックされている', user: () => userBlockingAlice, selector: (user: misskey.entities.UserDetailed) => user.isBlocked },
|
|
|
|
|
{ label: 'ミュート中になっている', user: () => userMutedByAlice, selector: (user: misskey.entities.UserDetailed) => user.isMuted },
|
|
|
|
|
{ label: 'リノートミュート中になっている', user: () => userRnMutedByAlice, selector: (user: misskey.entities.UserDetailed) => user.isRenoteMuted },
|
|
|
|
|
{ label: 'フォローリクエスト中になっている', user: () => userFollowRequested, me: () => userFollowRequesting, selector: (user: misskey.entities.UserDetailed) => user.hasPendingFollowRequestFromYou },
|
|
|
|
|
{ label: 'フォローリクエストされている', user: () => userFollowRequesting, me: () => userFollowRequested, selector: (user: misskey.entities.UserDetailed) => user.hasPendingFollowRequestToYou },
|
2023-04-12 04:20:16 +00:00
|
|
|
|
] as const)('を取得することができ、$labelこと', async ({ user, me, selector, expected }) => {
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'users/show', parameters: { userId: user().id }, user: me?.() ?? alice });
|
2024-03-03 11:15:35 +00:00
|
|
|
|
assert.strictEqual(selector(response as any), (expected ?? ((): true => true))());
|
2023-04-12 04:20:16 +00:00
|
|
|
|
});
|
|
|
|
|
test('を取得することができ、Publicなロールがセットされていること', async () => {
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'users/show', parameters: { userId: userRolePublic.id }, user: alice });
|
|
|
|
|
assert.deepStrictEqual(response.badgeRoles, []);
|
|
|
|
|
assert.deepStrictEqual(response.roles, [{
|
|
|
|
|
id: rolePublic.id,
|
|
|
|
|
name: rolePublic.name,
|
|
|
|
|
color: rolePublic.color,
|
|
|
|
|
iconUrl: rolePublic.iconUrl,
|
|
|
|
|
description: rolePublic.description,
|
|
|
|
|
isModerator: rolePublic.isModerator,
|
|
|
|
|
isAdministrator: rolePublic.isAdministrator,
|
|
|
|
|
displayOrder: rolePublic.displayOrder,
|
|
|
|
|
}]);
|
|
|
|
|
});
|
|
|
|
|
test('を取得することができ、バッヂロールがセットされていること', async () => {
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'users/show', parameters: { userId: userRoleBadge.id }, user: alice });
|
|
|
|
|
assert.deepStrictEqual(response.badgeRoles, [{
|
|
|
|
|
name: roleBadge.name,
|
|
|
|
|
iconUrl: roleBadge.iconUrl,
|
|
|
|
|
displayOrder: roleBadge.displayOrder,
|
|
|
|
|
}]);
|
2024-07-14 00:26:25 +00:00
|
|
|
|
assert.deepStrictEqual(response.roles, [{
|
|
|
|
|
id: roleBadge.id,
|
|
|
|
|
name: roleBadge.name,
|
|
|
|
|
color: roleBadge.color,
|
|
|
|
|
iconUrl: roleBadge.iconUrl,
|
|
|
|
|
description: roleBadge.description,
|
|
|
|
|
isModerator: roleBadge.isModerator,
|
|
|
|
|
isAdministrator: roleBadge.isAdministrator,
|
|
|
|
|
displayOrder: roleBadge.displayOrder,
|
|
|
|
|
}]);
|
2023-04-12 04:20:16 +00:00
|
|
|
|
});
|
|
|
|
|
test('をID指定のリスト形式で取得することができる(空)', async () => {
|
|
|
|
|
const parameters = { userIds: [] };
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'users/show', parameters, user: alice });
|
|
|
|
|
const expected: [] = [];
|
|
|
|
|
assert.deepStrictEqual(response, expected);
|
|
|
|
|
});
|
|
|
|
|
test('をID指定のリスト形式で取得することができる', async() => {
|
|
|
|
|
const parameters = { userIds: [bob.id, alice.id, carol.id] };
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'users/show', parameters, user: alice });
|
|
|
|
|
const expected = [
|
2023-06-24 23:34:18 +00:00
|
|
|
|
await successfulApiCall({ endpoint: 'users/show', parameters: { userId: bob.id }, user: alice }),
|
|
|
|
|
await successfulApiCall({ endpoint: 'users/show', parameters: { userId: alice.id }, user: alice }),
|
|
|
|
|
await successfulApiCall({ endpoint: 'users/show', parameters: { userId: carol.id }, user: alice }),
|
2023-04-12 04:20:16 +00:00
|
|
|
|
];
|
|
|
|
|
assert.deepStrictEqual(response, expected);
|
|
|
|
|
});
|
|
|
|
|
test.each([
|
2024-03-03 11:15:35 +00:00
|
|
|
|
{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable },
|
|
|
|
|
{ label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice },
|
|
|
|
|
{ label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice },
|
|
|
|
|
{ label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice },
|
|
|
|
|
{ label: '承認制ユーザーが含まれる', user: () => userLocking },
|
|
|
|
|
{ label: 'サイレンスユーザーが含まれる', user: () => userSilenced },
|
|
|
|
|
{ label: 'サスペンドユーザーが(モデレーターが見るときは)含まれる', user: () => userSuspended, me: () => root },
|
2023-04-12 04:20:16 +00:00
|
|
|
|
// BUG サスペンドユーザーを一般ユーザーから見るとrootユーザーが返ってくる
|
2024-03-03 11:15:35 +00:00
|
|
|
|
//{ label: 'サスペンドユーザーが(一般ユーザーが見るときは)含まれない', user: () => userSuspended, me: () => bob, excluded: true },
|
|
|
|
|
{ label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf },
|
|
|
|
|
{ label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin },
|
|
|
|
|
// @ts-expect-error excluded は上でコメントアウトされているので
|
2023-04-12 04:20:16 +00:00
|
|
|
|
] as const)('をID指定のリスト形式で取得することができ、結果に$label', async ({ user, me, excluded }) => {
|
|
|
|
|
const parameters = { userIds: [user().id] };
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'users/show', parameters, user: me?.() ?? alice });
|
|
|
|
|
const expected = (excluded ?? false) ? [] : [await show(user().id, me?.() ?? alice)];
|
|
|
|
|
assert.deepStrictEqual(response, expected);
|
|
|
|
|
});
|
|
|
|
|
test.todo('をID指定のリスト形式で取得することができる(リモート)');
|
|
|
|
|
|
|
|
|
|
//#endregion
|
|
|
|
|
//#region 検索(users/search)
|
|
|
|
|
|
|
|
|
|
test('を検索することができる', async () => {
|
|
|
|
|
const parameters = { query: 'carol', limit: 10 };
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'users/search', parameters, user: alice });
|
2023-05-07 08:38:06 +00:00
|
|
|
|
const expected = [await show(carol.id, alice)];
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.deepStrictEqual(response, expected);
|
|
|
|
|
});
|
|
|
|
|
test('を検索することができる(UserLite)', async () => {
|
|
|
|
|
const parameters = { query: 'carol', detail: false, limit: 10 };
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'users/search', parameters, user: alice });
|
2023-05-07 08:38:06 +00:00
|
|
|
|
const expected = [userLite(await show(carol.id, alice))];
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.deepStrictEqual(response, expected);
|
|
|
|
|
});
|
|
|
|
|
test.each([
|
2024-03-03 11:15:35 +00:00
|
|
|
|
{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable },
|
|
|
|
|
{ label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice },
|
|
|
|
|
{ label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice },
|
|
|
|
|
{ label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice },
|
|
|
|
|
{ label: '承認制ユーザーが含まれる', user: () => userLocking },
|
|
|
|
|
{ label: 'サイレンスユーザーが含まれる', user: () => userSilenced },
|
|
|
|
|
{ label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true },
|
|
|
|
|
{ label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf },
|
|
|
|
|
{ label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin },
|
2023-04-12 04:20:16 +00:00
|
|
|
|
] as const)('を検索することができ、結果に$labelが含まれる', async ({ user, excluded }) => {
|
|
|
|
|
const parameters = { query: user().username, limit: 1 };
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'users/search', parameters, user: alice });
|
2023-05-07 08:38:06 +00:00
|
|
|
|
const expected = (excluded ?? false) ? [] : [await show(user().id, alice)];
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.deepStrictEqual(response, expected);
|
|
|
|
|
});
|
|
|
|
|
test.todo('を検索することができる(リモート)');
|
|
|
|
|
test.todo('を検索することができる(pagenation)');
|
|
|
|
|
|
|
|
|
|
//#endregion
|
|
|
|
|
//#region ID指定検索(users/search-by-username-and-host)
|
|
|
|
|
|
2023-06-24 23:34:18 +00:00
|
|
|
|
test.each([
|
2024-03-03 11:15:35 +00:00
|
|
|
|
{ label: '自分', parameters: { username: 'alice' }, user: () => [alice] },
|
|
|
|
|
{ label: '自分かつusernameが大文字', parameters: { username: 'ALICE' }, user: () => [alice] },
|
|
|
|
|
{ label: 'ローカルのフォロイーでノートなし', parameters: { username: 'userFollowedByAlice' }, user: () => [userFollowedByAlice] },
|
|
|
|
|
{ label: 'ローカルでノートなしは検索に載らない', parameters: { username: 'userNoNote' }, user: () => [] },
|
|
|
|
|
{ label: 'ローカルの他人1', parameters: { username: 'bob' }, user: () => [bob] },
|
|
|
|
|
{ label: 'ローカルの他人2', parameters: { username: 'bob', host: null }, user: () => [bob] },
|
|
|
|
|
{ label: 'ローカルの他人3', parameters: { username: 'bob', host: '.' }, user: () => [bob] },
|
|
|
|
|
{ label: 'ローカル', parameters: { host: null, limit: 1 }, user: () => [userFollowedByAlice] },
|
|
|
|
|
{ label: 'ローカル', parameters: { host: '.', limit: 1 }, user: () => [userFollowedByAlice] },
|
2023-04-12 04:20:16 +00:00
|
|
|
|
])('をID&ホスト指定で検索できる($label)', async ({ parameters, user }) => {
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'users/search-by-username-and-host', parameters, user: alice });
|
2023-05-07 08:38:06 +00:00
|
|
|
|
const expected = await Promise.all(user().map(u => show(u.id, alice)));
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.deepStrictEqual(response, expected);
|
|
|
|
|
});
|
|
|
|
|
test.each([
|
2024-03-03 11:15:35 +00:00
|
|
|
|
{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable },
|
|
|
|
|
{ label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice },
|
|
|
|
|
{ label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice },
|
|
|
|
|
{ label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice },
|
|
|
|
|
{ label: '承認制ユーザーが含まれる', user: () => userLocking },
|
|
|
|
|
{ label: 'サイレンスユーザーが含まれる', user: () => userSilenced },
|
|
|
|
|
{ label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true },
|
|
|
|
|
{ label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf },
|
|
|
|
|
{ label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin },
|
2023-04-12 04:20:16 +00:00
|
|
|
|
] as const)('をID&ホスト指定で検索でき、結果に$label', async ({ user, excluded }) => {
|
|
|
|
|
const parameters = { username: user().username };
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'users/search-by-username-and-host', parameters, user: alice });
|
2023-05-07 08:38:06 +00:00
|
|
|
|
const expected = (excluded ?? false) ? [] : [await show(user().id, alice)];
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.deepStrictEqual(response, expected);
|
|
|
|
|
});
|
|
|
|
|
test.todo('をID&ホスト指定で検索できる(リモート)');
|
|
|
|
|
|
|
|
|
|
//#endregion
|
|
|
|
|
//#region ID指定検索(users/get-frequently-replied-users)
|
|
|
|
|
|
|
|
|
|
test('がよくリプライをするユーザーのリストを取得できる', async () => {
|
|
|
|
|
const parameters = { userId: alice.id, limit: 5 };
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'users/get-frequently-replied-users', parameters, user: alice });
|
2023-06-24 23:34:18 +00:00
|
|
|
|
const expected = await Promise.all(usersReplying.slice(0, parameters.limit).map(async (s, i) => ({
|
2023-05-07 08:38:06 +00:00
|
|
|
|
user: await show(s.id, alice),
|
2023-04-12 04:20:16 +00:00
|
|
|
|
weight: (usersReplying.length - i) / usersReplying.length,
|
|
|
|
|
})));
|
|
|
|
|
assert.deepStrictEqual(response, expected);
|
|
|
|
|
});
|
|
|
|
|
test.each([
|
2024-03-03 11:15:35 +00:00
|
|
|
|
{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable },
|
|
|
|
|
{ label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice },
|
|
|
|
|
{ label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice },
|
|
|
|
|
{ label: 'ブロックしてきているユーザーが含まれない', user: () => userBlockingAlice, excluded: true },
|
|
|
|
|
{ label: '承認制ユーザーが含まれる', user: () => userLocking },
|
|
|
|
|
{ label: 'サイレンスユーザーが含まれる', user: () => userSilenced },
|
|
|
|
|
//{ label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true },
|
|
|
|
|
{ label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf },
|
|
|
|
|
{ label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin },
|
2023-04-12 04:20:16 +00:00
|
|
|
|
] as const)('がよくリプライをするユーザーのリストを取得でき、結果に$label', async ({ user, excluded }) => {
|
|
|
|
|
const replyTo = (await successfulApiCall({ endpoint: 'users/notes', parameters: { userId: user().id }, user: undefined }))[0];
|
|
|
|
|
await post(alice, { text: `@${user().username} test`, replyId: replyTo.id });
|
|
|
|
|
const parameters = { userId: alice.id, limit: 100 };
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'users/get-frequently-replied-users', parameters, user: alice });
|
2023-05-07 08:38:06 +00:00
|
|
|
|
const expected = (excluded ?? false) ? [] : [await show(user().id, alice)];
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.deepStrictEqual(response.map(s => s.user).filter((u) => u.id === user().id), expected);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
//#endregion
|
|
|
|
|
//#region ハッシュタグ(hashtags/users)
|
|
|
|
|
|
|
|
|
|
test.each([
|
2024-03-03 11:15:35 +00:00
|
|
|
|
{ label: 'フォロワー昇順', sort: { sort: '+follower' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.followersCount) },
|
|
|
|
|
{ label: 'フォロワー降順', sort: { sort: '-follower' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.followersCount) },
|
|
|
|
|
{ label: '登録日時昇順', sort: { sort: '+createdAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => u.createdAt },
|
|
|
|
|
{ label: '登録日時降順', sort: { sort: '-createdAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => u.createdAt },
|
|
|
|
|
{ label: '投稿日時昇順', sort: { sort: '+updatedAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.updatedAt) },
|
|
|
|
|
{ label: '投稿日時降順', sort: { sort: '-updatedAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.updatedAt) },
|
2023-04-12 04:20:16 +00:00
|
|
|
|
] as const)('をハッシュタグ指定で取得することができる($label)', async ({ sort, selector }) => {
|
|
|
|
|
const hashtag = 'test_hashtag';
|
|
|
|
|
await successfulApiCall({ endpoint: 'i/update', parameters: { description: `#${hashtag}` }, user: alice });
|
|
|
|
|
const parameters = { tag: hashtag, limit: 5, ...sort };
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'hashtags/users', parameters, user: alice });
|
2023-05-07 08:38:06 +00:00
|
|
|
|
const users = await Promise.all(response.map(u => show(u.id, alice)));
|
2023-04-12 04:20:16 +00:00
|
|
|
|
const expected = users.sort((x, y) => {
|
|
|
|
|
const index = (selector(x) < selector(y)) ? -1 : (selector(x) > selector(y)) ? 1 : 0;
|
|
|
|
|
return index * (parameters.sort.startsWith('+') ? -1 : 1);
|
|
|
|
|
});
|
|
|
|
|
assert.deepStrictEqual(response, expected);
|
|
|
|
|
});
|
|
|
|
|
test.each([
|
2024-03-03 11:15:35 +00:00
|
|
|
|
{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable },
|
|
|
|
|
{ label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice },
|
|
|
|
|
{ label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice },
|
|
|
|
|
{ label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice },
|
|
|
|
|
{ label: '承認制ユーザーが含まれる', user: () => userLocking },
|
|
|
|
|
{ label: 'サイレンスユーザーが含まれる', user: () => userSilenced },
|
|
|
|
|
{ label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true },
|
|
|
|
|
{ label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf },
|
|
|
|
|
{ label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin },
|
2023-05-07 08:38:06 +00:00
|
|
|
|
] as const)('をハッシュタグ指定で取得することができ、結果に$label', async ({ user, excluded }) => {
|
2023-04-12 04:20:16 +00:00
|
|
|
|
const hashtag = `user_test${user().username}`;
|
|
|
|
|
if (user() !== userSuspended) {
|
|
|
|
|
// サスペンドユーザーはupdateできない。
|
|
|
|
|
await successfulApiCall({ endpoint: 'i/update', parameters: { description: `#${hashtag}` }, user: user() });
|
|
|
|
|
}
|
|
|
|
|
const parameters = { tag: hashtag, limit: 100, sort: '-follower' } as const;
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'hashtags/users', parameters, user: alice });
|
2023-05-07 08:38:06 +00:00
|
|
|
|
const expected = (excluded ?? false) ? [] : [await show(user().id, alice)];
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.deepStrictEqual(response, expected);
|
|
|
|
|
});
|
|
|
|
|
test.todo('をハッシュタグ指定で取得することができる(リモート)');
|
|
|
|
|
|
|
|
|
|
//#endregion
|
|
|
|
|
//#region オススメユーザー(users/recommendation)
|
|
|
|
|
|
|
|
|
|
// BUG users/recommendationは壊れている? > QueryFailedError: missing FROM-clause entry for table "note"
|
|
|
|
|
test.skip('のオススメを取得することができる', async () => {
|
|
|
|
|
const parameters = {};
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'users/recommendation', parameters, user: alice });
|
|
|
|
|
const expected = await Promise.all(response.map(u => show(u.id)));
|
|
|
|
|
assert.deepStrictEqual(response, expected);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
//#endregion
|
|
|
|
|
//#region ピン止めユーザー(pinned-users)
|
|
|
|
|
|
|
|
|
|
test('のピン止めユーザーを取得することができる', async () => {
|
|
|
|
|
await successfulApiCall({ endpoint: 'admin/update-meta', parameters: { pinnedUsers: [bob.username, `@${carol.username}`] }, user: root });
|
|
|
|
|
const parameters = {} as const;
|
|
|
|
|
const response = await successfulApiCall({ endpoint: 'pinned-users', parameters, user: alice });
|
2023-05-07 08:38:06 +00:00
|
|
|
|
const expected = await Promise.all([bob, carol].map(u => show(u.id, alice)));
|
2023-04-12 04:20:16 +00:00
|
|
|
|
assert.deepStrictEqual(response, expected);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
//#endregion
|
|
|
|
|
|
|
|
|
|
test.todo('を管理人として確認することができる(admin/show-user)');
|
|
|
|
|
test.todo('を管理人として確認することができる(admin/show-users)');
|
|
|
|
|
test.todo('をサーバー向けに取得することができる(federation/users)');
|
|
|
|
|
});
|