Merge branch 'develop' into feat-12997
This commit is contained in:
commit
e8f158fa3c
|
@ -0,0 +1,40 @@
|
||||||
|
name: "Release Manager: sync changelog with PR"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- release/**
|
||||||
|
paths:
|
||||||
|
- 'CHANGELOG.md'
|
||||||
|
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
edit:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
# headがrelease/かつopenのPRを1つ取得
|
||||||
|
- name: Get PR
|
||||||
|
run: |
|
||||||
|
echo "pr_number=$(gh pr list --limit 1 --head "${{ github.ref_name }}" --json number --jq '.[] | .number')" >> $GITHUB_OUTPUT
|
||||||
|
id: get_pr
|
||||||
|
- name: Get target version
|
||||||
|
uses: misskey-dev/release-manager-actions/.github/actions/get-target-version@v1
|
||||||
|
id: v
|
||||||
|
# CHANGELOG.mdの内容を取得
|
||||||
|
- name: Get changelog
|
||||||
|
uses: misskey-dev/release-manager-actions/.github/actions/get-changelog@v1
|
||||||
|
with:
|
||||||
|
version: ${{ steps.v.outputs.target_version }}
|
||||||
|
id: changelog
|
||||||
|
# PRのnotesを更新
|
||||||
|
- name: Update PR
|
||||||
|
run: |
|
||||||
|
gh pr edit ${{ steps.get_pr.outputs.pr_number }} --body "${{ steps.changelog.outputs.changelog }}"
|
|
@ -0,0 +1,122 @@
|
||||||
|
name: "Release Manager [Dispatch]"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
## Specify the type of the next release.
|
||||||
|
#version_increment_type:
|
||||||
|
# type: choice
|
||||||
|
# description: 'VERSION INCREMENT TYPE'
|
||||||
|
# default: 'patch'
|
||||||
|
# required: false
|
||||||
|
# options:
|
||||||
|
# - 'major'
|
||||||
|
# - 'minor'
|
||||||
|
# - 'patch'
|
||||||
|
merge:
|
||||||
|
type: boolean
|
||||||
|
description: 'MERGE RELEASE BRANCH TO MAIN'
|
||||||
|
default: false
|
||||||
|
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
get-pr:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
pr_number: ${{ steps.get_pr.outputs.pr_number }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
# headがrelease/かつopenのPRを1つ取得
|
||||||
|
- name: Get PRs
|
||||||
|
run: |
|
||||||
|
echo "pr_number=$(gh pr list --limit 1 --search "head:release/ is:open" --json number --jq '.[] | .number')" >> $GITHUB_OUTPUT
|
||||||
|
id: get_pr
|
||||||
|
|
||||||
|
merge:
|
||||||
|
uses: misskey-dev/release-manager-actions/.github/workflows/merge.yml@v1
|
||||||
|
needs: get-pr
|
||||||
|
if: ${{ needs.get-pr.outputs.pr_number != '' && inputs.merge == true }}
|
||||||
|
with:
|
||||||
|
pr_number: ${{ needs.get-pr.outputs.pr_number }}
|
||||||
|
package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
|
||||||
|
# Text to prepend to the changelog
|
||||||
|
# The first line must be `## Unreleased`
|
||||||
|
changes_template: |
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
### General
|
||||||
|
-
|
||||||
|
|
||||||
|
### Client
|
||||||
|
-
|
||||||
|
|
||||||
|
### Server
|
||||||
|
-
|
||||||
|
|
||||||
|
use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
|
||||||
|
secrets:
|
||||||
|
RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
|
||||||
|
RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
|
||||||
|
RULESET_EDIT_APP_ID: ${{ secrets.RULESET_EDIT_APP_ID }}
|
||||||
|
RULESET_EDIT_APP_PRIVATE_KEY: ${{ secrets.RULESET_EDIT_APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
|
create-prerelease:
|
||||||
|
uses: misskey-dev/release-manager-actions/.github/workflows/create-prerelease.yml@v1
|
||||||
|
needs: get-pr
|
||||||
|
if: ${{ needs.get-pr.outputs.pr_number != '' && inputs.merge != true }}
|
||||||
|
with:
|
||||||
|
pr_number: ${{ needs.get-pr.outputs.pr_number }}
|
||||||
|
package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
|
||||||
|
use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
|
||||||
|
secrets:
|
||||||
|
RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
|
||||||
|
RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
|
create-target:
|
||||||
|
uses: misskey-dev/release-manager-actions/.github/workflows/create-target.yml@v1
|
||||||
|
needs: get-pr
|
||||||
|
if: ${{ needs.get-pr.outputs.pr_number == '' }}
|
||||||
|
with:
|
||||||
|
# The script for version increment.
|
||||||
|
# process.env.CURRENT_VERSION: The current version.
|
||||||
|
#
|
||||||
|
# Misskey calender versioning (yyyy.MM.patch) example
|
||||||
|
version_increment_script: |
|
||||||
|
const now = new Date();
|
||||||
|
const year = now.toLocaleDateString('en-US', { year: 'numeric', timeZone: 'Asia/Tokyo' });
|
||||||
|
const month = now.toLocaleDateString('en-US', { month: 'numeric', timeZone: 'Asia/Tokyo' });
|
||||||
|
const [major, minor, _patch] = process.env.CURRENT_VERSION.split('.');
|
||||||
|
const patch = Number(_patch.split('-')[0]);
|
||||||
|
if (Number.isNaN(patch)) {
|
||||||
|
console.error('Invalid patch version', year, month, process.env.CURRENT_VERSION, major, minor, _patch);
|
||||||
|
throw new Error('Invalid patch version');
|
||||||
|
}
|
||||||
|
if (year !== major || month !== minor) {
|
||||||
|
return `${year}.${month}.0`;
|
||||||
|
} else {
|
||||||
|
return `${major}.${minor}.${patch + 1}`;
|
||||||
|
}
|
||||||
|
##Semver example
|
||||||
|
#version_increment_script: |
|
||||||
|
# const [major, minor, patch] = process.env.CURRENT_VERSION.split('.');
|
||||||
|
# if ("${{ inputs.version_increment_type }}" === "major") {
|
||||||
|
# return `${Number(major) + 1}.0.0`;
|
||||||
|
# } else if ("${{ inputs.version_increment_type }}" === "minor") {
|
||||||
|
# return `${major}.${Number(minor) + 1}.0`;
|
||||||
|
# } else {
|
||||||
|
# return `${major}.${minor}.${Number(patch) + 1}`;
|
||||||
|
# }
|
||||||
|
package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
|
||||||
|
use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
|
||||||
|
secrets:
|
||||||
|
RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
|
||||||
|
RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
|
||||||
|
RULESET_EDIT_APP_ID: ${{ secrets.RULESET_EDIT_APP_ID }}
|
||||||
|
RULESET_EDIT_APP_PRIVATE_KEY: ${{ secrets.RULESET_EDIT_APP_PRIVATE_KEY }}
|
|
@ -0,0 +1,38 @@
|
||||||
|
name: "Release Manager: release RC when ready for review"
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [ready_for_review]
|
||||||
|
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
ref: ${{ steps.get_pr.outputs.ref }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
# PR情報を取得
|
||||||
|
- name: Get PR
|
||||||
|
run: |
|
||||||
|
pr_json=$(gh pr view ${{ github.event.pull_request.number }} --json isDraft,headRefName)
|
||||||
|
echo "ref=$(echo $pr_json | jq -r '.headRefName')" >> $GITHUB_OUTPUT
|
||||||
|
id: get_pr
|
||||||
|
release:
|
||||||
|
uses: misskey-dev/release-manager-actions/.github/workflows/create-prerelease.yml@v1
|
||||||
|
needs: check
|
||||||
|
if: startsWith(needs.check.outputs.ref, 'release/')
|
||||||
|
with:
|
||||||
|
pr_number: ${{ github.event.pull_request.number }}
|
||||||
|
package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
|
||||||
|
use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
|
||||||
|
secrets:
|
||||||
|
RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
|
||||||
|
RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
|
24
CHANGELOG.md
24
CHANGELOG.md
|
@ -1,5 +1,4 @@
|
||||||
<!--
|
## Unreleased
|
||||||
## 202x.x.x (unreleased)
|
|
||||||
|
|
||||||
### General
|
### General
|
||||||
-
|
-
|
||||||
|
@ -10,12 +9,25 @@
|
||||||
### Server
|
### Server
|
||||||
-
|
-
|
||||||
|
|
||||||
-->
|
## 2024.3.1
|
||||||
## 202x.x.x (unreleased)
|
|
||||||
|
### General
|
||||||
|
-
|
||||||
|
|
||||||
|
### Client
|
||||||
|
- Fix: 絵文字関係の不具合を修正 (#13485)
|
||||||
|
- 履歴に残っている or ピン留めされた絵文字がコントロールパネルより削除されていた際にリアクションデッキが表示できなくなる
|
||||||
|
- Unicode絵文字が履歴に残っている or ピン留めされているとリアクションデッキが表示できなくなる
|
||||||
|
- Fix: カスタム絵文字の画像読み込みに失敗した際はテキストではなくダミー画像を表示 #13487
|
||||||
|
|
||||||
|
### Server
|
||||||
|
-
|
||||||
|
|
||||||
|
## 2024.3.0
|
||||||
|
|
||||||
### General
|
### General
|
||||||
- Enhance: 投稿者のロールに応じて、一つのノートに含むことのできるメンションとダイレクト投稿の宛先の人数に上限を設定できるように
|
- Enhance: 投稿者のロールに応じて、一つのノートに含むことのできるメンションとダイレクト投稿の宛先の人数に上限を設定できるように
|
||||||
* デフォルトのメンション上限は20アカウントに設定されます。(管理者はベースロールの設定で変更可能です。)
|
* デフォルトのメンション上限は20アカウントに設定されます。(管理者はベースロールの設定で変更可能です。)
|
||||||
* 連合の問い合わせに応答しないサーバーのリモートユーザーへのメンションは、上限の人数に含めない実装になっています。
|
* 連合の問い合わせに応答しないサーバーのリモートユーザーへのメンションは、上限の人数に含めない実装になっています。
|
||||||
- Enhance: 通知がミュート、凍結を考慮するようになりました
|
- Enhance: 通知がミュート、凍結を考慮するようになりました
|
||||||
- Enhance: サーバーごとにモデレーションノートを残せるように
|
- Enhance: サーバーごとにモデレーションノートを残せるように
|
||||||
|
@ -33,6 +45,7 @@
|
||||||
- Fix: 設定のバックアップ作成時に名前を入力しなかった場合、ローカライゼーションがおかしくなる問題を修正
|
- Fix: 設定のバックアップ作成時に名前を入力しなかった場合、ローカライゼーションがおかしくなる問題を修正
|
||||||
- Fix: ページ`/admin/emojis`の絵文字編集ダイアログで「リアクションとして使えるロール」を追加する際に何も選択せずOKを押下すると画面が固まる問題を修正
|
- Fix: ページ`/admin/emojis`の絵文字編集ダイアログで「リアクションとして使えるロール」を追加する際に何も選択せずOKを押下すると画面が固まる問題を修正
|
||||||
- Fix: 絵文字サジェストの順位で、絵文字自体の名前が同じものよりもタグで一致しているものが優先されてしまう問題を修正
|
- Fix: 絵文字サジェストの順位で、絵文字自体の名前が同じものよりもタグで一致しているものが優先されてしまう問題を修正
|
||||||
|
- Fix: ユーザの情報のポップアップが消えなくなることがある問題を修正
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
- Enhance: エンドポイント`flash/update`の`flashId`以外のパラメータは必須ではなくなりました
|
- Enhance: エンドポイント`flash/update`の`flashId`以外のパラメータは必須ではなくなりました
|
||||||
|
@ -119,7 +132,6 @@
|
||||||
- Fix: エラー画像URLを設定した後解除すると,デフォルトの画像が表示されない問題の修正
|
- Fix: エラー画像URLを設定した後解除すると,デフォルトの画像が表示されない問題の修正
|
||||||
- Fix: MkCodeEditorで行がずれていってしまう問題の修正
|
- Fix: MkCodeEditorで行がずれていってしまう問題の修正
|
||||||
- Fix: Summaly proxy利用時にプレイヤーが動作しないことがあるのを修正 #13196
|
- Fix: Summaly proxy利用時にプレイヤーが動作しないことがあるのを修正 #13196
|
||||||
- Fix: ユーザの情報のポップアップが消えなくなることがある問題を修正
|
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
- Enhance: 連合先のレートリミットを超過した際にリトライするようになりました
|
- Enhance: 連合先のレートリミットを超過した際にリトライするようになりました
|
||||||
|
|
41
README.md
41
README.md
|
@ -1,9 +1,11 @@
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://misskey-hub.net">
|
<a href="https://misskey-hub.net">
|
||||||
<img src="./assets/title_float.svg" alt="Misskey logo" style="border-radius:50%" width="400"/>
|
<img src="./assets/title_float.svg" alt="Misskey logo" style="border-radius:50%" width="300"/>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
**🌎 **[Misskey](https://misskey-hub.net/)** is an open source, decentralized social media platform that's free forever! 🚀**
|
**🌎 **Misskey** is an open source, federated social media platform that's free forever! 🚀**
|
||||||
|
|
||||||
|
[Learn more](https://misskey-hub.net/)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -22,41 +24,6 @@
|
||||||
<a href="https://www.patreon.com/syuilo">
|
<a href="https://www.patreon.com/syuilo">
|
||||||
<img src="https://custom-icon-badges.herokuapp.com/badge/become_a-patron-F96854?logoColor=F96854&style=for-the-badge&logo=patreon&labelColor=363B40" alt="become a patron"/></a>
|
<img src="https://custom-icon-badges.herokuapp.com/badge/become_a-patron-F96854?logoColor=F96854&style=for-the-badge&logo=patreon&labelColor=363B40" alt="become a patron"/></a>
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
[![codecov](https://codecov.io/gh/misskey-dev/misskey/branch/develop/graph/badge.svg?token=R6IQZ3QJOL)](https://codecov.io/gh/misskey-dev/misskey)
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<a href="https://xn--931a.moe/"><img src="https://github.com/misskey-dev/misskey/blob/develop/assets/ai.png?raw=true" align="right" height="320px"/></a>
|
|
||||||
|
|
||||||
## ✨ Features
|
|
||||||
- **ActivityPub support**\
|
|
||||||
Not on Misskey? No problem! Not only can Misskey instances talk to each other, but you can make friends with people on other networks like Mastodon and Pixelfed!
|
|
||||||
- **Reactions**\
|
|
||||||
You can add emoji reactions to any post! No longer are you bound by a like button, show everyone exactly how you feel with the tap of a button.
|
|
||||||
- **Drive**\
|
|
||||||
With Misskey's built in drive, you get cloud storage right in your social media, where you can upload any files, make folders, and find media from posts you've made!
|
|
||||||
- **Rich Web UI**\
|
|
||||||
Misskey has a rich and easy to use Web UI!
|
|
||||||
It is highly customizable, from changing the layout and adding widgets to making custom themes.
|
|
||||||
Furthermore, plugins can be created using AiScript, an original programming language.
|
|
||||||
- And much more...
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="clear: both;"></div>
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
Misskey Documentation can be found at [Misskey Hub](https://misskey-hub.net/docs/), some of the links and graphics above also lead to specific portions of it.
|
|
||||||
|
|
||||||
## Sponsors
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
<a class="rss3" title="RSS3" href="https://rss3.io/" target="_blank"><img src="https://rss3.mypinata.cloud/ipfs/QmUG6H3Z7D5P511shn7sB4CPmpjH5uZWu4m5mWX7U3Gqbu" alt="RSS3" height="60"></a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Thanks
|
## Thanks
|
||||||
|
|
|
@ -380,8 +380,11 @@ hcaptcha: "hCaptcha"
|
||||||
enableHcaptcha: "Activer hCaptcha"
|
enableHcaptcha: "Activer hCaptcha"
|
||||||
hcaptchaSiteKey: "Clé du site"
|
hcaptchaSiteKey: "Clé du site"
|
||||||
hcaptchaSecretKey: "Clé secrète"
|
hcaptchaSecretKey: "Clé secrète"
|
||||||
|
mcaptcha: "mCaptcha"
|
||||||
|
enableMcaptcha: "Activer mCaptcha"
|
||||||
mcaptchaSiteKey: "Clé du site"
|
mcaptchaSiteKey: "Clé du site"
|
||||||
mcaptchaSecretKey: "Clé secrète"
|
mcaptchaSecretKey: "Clé secrète"
|
||||||
|
mcaptchaInstanceUrl: "URL de l'instance de mCaptcha"
|
||||||
recaptcha: "reCAPTCHA"
|
recaptcha: "reCAPTCHA"
|
||||||
enableRecaptcha: "Activer reCAPTCHA"
|
enableRecaptcha: "Activer reCAPTCHA"
|
||||||
recaptchaSiteKey: "Clé du site"
|
recaptchaSiteKey: "Clé du site"
|
||||||
|
@ -523,7 +526,7 @@ hideThisNote: "Masquer cette note"
|
||||||
showFeaturedNotesInTimeline: "Afficher les notes des Tendances dans le fil d'actualité"
|
showFeaturedNotesInTimeline: "Afficher les notes des Tendances dans le fil d'actualité"
|
||||||
objectStorage: "Stockage d'objets"
|
objectStorage: "Stockage d'objets"
|
||||||
useObjectStorage: "Utiliser le stockage d'objets"
|
useObjectStorage: "Utiliser le stockage d'objets"
|
||||||
objectStorageBaseUrl: "Base URL"
|
objectStorageBaseUrl: "URL de base"
|
||||||
objectStorageBaseUrlDesc: "Préfixe d’URL utilisé pour construire l’URL vers le référencement d’objet (média). Spécifiez son URL si vous utilisez un CDN ou un proxy, sinon spécifiez l’adresse accessible au public selon le guide de service que vous allez utiliser. P.ex. 'https://<bucket>.s3.amazonaws.com' pour AWS S3 et 'https://storage.googleapis.com/<bucket>' pour GCS."
|
objectStorageBaseUrlDesc: "Préfixe d’URL utilisé pour construire l’URL vers le référencement d’objet (média). Spécifiez son URL si vous utilisez un CDN ou un proxy, sinon spécifiez l’adresse accessible au public selon le guide de service que vous allez utiliser. P.ex. 'https://<bucket>.s3.amazonaws.com' pour AWS S3 et 'https://storage.googleapis.com/<bucket>' pour GCS."
|
||||||
objectStorageBucket: "Bucket"
|
objectStorageBucket: "Bucket"
|
||||||
objectStorageBucketDesc: "Veuillez spécifier le nom du compartiment utilisé sur le service configuré."
|
objectStorageBucketDesc: "Veuillez spécifier le nom du compartiment utilisé sur le service configuré."
|
||||||
|
@ -628,6 +631,7 @@ medium: "Moyen"
|
||||||
small: "Petit"
|
small: "Petit"
|
||||||
generateAccessToken: "Générer un jeton d'accès"
|
generateAccessToken: "Générer un jeton d'accès"
|
||||||
permission: "Autorisations "
|
permission: "Autorisations "
|
||||||
|
adminPermission: "Droits de l'administrateur"
|
||||||
enableAll: "Tout activer"
|
enableAll: "Tout activer"
|
||||||
disableAll: "Tout désactiver"
|
disableAll: "Tout désactiver"
|
||||||
tokenRequested: "Autoriser l'accès au compte"
|
tokenRequested: "Autoriser l'accès au compte"
|
||||||
|
@ -1031,12 +1035,18 @@ nonSensitiveOnlyForLocalLikeOnlyForRemote: "Non sensibles seulement (mentions j'
|
||||||
rolesAssignedToMe: "Rôles attribués à moi"
|
rolesAssignedToMe: "Rôles attribués à moi"
|
||||||
resetPasswordConfirm: "Souhaitez-vous réinitialiser votre mot de passe ?"
|
resetPasswordConfirm: "Souhaitez-vous réinitialiser votre mot de passe ?"
|
||||||
sensitiveWords: "Mots sensibles"
|
sensitiveWords: "Mots sensibles"
|
||||||
|
sensitiveWordsDescription2: "Séparer par une espace pour créer une expression AND ; entourer de barres obliques pour créer une expression régulière."
|
||||||
|
prohibitedWords: "Mots interdits"
|
||||||
|
prohibitedWordsDescription2: "Séparer par une espace pour créer une expression AND ; entourer de barres obliques pour créer une expression régulière."
|
||||||
hiddenTags: "Hashtags cachés"
|
hiddenTags: "Hashtags cachés"
|
||||||
hiddenTagsDescription: "Les hashtags définis ne s'afficheront pas dans les tendances. Vous pouvez définir plusieurs hashtags en faisant un saut de ligne."
|
hiddenTagsDescription: "Les hashtags définis ne s'afficheront pas dans les tendances. Vous pouvez définir plusieurs hashtags en faisant un saut de ligne."
|
||||||
notesSearchNotAvailable: "La recherche de notes n'est pas disponible."
|
notesSearchNotAvailable: "La recherche de notes n'est pas disponible."
|
||||||
license: "Licence"
|
license: "Licence"
|
||||||
|
unfavoriteConfirm: "Vraiment supprimer des favoris ?"
|
||||||
myClips: "Mes clips"
|
myClips: "Mes clips"
|
||||||
drivecleaner: "Nettoyeur du Disque"
|
drivecleaner: "Nettoyeur du Disque"
|
||||||
|
retryAllQueuesNow: "Réessayer tous les fils d'attente immédiatement"
|
||||||
|
retryAllQueuesConfirmTitle: "Vraiment réessayer ?"
|
||||||
retryAllQueuesConfirmText: "Cela peut augmenter temporairement la charge du serveur."
|
retryAllQueuesConfirmText: "Cela peut augmenter temporairement la charge du serveur."
|
||||||
enableChartsForRemoteUser: "Générer les graphiques pour les utilisateurs distants"
|
enableChartsForRemoteUser: "Générer les graphiques pour les utilisateurs distants"
|
||||||
enableChartsForFederatedInstances: "Générer les graphiques pour les instances distantes"
|
enableChartsForFederatedInstances: "Générer les graphiques pour les instances distantes"
|
||||||
|
@ -1046,6 +1056,8 @@ limitWidthOfReaction: "Limiter la largeur maximale des réactions et les affiche
|
||||||
noteIdOrUrl: "Identifiant de la note ou URL"
|
noteIdOrUrl: "Identifiant de la note ou URL"
|
||||||
video: "Vidéo"
|
video: "Vidéo"
|
||||||
videos: "Vidéos"
|
videos: "Vidéos"
|
||||||
|
audio: "Audio"
|
||||||
|
audioFiles: "Fichiers audio"
|
||||||
dataSaver: "Économiseur de données"
|
dataSaver: "Économiseur de données"
|
||||||
accountMigration: "Migration de compte"
|
accountMigration: "Migration de compte"
|
||||||
accountMoved: "Cet·te utilisateur·rice a migré son compte vers :"
|
accountMoved: "Cet·te utilisateur·rice a migré son compte vers :"
|
||||||
|
@ -1084,7 +1096,10 @@ specifyUser: "Spécifier l'utilisateur·rice"
|
||||||
failedToPreviewUrl: "Aperçu d'URL échoué"
|
failedToPreviewUrl: "Aperçu d'URL échoué"
|
||||||
update: "Mettre à jour"
|
update: "Mettre à jour"
|
||||||
rolesThatCanBeUsedThisEmojiAsReaction: "Rôles qui peuvent utiliser cet émoji comme réaction"
|
rolesThatCanBeUsedThisEmojiAsReaction: "Rôles qui peuvent utiliser cet émoji comme réaction"
|
||||||
|
rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "Si aucun rôle n'est spécifié, tout le monde peut utiliser cet émoji comme réaction."
|
||||||
|
rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn: "Il faut un rôle public."
|
||||||
cancelReactionConfirm: "Supprimez la réaction ?"
|
cancelReactionConfirm: "Supprimez la réaction ?"
|
||||||
|
changeReactionConfirm: "Changer la réaction ?"
|
||||||
later: "Plus tard"
|
later: "Plus tard"
|
||||||
goToMisskey: "Retour vers Misskey"
|
goToMisskey: "Retour vers Misskey"
|
||||||
additionalEmojiDictionary: "Dictionnaires d'émojis additionnels"
|
additionalEmojiDictionary: "Dictionnaires d'émojis additionnels"
|
||||||
|
@ -1110,11 +1125,13 @@ used: "Utilisé"
|
||||||
expired: "Expiré"
|
expired: "Expiré"
|
||||||
doYouAgree: "Êtes-vous d’accord ?"
|
doYouAgree: "Êtes-vous d’accord ?"
|
||||||
beSureToReadThisAsItIsImportant: "Assurez-vous de le lire ; c'est important."
|
beSureToReadThisAsItIsImportant: "Assurez-vous de le lire ; c'est important."
|
||||||
|
iHaveReadXCarefullyAndAgree: "J'ai lu le contenu de « {x} » et donne mon accord."
|
||||||
dialog: "Dialogue"
|
dialog: "Dialogue"
|
||||||
icon: "Avatar"
|
icon: "Avatar"
|
||||||
forYou: "Pour vous"
|
forYou: "Pour vous"
|
||||||
currentAnnouncements: "Annonces actuelles"
|
currentAnnouncements: "Annonces actuelles"
|
||||||
pastAnnouncements: "Annonces passées"
|
pastAnnouncements: "Annonces passées"
|
||||||
|
youHaveUnreadAnnouncements: "Il y a des annonces non lues."
|
||||||
replies: "Réponses"
|
replies: "Réponses"
|
||||||
renotes: "Renotes"
|
renotes: "Renotes"
|
||||||
loadReplies: "Inclure les réponses"
|
loadReplies: "Inclure les réponses"
|
||||||
|
@ -1129,6 +1146,7 @@ showRenotes: "Afficher les renotes"
|
||||||
edited: "Modifié"
|
edited: "Modifié"
|
||||||
notificationRecieveConfig: "Paramètres des notifications"
|
notificationRecieveConfig: "Paramètres des notifications"
|
||||||
mutualFollow: "Abonnement mutuel"
|
mutualFollow: "Abonnement mutuel"
|
||||||
|
fileAttachedOnly: "Avec fichiers joints seulement"
|
||||||
showRepliesToOthersInTimeline: "Afficher les réponses aux autres dans le fil"
|
showRepliesToOthersInTimeline: "Afficher les réponses aux autres dans le fil"
|
||||||
hideRepliesToOthersInTimeline: "Masquer les réponses aux autres dans le fil"
|
hideRepliesToOthersInTimeline: "Masquer les réponses aux autres dans le fil"
|
||||||
showRepliesToOthersInTimelineAll: "Afficher les réponses de toutes les personnes que vous suivez dans le fil"
|
showRepliesToOthersInTimelineAll: "Afficher les réponses de toutes les personnes que vous suivez dans le fil"
|
||||||
|
@ -1137,6 +1155,11 @@ confirmShowRepliesAll: "Cette opération est irréversible. Voulez-vous vraiment
|
||||||
confirmHideRepliesAll: "Cette opération est irréversible. Voulez-vous vraiment masquer les réponses de toutes les personnes que vous suivez dans le fil ?"
|
confirmHideRepliesAll: "Cette opération est irréversible. Voulez-vous vraiment masquer les réponses de toutes les personnes que vous suivez dans le fil ?"
|
||||||
externalServices: "Services externes"
|
externalServices: "Services externes"
|
||||||
sourceCode: "Code source"
|
sourceCode: "Code source"
|
||||||
|
sourceCodeIsNotYetProvided: "Le code source n'est pas encore disponible. Veuillez signaler ce problème aux administrateurs."
|
||||||
|
repositoryUrl: "URL du dépôt"
|
||||||
|
repositoryUrlDescription: "Entrez l'URL du dépôt où se trouve le code source ici. Si vous utilisez Misskey tel quel (sans changer le code source), entrez https://github.com/misskey-dev/misskey"
|
||||||
|
feedback: "Commentaires"
|
||||||
|
feedbackUrl: "URL pour les commentaires"
|
||||||
impressum: "Impressum"
|
impressum: "Impressum"
|
||||||
impressumUrl: "URL de l'impressum"
|
impressumUrl: "URL de l'impressum"
|
||||||
impressumDescription: "Dans certains pays comme l'Allemagne, il est obligatoire d'afficher les informations sur l'opérateur d'un site (un impressum)."
|
impressumDescription: "Dans certains pays comme l'Allemagne, il est obligatoire d'afficher les informations sur l'opérateur d'un site (un impressum)."
|
||||||
|
@ -1164,11 +1187,32 @@ remainingN: "Restants : {n}"
|
||||||
overwriteContentConfirm: "Voulez-vous remplacer le contenu actuel ?"
|
overwriteContentConfirm: "Voulez-vous remplacer le contenu actuel ?"
|
||||||
seasonalScreenEffect: "Effet d'écran saisonnier"
|
seasonalScreenEffect: "Effet d'écran saisonnier"
|
||||||
decorate: "Décorer"
|
decorate: "Décorer"
|
||||||
|
addMfmFunction: "Insérer MFM"
|
||||||
|
enableQuickAddMfmFunction: "Afficher le sélecteur de MFM avancé"
|
||||||
|
bubbleGame: "Jeu de bulles"
|
||||||
sfx: "Effets sonores"
|
sfx: "Effets sonores"
|
||||||
|
soundWillBePlayed: "Le son sera joué"
|
||||||
showReplay: "Voir le replay"
|
showReplay: "Voir le replay"
|
||||||
|
replay: "Rediffusion"
|
||||||
|
replaying: "En cours de rediffusion"
|
||||||
|
endReplay: "Arrêter la rediffusion"
|
||||||
|
copyReplayData: "Copier les données de la rediffusion"
|
||||||
ranking: "Classement"
|
ranking: "Classement"
|
||||||
lastNDays: "Derniers {n} jours"
|
lastNDays: "Derniers {n} jours"
|
||||||
|
backToTitle: "Retourner au titre"
|
||||||
|
hemisphere: "Votre région"
|
||||||
|
enableHorizontalSwipe: "Glisser pour changer d'onglet"
|
||||||
|
loading: "Chargement en cours"
|
||||||
surrender: "Annuler"
|
surrender: "Annuler"
|
||||||
|
gameRetry: "Réessayer"
|
||||||
|
_bubbleGame:
|
||||||
|
howToPlay: "Comment jouer"
|
||||||
|
hold: "Réserver"
|
||||||
|
_score:
|
||||||
|
score: "Score"
|
||||||
|
scoreYen: "Montant gagné"
|
||||||
|
highScore: "Meilleur score"
|
||||||
|
yen: "{yen} yens"
|
||||||
_announcement:
|
_announcement:
|
||||||
forExistingUsers: "Pour les utilisateurs existants seulement"
|
forExistingUsers: "Pour les utilisateurs existants seulement"
|
||||||
readConfirmTitle: "Marquer comme lu ?"
|
readConfirmTitle: "Marquer comme lu ?"
|
||||||
|
|
|
@ -1655,6 +1655,7 @@ _role:
|
||||||
gtlAvailable: "瀏覽全域時間軸"
|
gtlAvailable: "瀏覽全域時間軸"
|
||||||
ltlAvailable: "瀏覽本地時間軸"
|
ltlAvailable: "瀏覽本地時間軸"
|
||||||
canPublicNote: "允許公開貼文"
|
canPublicNote: "允許公開貼文"
|
||||||
|
mentionMax: "貼文內的最大提及數"
|
||||||
canInvite: "發行伺服器邀請碼"
|
canInvite: "發行伺服器邀請碼"
|
||||||
inviteLimit: "可建立邀請碼的數量"
|
inviteLimit: "可建立邀請碼的數量"
|
||||||
inviteLimitCycle: "邀請碼的發放間隔"
|
inviteLimitCycle: "邀請碼的發放間隔"
|
||||||
|
@ -2299,6 +2300,7 @@ _notification:
|
||||||
reactedBySomeUsers: "{n}人做出了反應"
|
reactedBySomeUsers: "{n}人做出了反應"
|
||||||
renotedBySomeUsers: "{n}人做了轉發"
|
renotedBySomeUsers: "{n}人做了轉發"
|
||||||
followedBySomeUsers: "被{n}人追隨了"
|
followedBySomeUsers: "被{n}人追隨了"
|
||||||
|
flushNotification: "重置通知歷史紀錄"
|
||||||
_types:
|
_types:
|
||||||
all: "全部 "
|
all: "全部 "
|
||||||
note: "使用者的最新貼文"
|
note: "使用者的最新貼文"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"version": "2024.2.0",
|
"version": "2024.3.1",
|
||||||
"codename": "nasubi",
|
"codename": "nasubi",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -81,9 +81,9 @@
|
||||||
"@fastify/view": "8.2.0",
|
"@fastify/view": "8.2.0",
|
||||||
"@misskey-dev/sharp-read-bmp": "1.2.0",
|
"@misskey-dev/sharp-read-bmp": "1.2.0",
|
||||||
"@misskey-dev/summaly": "5.0.3",
|
"@misskey-dev/summaly": "5.0.3",
|
||||||
"@nestjs/common": "10.2.10",
|
"@nestjs/common": "10.3.3",
|
||||||
"@nestjs/core": "10.2.10",
|
"@nestjs/core": "10.3.3",
|
||||||
"@nestjs/testing": "10.2.10",
|
"@nestjs/testing": "10.3.3",
|
||||||
"@peertube/http-signature": "1.7.0",
|
"@peertube/http-signature": "1.7.0",
|
||||||
"@simplewebauthn/server": "9.0.3",
|
"@simplewebauthn/server": "9.0.3",
|
||||||
"@sinonjs/fake-timers": "11.2.2",
|
"@sinonjs/fake-timers": "11.2.2",
|
||||||
|
@ -159,7 +159,7 @@
|
||||||
"ratelimiter": "3.4.1",
|
"ratelimiter": "3.4.1",
|
||||||
"re2": "1.20.9",
|
"re2": "1.20.9",
|
||||||
"redis-lock": "0.1.4",
|
"redis-lock": "0.1.4",
|
||||||
"reflect-metadata": "0.1.14",
|
"reflect-metadata": "0.2.1",
|
||||||
"rename": "1.0.4",
|
"rename": "1.0.4",
|
||||||
"rss-parser": "3.13.0",
|
"rss-parser": "3.13.0",
|
||||||
"rxjs": "7.8.1",
|
"rxjs": "7.8.1",
|
||||||
|
|
|
@ -51,21 +51,35 @@ export class FetchInstanceMetadataService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async tryLock(host: string): Promise<boolean> {
|
// public for test
|
||||||
const mutex = await this.redisClient.set(`fetchInstanceMetadata:mutex:${host}`, '1', 'GET');
|
public async tryLock(host: string): Promise<string | null> {
|
||||||
return mutex !== '1';
|
// TODO: マイグレーションなのであとで消す (2024.3.1)
|
||||||
|
this.redisClient.del(`fetchInstanceMetadata:mutex:${host}`);
|
||||||
|
|
||||||
|
return await this.redisClient.set(
|
||||||
|
`fetchInstanceMetadata:mutex:v2:${host}`, '1',
|
||||||
|
'EX', 30, // 30秒したら自動でロック解除 https://github.com/misskey-dev/misskey/issues/13506#issuecomment-1975375395
|
||||||
|
'GET' // 古い値を返す(なかったらnull)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public unlock(host: string): Promise<'OK'> {
|
// public for test
|
||||||
return this.redisClient.set(`fetchInstanceMetadata:mutex:${host}`, '0');
|
public unlock(host: string): Promise<number> {
|
||||||
|
return this.redisClient.del(`fetchInstanceMetadata:mutex:v2:${host}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async fetchInstanceMetadata(instance: MiInstance, force = false): Promise<void> {
|
public async fetchInstanceMetadata(instance: MiInstance, force = false): Promise<void> {
|
||||||
const host = instance.host;
|
const host = instance.host;
|
||||||
// Acquire mutex to ensure no parallel runs
|
|
||||||
if (!await this.tryLock(host)) return;
|
// finallyでunlockされてしまうのでtry内でロックチェックをしない
|
||||||
|
// (returnであってもfinallyは実行される)
|
||||||
|
if (!force && await this.tryLock(host) === '1') {
|
||||||
|
// 1が返ってきていたらロックされているという意味なので、何もしない
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!force) {
|
if (!force) {
|
||||||
const _instance = await this.federatedInstanceService.fetch(host);
|
const _instance = await this.federatedInstanceService.fetch(host);
|
||||||
|
|
|
@ -263,7 +263,13 @@ export class NoteCreateService implements OnApplicationShutdown {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', meta.prohibitedWords)) {
|
const hasProhibitedWords = await this.checkProhibitedWordsContain({
|
||||||
|
cw: data.cw,
|
||||||
|
text: data.text,
|
||||||
|
pollChoices: data.poll?.choices,
|
||||||
|
}, meta.prohibitedWords);
|
||||||
|
|
||||||
|
if (hasProhibitedWords) {
|
||||||
throw new IdentifiableError('689ee33f-f97c-479a-ac49-1b9f8140af99', 'Note contains prohibited words');
|
throw new IdentifiableError('689ee33f-f97c-479a-ac49-1b9f8140af99', 'Note contains prohibited words');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -995,6 +1001,23 @@ export class NoteCreateService implements OnApplicationShutdown {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async checkProhibitedWordsContain(content: Parameters<UtilityService['concatNoteContentsForKeyWordCheck']>[0], prohibitedWords?: string[]) {
|
||||||
|
if (prohibitedWords == null) {
|
||||||
|
prohibitedWords = (await this.metaService.fetch()).prohibitedWords;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.utilityService.isKeyWordIncluded(
|
||||||
|
this.utilityService.concatNoteContentsForKeyWordCheck(content),
|
||||||
|
prohibitedWords,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
this.#shutdownController.abort();
|
this.#shutdownController.abort();
|
||||||
|
|
|
@ -42,6 +42,20 @@ export class UtilityService {
|
||||||
return silencedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`));
|
return silencedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public concatNoteContentsForKeyWordCheck(content: {
|
||||||
|
cw?: string | null;
|
||||||
|
text?: string | null;
|
||||||
|
pollChoices?: string[] | null;
|
||||||
|
others?: string[] | null;
|
||||||
|
}): string {
|
||||||
|
/**
|
||||||
|
* ノートの内容を結合してキーワードチェック用の文字列を生成する
|
||||||
|
* cwとtextは内容が繋がっているかもしれないので間に何も入れずにチェックする
|
||||||
|
*/
|
||||||
|
return `${content.cw ?? ''}${content.text ?? ''}\n${(content.pollChoices ?? []).join('\n')}\n${(content.others ?? []).join('\n')}`;
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public isKeyWordIncluded(text: string, keyWords: string[]): boolean {
|
public isKeyWordIncluded(text: string, keyWords: string[]): boolean {
|
||||||
if (keyWords.length === 0) return false;
|
if (keyWords.length === 0) return false;
|
||||||
|
|
|
@ -36,7 +36,6 @@ import { ApResolverService } from './ApResolverService.js';
|
||||||
import { ApAudienceService } from './ApAudienceService.js';
|
import { ApAudienceService } from './ApAudienceService.js';
|
||||||
import { ApPersonService } from './models/ApPersonService.js';
|
import { ApPersonService } from './models/ApPersonService.js';
|
||||||
import { ApQuestionService } from './models/ApQuestionService.js';
|
import { ApQuestionService } from './models/ApQuestionService.js';
|
||||||
import { CacheService } from '@/core/CacheService.js';
|
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import type { Resolver } from './ApResolverService.js';
|
import type { Resolver } from './ApResolverService.js';
|
||||||
import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IReject, IRemove, IUndo, IUpdate, IMove } from './type.js';
|
import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IReject, IRemove, IUndo, IUpdate, IMove } from './type.js';
|
||||||
|
|
|
@ -24,6 +24,8 @@ import { StatusError } from '@/misc/status-error.js';
|
||||||
import { UtilityService } from '@/core/UtilityService.js';
|
import { UtilityService } from '@/core/UtilityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { checkHttps } from '@/misc/check-https.js';
|
import { checkHttps } from '@/misc/check-https.js';
|
||||||
|
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||||
|
import { isNotNull } from '@/misc/is-not-null.js';
|
||||||
import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js';
|
import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js';
|
||||||
import { ApLoggerService } from '../ApLoggerService.js';
|
import { ApLoggerService } from '../ApLoggerService.js';
|
||||||
import { ApMfmService } from '../ApMfmService.js';
|
import { ApMfmService } from '../ApMfmService.js';
|
||||||
|
@ -37,7 +39,6 @@ import { ApQuestionService } from './ApQuestionService.js';
|
||||||
import { ApImageService } from './ApImageService.js';
|
import { ApImageService } from './ApImageService.js';
|
||||||
import type { Resolver } from '../ApResolverService.js';
|
import type { Resolver } from '../ApResolverService.js';
|
||||||
import type { IObject, IPost } from '../type.js';
|
import type { IObject, IPost } from '../type.js';
|
||||||
import { isNotNull } from '@/misc/is-not-null.js';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ApNoteService {
|
export class ApNoteService {
|
||||||
|
@ -152,11 +153,47 @@ export class ApNoteService {
|
||||||
throw new Error('invalid note.attributedTo: ' + note.attributedTo);
|
throw new Error('invalid note.attributedTo: ' + note.attributedTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as MiRemoteUser;
|
const uri = getOneApId(note.attributedTo);
|
||||||
|
|
||||||
// 投稿者が凍結されていたらスキップ
|
// ローカルで投稿者を検索し、もし凍結されていたらスキップ
|
||||||
|
const cachedActor = await this.apPersonService.fetchPerson(uri) as MiRemoteUser;
|
||||||
|
if (cachedActor && cachedActor.isSuspended) {
|
||||||
|
throw new IdentifiableError('85ab9bd7-3a41-4530-959d-f07073900109', 'actor has been suspended');
|
||||||
|
}
|
||||||
|
|
||||||
|
const apMentions = await this.apMentionService.extractApMentions(note.tag, resolver);
|
||||||
|
const apHashtags = extractApHashtags(note.tag);
|
||||||
|
|
||||||
|
const cw = note.summary === '' ? null : note.summary;
|
||||||
|
|
||||||
|
// テキストのパース
|
||||||
|
let text: string | null = null;
|
||||||
|
if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source.content === 'string') {
|
||||||
|
text = note.source.content;
|
||||||
|
} else if (typeof note._misskey_content !== 'undefined') {
|
||||||
|
text = note._misskey_content;
|
||||||
|
} else if (typeof note.content === 'string') {
|
||||||
|
text = this.apMfmService.htmlToMfm(note.content, note.tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined);
|
||||||
|
|
||||||
|
//#region Contents Check
|
||||||
|
// 添付ファイルとユーザーをこのサーバーで登録する前に内容をチェックする
|
||||||
|
/**
|
||||||
|
* 禁止ワードチェック
|
||||||
|
*/
|
||||||
|
const hasProhibitedWords = await this.noteCreateService.checkProhibitedWordsContain({ cw, text, pollChoices: poll?.choices });
|
||||||
|
if (hasProhibitedWords) {
|
||||||
|
throw new IdentifiableError('689ee33f-f97c-479a-ac49-1b9f8140af99', 'Note contains prohibited words');
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
const actor = cachedActor ?? await this.apPersonService.resolvePerson(uri, resolver) as MiRemoteUser;
|
||||||
|
|
||||||
|
// 解決した投稿者が凍結されていたらスキップ
|
||||||
if (actor.isSuspended) {
|
if (actor.isSuspended) {
|
||||||
throw new Error('actor has been suspended');
|
throw new IdentifiableError('85ab9bd7-3a41-4530-959d-f07073900109', 'actor has been suspended');
|
||||||
}
|
}
|
||||||
|
|
||||||
const noteAudience = await this.apAudienceService.parseAudience(actor, note.to, note.cc, resolver);
|
const noteAudience = await this.apAudienceService.parseAudience(actor, note.to, note.cc, resolver);
|
||||||
|
@ -171,9 +208,6 @@ export class ApNoteService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const apMentions = await this.apMentionService.extractApMentions(note.tag, resolver);
|
|
||||||
const apHashtags = extractApHashtags(note.tag);
|
|
||||||
|
|
||||||
// 添付ファイル
|
// 添付ファイル
|
||||||
// TODO: attachmentは必ずしもImageではない
|
// TODO: attachmentは必ずしもImageではない
|
||||||
// TODO: attachmentは必ずしも配列ではない
|
// TODO: attachmentは必ずしも配列ではない
|
||||||
|
@ -233,18 +267,6 @@ export class ApNoteService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const cw = note.summary === '' ? null : note.summary;
|
|
||||||
|
|
||||||
// テキストのパース
|
|
||||||
let text: string | null = null;
|
|
||||||
if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source.content === 'string') {
|
|
||||||
text = note.source.content;
|
|
||||||
} else if (typeof note._misskey_content !== 'undefined') {
|
|
||||||
text = note._misskey_content;
|
|
||||||
} else if (typeof note.content === 'string') {
|
|
||||||
text = this.apMfmService.htmlToMfm(note.content, note.tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
// vote
|
// vote
|
||||||
if (reply && reply.hasPoll) {
|
if (reply && reply.hasPoll) {
|
||||||
const poll = await this.pollsRepository.findOneByOrFail({ noteId: reply.id });
|
const poll = await this.pollsRepository.findOneByOrFail({ noteId: reply.id });
|
||||||
|
@ -274,8 +296,6 @@ export class ApNoteService {
|
||||||
|
|
||||||
const apEmojis = emojis.map(emoji => emoji.name);
|
const apEmojis = emojis.map(emoji => emoji.name);
|
||||||
|
|
||||||
const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await this.noteCreateService.create(actor, {
|
return await this.noteCreateService.create(actor, {
|
||||||
createdAt: note.published ? new Date(note.published) : null,
|
createdAt: note.published ? new Date(note.published) : null,
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
import * as fs from 'node:fs/promises';
|
import * as fs from 'node:fs/promises';
|
||||||
import type { PathLike } from 'node:fs';
|
import type { PathLike } from 'node:fs';
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
import { TransformStream } from 'node:stream/web';
|
import { TransformStream } from 'node:stream/web';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -185,7 +185,10 @@ export class InboxProcessorService {
|
||||||
await this.apInboxService.performActivity(authUser.user, activity);
|
await this.apInboxService.performActivity(authUser.user, activity);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof IdentifiableError) {
|
if (e instanceof IdentifiableError) {
|
||||||
if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') return 'blocked notes with prohibited words';
|
if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') {
|
||||||
|
return 'blocked notes with prohibited words';
|
||||||
|
}
|
||||||
|
if (e.id === '85ab9bd7-3a41-4530-959d-f07073900109') return 'actor has been suspended';
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
* achievementEarned - 実績を獲得
|
* achievementEarned - 実績を獲得
|
||||||
* app - アプリ通知
|
* app - アプリ通知
|
||||||
* test - テスト通知(サーバー側)
|
* test - テスト通知(サーバー側)
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
export const notificationTypes = [
|
export const notificationTypes = [
|
||||||
'note',
|
'note',
|
||||||
|
|
|
@ -187,7 +187,7 @@ describe('2要素認証', () => {
|
||||||
}, 1000 * 60 * 2);
|
}, 1000 * 60 * 2);
|
||||||
|
|
||||||
test('が設定でき、OTPでログインできる。', async () => {
|
test('が設定でき、OTPでログインできる。', async () => {
|
||||||
const registerResponse = await api('/i/2fa/register', {
|
const registerResponse = await api('i/2fa/register', {
|
||||||
password,
|
password,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(registerResponse.status, 200);
|
assert.strictEqual(registerResponse.status, 200);
|
||||||
|
@ -197,18 +197,18 @@ describe('2要素認証', () => {
|
||||||
assert.strictEqual(registerResponse.body.label, username);
|
assert.strictEqual(registerResponse.body.label, username);
|
||||||
assert.strictEqual(registerResponse.body.issuer, config.host);
|
assert.strictEqual(registerResponse.body.issuer, config.host);
|
||||||
|
|
||||||
const doneResponse = await api('/i/2fa/done', {
|
const doneResponse = await api('i/2fa/done', {
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(doneResponse.status, 200);
|
assert.strictEqual(doneResponse.status, 200);
|
||||||
|
|
||||||
const usersShowResponse = await api('/users/show', {
|
const usersShowResponse = await api('users/show', {
|
||||||
username,
|
username,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(usersShowResponse.status, 200);
|
assert.strictEqual(usersShowResponse.status, 200);
|
||||||
assert.strictEqual(usersShowResponse.body.twoFactorEnabled, true);
|
assert.strictEqual(usersShowResponse.body.twoFactorEnabled, true);
|
||||||
|
|
||||||
const signinResponse = await api('/signin', {
|
const signinResponse = await api('signin', {
|
||||||
...signinParam(),
|
...signinParam(),
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
});
|
});
|
||||||
|
@ -216,24 +216,24 @@ describe('2要素認証', () => {
|
||||||
assert.notEqual(signinResponse.body.i, undefined);
|
assert.notEqual(signinResponse.body.i, undefined);
|
||||||
|
|
||||||
// 後片付け
|
// 後片付け
|
||||||
await api('/i/2fa/unregister', {
|
await api('i/2fa/unregister', {
|
||||||
password,
|
password,
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
}, alice);
|
}, alice);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('が設定でき、セキュリティキーでログインできる。', async () => {
|
test('が設定でき、セキュリティキーでログインできる。', async () => {
|
||||||
const registerResponse = await api('/i/2fa/register', {
|
const registerResponse = await api('i/2fa/register', {
|
||||||
password,
|
password,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(registerResponse.status, 200);
|
assert.strictEqual(registerResponse.status, 200);
|
||||||
|
|
||||||
const doneResponse = await api('/i/2fa/done', {
|
const doneResponse = await api('i/2fa/done', {
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(doneResponse.status, 200);
|
assert.strictEqual(doneResponse.status, 200);
|
||||||
|
|
||||||
const registerKeyResponse = await api('/i/2fa/register-key', {
|
const registerKeyResponse = await api('i/2fa/register-key', {
|
||||||
password,
|
password,
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -243,23 +243,23 @@ describe('2要素認証', () => {
|
||||||
|
|
||||||
const keyName = 'example-key';
|
const keyName = 'example-key';
|
||||||
const credentialId = crypto.randomBytes(0x41);
|
const credentialId = crypto.randomBytes(0x41);
|
||||||
const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({
|
const keyDoneResponse = await api('i/2fa/key-done', keyDoneParam({
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
keyName,
|
keyName,
|
||||||
credentialId,
|
credentialId,
|
||||||
creationOptions: registerKeyResponse.body,
|
creationOptions: registerKeyResponse.body,
|
||||||
}), alice);
|
}) as any, alice);
|
||||||
assert.strictEqual(keyDoneResponse.status, 200);
|
assert.strictEqual(keyDoneResponse.status, 200);
|
||||||
assert.strictEqual(keyDoneResponse.body.id, credentialId.toString('base64url'));
|
assert.strictEqual(keyDoneResponse.body.id, credentialId.toString('base64url'));
|
||||||
assert.strictEqual(keyDoneResponse.body.name, keyName);
|
assert.strictEqual(keyDoneResponse.body.name, keyName);
|
||||||
|
|
||||||
const usersShowResponse = await api('/users/show', {
|
const usersShowResponse = await api('users/show', {
|
||||||
username,
|
username,
|
||||||
});
|
});
|
||||||
assert.strictEqual(usersShowResponse.status, 200);
|
assert.strictEqual(usersShowResponse.status, 200);
|
||||||
assert.strictEqual(usersShowResponse.body.securityKeys, true);
|
assert.strictEqual(usersShowResponse.body.securityKeys, true);
|
||||||
|
|
||||||
const signinResponse = await api('/signin', {
|
const signinResponse = await api('signin', {
|
||||||
...signinParam(),
|
...signinParam(),
|
||||||
});
|
});
|
||||||
assert.strictEqual(signinResponse.status, 200);
|
assert.strictEqual(signinResponse.status, 200);
|
||||||
|
@ -268,7 +268,7 @@ describe('2要素認証', () => {
|
||||||
assert.notEqual(signinResponse.body.allowCredentials, undefined);
|
assert.notEqual(signinResponse.body.allowCredentials, undefined);
|
||||||
assert.strictEqual(signinResponse.body.allowCredentials[0].id, credentialId.toString('base64url'));
|
assert.strictEqual(signinResponse.body.allowCredentials[0].id, credentialId.toString('base64url'));
|
||||||
|
|
||||||
const signinResponse2 = await api('/signin', signinWithSecurityKeyParam({
|
const signinResponse2 = await api('signin', signinWithSecurityKeyParam({
|
||||||
keyName,
|
keyName,
|
||||||
credentialId,
|
credentialId,
|
||||||
requestOptions: signinResponse.body,
|
requestOptions: signinResponse.body,
|
||||||
|
@ -277,24 +277,24 @@ describe('2要素認証', () => {
|
||||||
assert.notEqual(signinResponse2.body.i, undefined);
|
assert.notEqual(signinResponse2.body.i, undefined);
|
||||||
|
|
||||||
// 後片付け
|
// 後片付け
|
||||||
await api('/i/2fa/unregister', {
|
await api('i/2fa/unregister', {
|
||||||
password,
|
password,
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
}, alice);
|
}, alice);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('が設定でき、セキュリティキーでパスワードレスログインできる。', async () => {
|
test('が設定でき、セキュリティキーでパスワードレスログインできる。', async () => {
|
||||||
const registerResponse = await api('/i/2fa/register', {
|
const registerResponse = await api('i/2fa/register', {
|
||||||
password,
|
password,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(registerResponse.status, 200);
|
assert.strictEqual(registerResponse.status, 200);
|
||||||
|
|
||||||
const doneResponse = await api('/i/2fa/done', {
|
const doneResponse = await api('i/2fa/done', {
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(doneResponse.status, 200);
|
assert.strictEqual(doneResponse.status, 200);
|
||||||
|
|
||||||
const registerKeyResponse = await api('/i/2fa/register-key', {
|
const registerKeyResponse = await api('i/2fa/register-key', {
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
password,
|
password,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -302,33 +302,33 @@ describe('2要素認証', () => {
|
||||||
|
|
||||||
const keyName = 'example-key';
|
const keyName = 'example-key';
|
||||||
const credentialId = crypto.randomBytes(0x41);
|
const credentialId = crypto.randomBytes(0x41);
|
||||||
const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({
|
const keyDoneResponse = await api('i/2fa/key-done', keyDoneParam({
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
keyName,
|
keyName,
|
||||||
credentialId,
|
credentialId,
|
||||||
creationOptions: registerKeyResponse.body,
|
creationOptions: registerKeyResponse.body,
|
||||||
}), alice);
|
}) as any, alice);
|
||||||
assert.strictEqual(keyDoneResponse.status, 200);
|
assert.strictEqual(keyDoneResponse.status, 200);
|
||||||
|
|
||||||
const passwordLessResponse = await api('/i/2fa/password-less', {
|
const passwordLessResponse = await api('i/2fa/password-less', {
|
||||||
value: true,
|
value: true,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(passwordLessResponse.status, 204);
|
assert.strictEqual(passwordLessResponse.status, 204);
|
||||||
|
|
||||||
const usersShowResponse = await api('/users/show', {
|
const usersShowResponse = await api('users/show', {
|
||||||
username,
|
username,
|
||||||
});
|
});
|
||||||
assert.strictEqual(usersShowResponse.status, 200);
|
assert.strictEqual(usersShowResponse.status, 200);
|
||||||
assert.strictEqual(usersShowResponse.body.usePasswordLessLogin, true);
|
assert.strictEqual(usersShowResponse.body.usePasswordLessLogin, true);
|
||||||
|
|
||||||
const signinResponse = await api('/signin', {
|
const signinResponse = await api('signin', {
|
||||||
...signinParam(),
|
...signinParam(),
|
||||||
password: '',
|
password: '',
|
||||||
});
|
});
|
||||||
assert.strictEqual(signinResponse.status, 200);
|
assert.strictEqual(signinResponse.status, 200);
|
||||||
assert.strictEqual(signinResponse.body.i, undefined);
|
assert.strictEqual(signinResponse.body.i, undefined);
|
||||||
|
|
||||||
const signinResponse2 = await api('/signin', {
|
const signinResponse2 = await api('signin', {
|
||||||
...signinWithSecurityKeyParam({
|
...signinWithSecurityKeyParam({
|
||||||
keyName,
|
keyName,
|
||||||
credentialId,
|
credentialId,
|
||||||
|
@ -340,24 +340,24 @@ describe('2要素認証', () => {
|
||||||
assert.notEqual(signinResponse2.body.i, undefined);
|
assert.notEqual(signinResponse2.body.i, undefined);
|
||||||
|
|
||||||
// 後片付け
|
// 後片付け
|
||||||
await api('/i/2fa/unregister', {
|
await api('i/2fa/unregister', {
|
||||||
password,
|
password,
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
}, alice);
|
}, alice);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('が設定でき、設定したセキュリティキーの名前を変更できる。', async () => {
|
test('が設定でき、設定したセキュリティキーの名前を変更できる。', async () => {
|
||||||
const registerResponse = await api('/i/2fa/register', {
|
const registerResponse = await api('i/2fa/register', {
|
||||||
password,
|
password,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(registerResponse.status, 200);
|
assert.strictEqual(registerResponse.status, 200);
|
||||||
|
|
||||||
const doneResponse = await api('/i/2fa/done', {
|
const doneResponse = await api('i/2fa/done', {
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(doneResponse.status, 200);
|
assert.strictEqual(doneResponse.status, 200);
|
||||||
|
|
||||||
const registerKeyResponse = await api('/i/2fa/register-key', {
|
const registerKeyResponse = await api('i/2fa/register-key', {
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
password,
|
password,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -365,22 +365,22 @@ describe('2要素認証', () => {
|
||||||
|
|
||||||
const keyName = 'example-key';
|
const keyName = 'example-key';
|
||||||
const credentialId = crypto.randomBytes(0x41);
|
const credentialId = crypto.randomBytes(0x41);
|
||||||
const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({
|
const keyDoneResponse = await api('i/2fa/key-done', keyDoneParam({
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
keyName,
|
keyName,
|
||||||
credentialId,
|
credentialId,
|
||||||
creationOptions: registerKeyResponse.body,
|
creationOptions: registerKeyResponse.body,
|
||||||
}), alice);
|
}) as any, alice);
|
||||||
assert.strictEqual(keyDoneResponse.status, 200);
|
assert.strictEqual(keyDoneResponse.status, 200);
|
||||||
|
|
||||||
const renamedKey = 'other-key';
|
const renamedKey = 'other-key';
|
||||||
const updateKeyResponse = await api('/i/2fa/update-key', {
|
const updateKeyResponse = await api('i/2fa/update-key', {
|
||||||
name: renamedKey,
|
name: renamedKey,
|
||||||
credentialId: credentialId.toString('base64url'),
|
credentialId: credentialId.toString('base64url'),
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(updateKeyResponse.status, 200);
|
assert.strictEqual(updateKeyResponse.status, 200);
|
||||||
|
|
||||||
const iResponse = await api('/i', {
|
const iResponse = await api('i', {
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(iResponse.status, 200);
|
assert.strictEqual(iResponse.status, 200);
|
||||||
const securityKeys = iResponse.body.securityKeysList.filter((s: { id: string; }) => s.id === credentialId.toString('base64url'));
|
const securityKeys = iResponse.body.securityKeysList.filter((s: { id: string; }) => s.id === credentialId.toString('base64url'));
|
||||||
|
@ -389,24 +389,24 @@ describe('2要素認証', () => {
|
||||||
assert.notEqual(securityKeys[0].lastUsed, undefined);
|
assert.notEqual(securityKeys[0].lastUsed, undefined);
|
||||||
|
|
||||||
// 後片付け
|
// 後片付け
|
||||||
await api('/i/2fa/unregister', {
|
await api('i/2fa/unregister', {
|
||||||
password,
|
password,
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
}, alice);
|
}, alice);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('が設定でき、設定したセキュリティキーを削除できる。', async () => {
|
test('が設定でき、設定したセキュリティキーを削除できる。', async () => {
|
||||||
const registerResponse = await api('/i/2fa/register', {
|
const registerResponse = await api('i/2fa/register', {
|
||||||
password,
|
password,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(registerResponse.status, 200);
|
assert.strictEqual(registerResponse.status, 200);
|
||||||
|
|
||||||
const doneResponse = await api('/i/2fa/done', {
|
const doneResponse = await api('i/2fa/done', {
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(doneResponse.status, 200);
|
assert.strictEqual(doneResponse.status, 200);
|
||||||
|
|
||||||
const registerKeyResponse = await api('/i/2fa/register-key', {
|
const registerKeyResponse = await api('i/2fa/register-key', {
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
password,
|
password,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -414,20 +414,20 @@ describe('2要素認証', () => {
|
||||||
|
|
||||||
const keyName = 'example-key';
|
const keyName = 'example-key';
|
||||||
const credentialId = crypto.randomBytes(0x41);
|
const credentialId = crypto.randomBytes(0x41);
|
||||||
const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({
|
const keyDoneResponse = await api('i/2fa/key-done', keyDoneParam({
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
keyName,
|
keyName,
|
||||||
credentialId,
|
credentialId,
|
||||||
creationOptions: registerKeyResponse.body,
|
creationOptions: registerKeyResponse.body,
|
||||||
}), alice);
|
}) as any, alice);
|
||||||
assert.strictEqual(keyDoneResponse.status, 200);
|
assert.strictEqual(keyDoneResponse.status, 200);
|
||||||
|
|
||||||
// テストの実行順によっては複数残ってるので全部消す
|
// テストの実行順によっては複数残ってるので全部消す
|
||||||
const iResponse = await api('/i', {
|
const iResponse = await api('i', {
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(iResponse.status, 200);
|
assert.strictEqual(iResponse.status, 200);
|
||||||
for (const key of iResponse.body.securityKeysList) {
|
for (const key of iResponse.body.securityKeysList) {
|
||||||
const removeKeyResponse = await api('/i/2fa/remove-key', {
|
const removeKeyResponse = await api('i/2fa/remove-key', {
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
password,
|
password,
|
||||||
credentialId: key.id,
|
credentialId: key.id,
|
||||||
|
@ -435,13 +435,13 @@ describe('2要素認証', () => {
|
||||||
assert.strictEqual(removeKeyResponse.status, 200);
|
assert.strictEqual(removeKeyResponse.status, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
const usersShowResponse = await api('/users/show', {
|
const usersShowResponse = await api('users/show', {
|
||||||
username,
|
username,
|
||||||
});
|
});
|
||||||
assert.strictEqual(usersShowResponse.status, 200);
|
assert.strictEqual(usersShowResponse.status, 200);
|
||||||
assert.strictEqual(usersShowResponse.body.securityKeys, false);
|
assert.strictEqual(usersShowResponse.body.securityKeys, false);
|
||||||
|
|
||||||
const signinResponse = await api('/signin', {
|
const signinResponse = await api('signin', {
|
||||||
...signinParam(),
|
...signinParam(),
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
});
|
});
|
||||||
|
@ -449,43 +449,43 @@ describe('2要素認証', () => {
|
||||||
assert.notEqual(signinResponse.body.i, undefined);
|
assert.notEqual(signinResponse.body.i, undefined);
|
||||||
|
|
||||||
// 後片付け
|
// 後片付け
|
||||||
await api('/i/2fa/unregister', {
|
await api('i/2fa/unregister', {
|
||||||
password,
|
password,
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
}, alice);
|
}, alice);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('が設定でき、設定解除できる。(パスワードのみでログインできる。)', async () => {
|
test('が設定でき、設定解除できる。(パスワードのみでログインできる。)', async () => {
|
||||||
const registerResponse = await api('/i/2fa/register', {
|
const registerResponse = await api('i/2fa/register', {
|
||||||
password,
|
password,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(registerResponse.status, 200);
|
assert.strictEqual(registerResponse.status, 200);
|
||||||
|
|
||||||
const doneResponse = await api('/i/2fa/done', {
|
const doneResponse = await api('i/2fa/done', {
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(doneResponse.status, 200);
|
assert.strictEqual(doneResponse.status, 200);
|
||||||
|
|
||||||
const usersShowResponse = await api('/users/show', {
|
const usersShowResponse = await api('users/show', {
|
||||||
username,
|
username,
|
||||||
});
|
});
|
||||||
assert.strictEqual(usersShowResponse.status, 200);
|
assert.strictEqual(usersShowResponse.status, 200);
|
||||||
assert.strictEqual(usersShowResponse.body.twoFactorEnabled, true);
|
assert.strictEqual(usersShowResponse.body.twoFactorEnabled, true);
|
||||||
|
|
||||||
const unregisterResponse = await api('/i/2fa/unregister', {
|
const unregisterResponse = await api('i/2fa/unregister', {
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
password,
|
password,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(unregisterResponse.status, 204);
|
assert.strictEqual(unregisterResponse.status, 204);
|
||||||
|
|
||||||
const signinResponse = await api('/signin', {
|
const signinResponse = await api('signin', {
|
||||||
...signinParam(),
|
...signinParam(),
|
||||||
});
|
});
|
||||||
assert.strictEqual(signinResponse.status, 200);
|
assert.strictEqual(signinResponse.status, 200);
|
||||||
assert.notEqual(signinResponse.body.i, undefined);
|
assert.notEqual(signinResponse.body.i, undefined);
|
||||||
|
|
||||||
// 後片付け
|
// 後片付け
|
||||||
await api('/i/2fa/unregister', {
|
await api('i/2fa/unregister', {
|
||||||
password,
|
password,
|
||||||
token: otpToken(registerResponse.body.secret),
|
token: otpToken(registerResponse.body.secret),
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
|
@ -7,7 +7,6 @@ process.env.NODE_ENV = 'test';
|
||||||
|
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
|
||||||
import {
|
import {
|
||||||
api,
|
api,
|
||||||
failedApiCall,
|
failedApiCall,
|
||||||
|
@ -29,10 +28,7 @@ describe('アンテナ', () => {
|
||||||
// エンティティとしてのアンテナを主眼においたテストを記述する
|
// エンティティとしてのアンテナを主眼においたテストを記述する
|
||||||
// (Antennaを返すエンドポイント、Antennaエンティティを書き換えるエンドポイント、Antennaからノートを取得するエンドポイントをテストする)
|
// (Antennaを返すエンドポイント、Antennaエンティティを書き換えるエンドポイント、Antennaからノートを取得するエンドポイントをテストする)
|
||||||
|
|
||||||
// BUG misskey-jsとjson-schemaが一致していない。
|
type Antenna = misskey.entities.Antenna;
|
||||||
// - srcのenumにgroupが残っている
|
|
||||||
// - userGroupIdが残っている, isActiveがない
|
|
||||||
type Antenna = misskey.entities.Antenna | Packed<'Antenna'>;
|
|
||||||
type User = misskey.entities.SignupResponse;
|
type User = misskey.entities.SignupResponse;
|
||||||
type Note = misskey.entities.Note;
|
type Note = misskey.entities.Note;
|
||||||
|
|
||||||
|
@ -80,7 +76,7 @@ describe('アンテナ', () => {
|
||||||
aliceList = await userList(alice, {});
|
aliceList = await userList(alice, {});
|
||||||
bob = await signup({ username: 'bob' });
|
bob = await signup({ username: 'bob' });
|
||||||
aliceList = await userList(alice, {});
|
aliceList = await userList(alice, {});
|
||||||
bobFile = (await uploadFile(bob)).body;
|
bobFile = (await uploadFile(bob)).body!;
|
||||||
bobList = await userList(bob);
|
bobList = await userList(bob);
|
||||||
carol = await signup({ username: 'carol' });
|
carol = await signup({ username: 'carol' });
|
||||||
await api('users/lists/push', { listId: aliceList.id, userId: bob.id }, alice);
|
await api('users/lists/push', { listId: aliceList.id, userId: bob.id }, alice);
|
||||||
|
@ -129,9 +125,9 @@ describe('アンテナ', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
// テスト間で影響し合わないように毎回全部消す。
|
// テスト間で影響し合わないように毎回全部消す。
|
||||||
for (const user of [alice, bob]) {
|
for (const user of [alice, bob]) {
|
||||||
const list = await api('/antennas/list', {}, user);
|
const list = await api('antennas/list', {}, user);
|
||||||
for (const antenna of list.body) {
|
for (const antenna of list.body) {
|
||||||
await api('/antennas/delete', { antennaId: antenna.id }, user);
|
await api('antennas/delete', { antennaId: antenna.id }, user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -141,11 +137,11 @@ describe('アンテナ', () => {
|
||||||
test('が作成できること、キーが過不足なく入っていること。', async () => {
|
test('が作成できること、キーが過不足なく入っていること。', async () => {
|
||||||
const response = await successfulApiCall({
|
const response = await successfulApiCall({
|
||||||
endpoint: 'antennas/create',
|
endpoint: 'antennas/create',
|
||||||
parameters: { ...defaultParam },
|
parameters: defaultParam,
|
||||||
user: alice,
|
user: alice,
|
||||||
});
|
});
|
||||||
assert.match(response.id, /[0-9a-z]{10}/);
|
assert.match(response.id, /[0-9a-z]{10}/);
|
||||||
const expected = {
|
const expected: Antenna = {
|
||||||
id: response.id,
|
id: response.id,
|
||||||
caseSensitive: false,
|
caseSensitive: false,
|
||||||
createdAt: new Date(response.createdAt).toISOString(),
|
createdAt: new Date(response.createdAt).toISOString(),
|
||||||
|
@ -161,7 +157,7 @@ describe('アンテナ', () => {
|
||||||
withFile: false,
|
withFile: false,
|
||||||
withReplies: false,
|
withReplies: false,
|
||||||
localOnly: false,
|
localOnly: false,
|
||||||
} as Antenna;
|
};
|
||||||
assert.deepStrictEqual(response, expected);
|
assert.deepStrictEqual(response, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -202,27 +198,27 @@ describe('アンテナ', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const antennaParamPattern = [
|
const antennaParamPattern = [
|
||||||
{ parameters: (): object => ({ name: 'x'.repeat(100) }) },
|
{ parameters: () => ({ name: 'x'.repeat(100) }) },
|
||||||
{ parameters: (): object => ({ name: 'x' }) },
|
{ parameters: () => ({ name: 'x' }) },
|
||||||
{ parameters: (): object => ({ src: 'home' }) },
|
{ parameters: () => ({ src: 'home' as const }) },
|
||||||
{ parameters: (): object => ({ src: 'all' }) },
|
{ parameters: () => ({ src: 'all' as const }) },
|
||||||
{ parameters: (): object => ({ src: 'users' }) },
|
{ parameters: () => ({ src: 'users' as const }) },
|
||||||
{ parameters: (): object => ({ src: 'list' }) },
|
{ parameters: () => ({ src: 'list' as const }) },
|
||||||
{ parameters: (): object => ({ userListId: null }) },
|
{ parameters: () => ({ userListId: null }) },
|
||||||
{ parameters: (): object => ({ src: 'list', userListId: aliceList.id }) },
|
{ parameters: () => ({ src: 'list' as const, userListId: aliceList.id }) },
|
||||||
{ parameters: (): object => ({ keywords: [['x']] }) },
|
{ parameters: () => ({ keywords: [['x']] }) },
|
||||||
{ parameters: (): object => ({ keywords: [['a', 'b', 'c'], ['x'], ['y'], ['z']] }) },
|
{ parameters: () => ({ keywords: [['a', 'b', 'c'], ['x'], ['y'], ['z']] }) },
|
||||||
{ parameters: (): object => ({ excludeKeywords: [['a', 'b', 'c'], ['x'], ['y'], ['z']] }) },
|
{ parameters: () => ({ excludeKeywords: [['a', 'b', 'c'], ['x'], ['y'], ['z']] }) },
|
||||||
{ parameters: (): object => ({ users: [alice.username] }) },
|
{ parameters: () => ({ users: [alice.username] }) },
|
||||||
{ parameters: (): object => ({ users: [alice.username, bob.username, carol.username] }) },
|
{ parameters: () => ({ users: [alice.username, bob.username, carol.username] }) },
|
||||||
{ parameters: (): object => ({ caseSensitive: false }) },
|
{ parameters: () => ({ caseSensitive: false }) },
|
||||||
{ parameters: (): object => ({ caseSensitive: true }) },
|
{ parameters: () => ({ caseSensitive: true }) },
|
||||||
{ parameters: (): object => ({ withReplies: false }) },
|
{ parameters: () => ({ withReplies: false }) },
|
||||||
{ parameters: (): object => ({ withReplies: true }) },
|
{ parameters: () => ({ withReplies: true }) },
|
||||||
{ parameters: (): object => ({ withFile: false }) },
|
{ parameters: () => ({ withFile: false }) },
|
||||||
{ parameters: (): object => ({ withFile: true }) },
|
{ parameters: () => ({ withFile: true }) },
|
||||||
{ parameters: (): object => ({ notify: false }) },
|
{ parameters: () => ({ notify: false }) },
|
||||||
{ parameters: (): object => ({ notify: true }) },
|
{ parameters: () => ({ notify: true }) },
|
||||||
];
|
];
|
||||||
test.each(antennaParamPattern)('を作成できること($#)', async ({ parameters }) => {
|
test.each(antennaParamPattern)('を作成できること($#)', async ({ parameters }) => {
|
||||||
const response = await successfulApiCall({
|
const response = await successfulApiCall({
|
||||||
|
@ -335,7 +331,7 @@ describe('アンテナ', () => {
|
||||||
test.each([
|
test.each([
|
||||||
{
|
{
|
||||||
label: '全体から',
|
label: '全体から',
|
||||||
parameters: (): object => ({ src: 'all' }),
|
parameters: () => ({ src: 'all' }),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(alice, { text: `${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(alice, { text: `${keyword}` }), included: true },
|
||||||
{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}` }), included: true },
|
||||||
|
@ -346,7 +342,7 @@ describe('アンテナ', () => {
|
||||||
{
|
{
|
||||||
// BUG e4144a1 以降home指定は壊れている(allと同じ)
|
// BUG e4144a1 以降home指定は壊れている(allと同じ)
|
||||||
label: 'ホーム指定はallと同じ',
|
label: 'ホーム指定はallと同じ',
|
||||||
parameters: (): object => ({ src: 'home' }),
|
parameters: () => ({ src: 'home' }),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(alice, { text: `${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(alice, { text: `${keyword}` }), included: true },
|
||||||
{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}` }), included: true },
|
||||||
|
@ -357,7 +353,7 @@ describe('アンテナ', () => {
|
||||||
{
|
{
|
||||||
// https://github.com/misskey-dev/misskey/issues/9025
|
// https://github.com/misskey-dev/misskey/issues/9025
|
||||||
label: 'ただし、フォロワー限定投稿とDM投稿を含まない。フォロワーであっても。',
|
label: 'ただし、フォロワー限定投稿とDM投稿を含まない。フォロワーであっても。',
|
||||||
parameters: (): object => ({}),
|
parameters: () => ({}),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'public' }), included: true },
|
{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'public' }), included: true },
|
||||||
{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'home' }), included: true },
|
{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'home' }), included: true },
|
||||||
|
@ -367,56 +363,56 @@ describe('アンテナ', () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'ブロックしているユーザーのノートは含む',
|
label: 'ブロックしているユーザーのノートは含む',
|
||||||
parameters: (): object => ({}),
|
parameters: () => ({}),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(userBlockedByAlice, { text: `${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(userBlockedByAlice, { text: `${keyword}` }), included: true },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'ブロックされているユーザーのノートは含まない',
|
label: 'ブロックされているユーザーのノートは含まない',
|
||||||
parameters: (): object => ({}),
|
parameters: () => ({}),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(userBlockingAlice, { text: `${keyword}` }) },
|
{ note: (): Promise<Note> => post(userBlockingAlice, { text: `${keyword}` }) },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'ミュートしているユーザーのノートは含まない',
|
label: 'ミュートしているユーザーのノートは含まない',
|
||||||
parameters: (): object => ({}),
|
parameters: () => ({}),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(userMutedByAlice, { text: `${keyword}` }) },
|
{ note: (): Promise<Note> => post(userMutedByAlice, { text: `${keyword}` }) },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'ミュートされているユーザーのノートは含む',
|
label: 'ミュートされているユーザーのノートは含む',
|
||||||
parameters: (): object => ({}),
|
parameters: () => ({}),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(userMutingAlice, { text: `${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(userMutingAlice, { text: `${keyword}` }), included: true },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '「見つけやすくする」がOFFのユーザーのノートも含まれる',
|
label: '「見つけやすくする」がOFFのユーザーのノートも含まれる',
|
||||||
parameters: (): object => ({}),
|
parameters: () => ({}),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(userNotExplorable, { text: `${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(userNotExplorable, { text: `${keyword}` }), included: true },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '鍵付きユーザーのノートも含まれる',
|
label: '鍵付きユーザーのノートも含まれる',
|
||||||
parameters: (): object => ({}),
|
parameters: () => ({}),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(userLocking, { text: `${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(userLocking, { text: `${keyword}` }), included: true },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'サイレンスのノートも含まれる',
|
label: 'サイレンスのノートも含まれる',
|
||||||
parameters: (): object => ({}),
|
parameters: () => ({}),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(userSilenced, { text: `${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(userSilenced, { text: `${keyword}` }), included: true },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '削除ユーザーのノートも含まれる',
|
label: '削除ユーザーのノートも含まれる',
|
||||||
parameters: (): object => ({}),
|
parameters: () => ({}),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(userDeletedBySelf, { text: `${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(userDeletedBySelf, { text: `${keyword}` }), included: true },
|
||||||
{ note: (): Promise<Note> => post(userDeletedByAdmin, { text: `${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(userDeletedByAdmin, { text: `${keyword}` }), included: true },
|
||||||
|
@ -424,7 +420,7 @@ describe('アンテナ', () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'ユーザー指定で',
|
label: 'ユーザー指定で',
|
||||||
parameters: (): object => ({ src: 'users', users: [`@${bob.username}`, `@${carol.username}`] }),
|
parameters: () => ({ src: 'users', users: [`@${bob.username}`, `@${carol.username}`] }),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(alice, { text: `test ${keyword}` }) },
|
{ note: (): Promise<Note> => post(alice, { text: `test ${keyword}` }) },
|
||||||
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
|
||||||
|
@ -433,7 +429,7 @@ describe('アンテナ', () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'リスト指定で',
|
label: 'リスト指定で',
|
||||||
parameters: (): object => ({ src: 'list', userListId: aliceList.id }),
|
parameters: () => ({ src: 'list', userListId: aliceList.id }),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(alice, { text: `test ${keyword}` }) },
|
{ note: (): Promise<Note> => post(alice, { text: `test ${keyword}` }) },
|
||||||
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
|
||||||
|
@ -442,14 +438,14 @@ describe('アンテナ', () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'CWにもマッチする',
|
label: 'CWにもマッチする',
|
||||||
parameters: (): object => ({ keywords: [[keyword]] }),
|
parameters: () => ({ keywords: [[keyword]] }),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(bob, { text: 'test', cw: `cw ${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(bob, { text: 'test', cw: `cw ${keyword}` }), included: true },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'キーワード1つ',
|
label: 'キーワード1つ',
|
||||||
parameters: (): object => ({ keywords: [[keyword]] }),
|
parameters: () => ({ keywords: [[keyword]] }),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(alice, { text: 'test' }) },
|
{ note: (): Promise<Note> => post(alice, { text: 'test' }) },
|
||||||
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
|
||||||
|
@ -458,7 +454,7 @@ describe('アンテナ', () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'キーワード3つ(AND)',
|
label: 'キーワード3つ(AND)',
|
||||||
parameters: (): object => ({ keywords: [['A', 'B', 'C']] }),
|
parameters: () => ({ keywords: [['A', 'B', 'C']] }),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(bob, { text: 'test A' }) },
|
{ note: (): Promise<Note> => post(bob, { text: 'test A' }) },
|
||||||
{ note: (): Promise<Note> => post(bob, { text: 'test A B' }) },
|
{ note: (): Promise<Note> => post(bob, { text: 'test A B' }) },
|
||||||
|
@ -469,7 +465,7 @@ describe('アンテナ', () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'キーワード3つ(OR)',
|
label: 'キーワード3つ(OR)',
|
||||||
parameters: (): object => ({ keywords: [['A'], ['B'], ['C']] }),
|
parameters: () => ({ keywords: [['A'], ['B'], ['C']] }),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(bob, { text: 'test' }) },
|
{ note: (): Promise<Note> => post(bob, { text: 'test' }) },
|
||||||
{ note: (): Promise<Note> => post(bob, { text: 'test A' }), included: true },
|
{ note: (): Promise<Note> => post(bob, { text: 'test A' }), included: true },
|
||||||
|
@ -482,7 +478,7 @@ describe('アンテナ', () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '除外ワード3つ(AND)',
|
label: '除外ワード3つ(AND)',
|
||||||
parameters: (): object => ({ excludeKeywords: [['A', 'B', 'C']] }),
|
parameters: () => ({ excludeKeywords: [['A', 'B', 'C']] }),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
|
||||||
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword} A` }), included: true },
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword} A` }), included: true },
|
||||||
|
@ -495,7 +491,7 @@ describe('アンテナ', () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '除外ワード3つ(OR)',
|
label: '除外ワード3つ(OR)',
|
||||||
parameters: (): object => ({ excludeKeywords: [['A'], ['B'], ['C']] }),
|
parameters: () => ({ excludeKeywords: [['A'], ['B'], ['C']] }),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
|
||||||
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword} A` }) },
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword} A` }) },
|
||||||
|
@ -508,7 +504,7 @@ describe('アンテナ', () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'キーワード1つ(大文字小文字区別する)',
|
label: 'キーワード1つ(大文字小文字区別する)',
|
||||||
parameters: (): object => ({ keywords: [['KEYWORD']], caseSensitive: true }),
|
parameters: () => ({ keywords: [['KEYWORD']], caseSensitive: true }),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(bob, { text: 'keyword' }) },
|
{ note: (): Promise<Note> => post(bob, { text: 'keyword' }) },
|
||||||
{ note: (): Promise<Note> => post(bob, { text: 'kEyWoRd' }) },
|
{ note: (): Promise<Note> => post(bob, { text: 'kEyWoRd' }) },
|
||||||
|
@ -517,7 +513,7 @@ describe('アンテナ', () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'キーワード1つ(大文字小文字区別しない)',
|
label: 'キーワード1つ(大文字小文字区別しない)',
|
||||||
parameters: (): object => ({ keywords: [['KEYWORD']], caseSensitive: false }),
|
parameters: () => ({ keywords: [['KEYWORD']], caseSensitive: false }),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(bob, { text: 'keyword' }), included: true },
|
{ note: (): Promise<Note> => post(bob, { text: 'keyword' }), included: true },
|
||||||
{ note: (): Promise<Note> => post(bob, { text: 'kEyWoRd' }), included: true },
|
{ note: (): Promise<Note> => post(bob, { text: 'kEyWoRd' }), included: true },
|
||||||
|
@ -526,7 +522,7 @@ describe('アンテナ', () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '除外ワード1つ(大文字小文字区別する)',
|
label: '除外ワード1つ(大文字小文字区別する)',
|
||||||
parameters: (): object => ({ excludeKeywords: [['KEYWORD']], caseSensitive: true }),
|
parameters: () => ({ excludeKeywords: [['KEYWORD']], caseSensitive: true }),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true },
|
||||||
{ note: (): Promise<Note> => post(bob, { text: `${keyword} keyword` }), included: true },
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword} keyword` }), included: true },
|
||||||
|
@ -536,7 +532,7 @@ describe('アンテナ', () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '除外ワード1つ(大文字小文字区別しない)',
|
label: '除外ワード1つ(大文字小文字区別しない)',
|
||||||
parameters: (): object => ({ excludeKeywords: [['KEYWORD']], caseSensitive: false }),
|
parameters: () => ({ excludeKeywords: [['KEYWORD']], caseSensitive: false }),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true },
|
||||||
{ note: (): Promise<Note> => post(bob, { text: `${keyword} keyword` }) },
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword} keyword` }) },
|
||||||
|
@ -546,7 +542,7 @@ describe('アンテナ', () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '添付ファイルを問わない',
|
label: '添付ファイルを問わない',
|
||||||
parameters: (): object => ({ withFile: false }),
|
parameters: () => ({ withFile: false }),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(bob, { text: `${keyword}`, fileIds: [bobFile.id] }), included: true },
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword}`, fileIds: [bobFile.id] }), included: true },
|
||||||
{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true },
|
||||||
|
@ -554,7 +550,7 @@ describe('アンテナ', () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '添付ファイル付きのみ',
|
label: '添付ファイル付きのみ',
|
||||||
parameters: (): object => ({ withFile: true }),
|
parameters: () => ({ withFile: true }),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(bob, { text: `${keyword}`, fileIds: [bobFile.id] }), included: true },
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword}`, fileIds: [bobFile.id] }), included: true },
|
||||||
{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }) },
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }) },
|
||||||
|
@ -562,7 +558,7 @@ describe('アンテナ', () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'リプライ以外',
|
label: 'リプライ以外',
|
||||||
parameters: (): object => ({ withReplies: false }),
|
parameters: () => ({ withReplies: false }),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(bob, { text: `${keyword}`, replyId: alicePost.id }) },
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword}`, replyId: alicePost.id }) },
|
||||||
{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true },
|
||||||
|
@ -570,7 +566,7 @@ describe('アンテナ', () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'リプライも含む',
|
label: 'リプライも含む',
|
||||||
parameters: (): object => ({ withReplies: true }),
|
parameters: () => ({ withReplies: true }),
|
||||||
posts: [
|
posts: [
|
||||||
{ note: (): Promise<Note> => post(bob, { text: `${keyword}`, replyId: alicePost.id }), included: true },
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword}`, replyId: alicePost.id }), included: true },
|
||||||
{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true },
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true },
|
||||||
|
@ -633,7 +629,7 @@ describe('アンテナ', () => {
|
||||||
endpoint: 'antennas/notes',
|
endpoint: 'antennas/notes',
|
||||||
parameters: { antennaId: antenna.id, ...paginationParam },
|
parameters: { antennaId: antenna.id, ...paginationParam },
|
||||||
user: alice,
|
user: alice,
|
||||||
}) as any as Note[];
|
});
|
||||||
}, offsetBy, 'desc');
|
}, offsetBy, 'desc');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
process.env.NODE_ENV = 'test';
|
process.env.NODE_ENV = 'test';
|
||||||
|
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import { api, post, signup } from '../utils.js';
|
import { UserToken, api, post, signup } from '../utils.js';
|
||||||
import type * as misskey from 'misskey-js';
|
import type * as misskey from 'misskey-js';
|
||||||
|
|
||||||
describe('API visibility', () => {
|
describe('API visibility', () => {
|
||||||
|
@ -24,38 +24,38 @@ describe('API visibility', () => {
|
||||||
let target2: misskey.entities.SignupResponse;
|
let target2: misskey.entities.SignupResponse;
|
||||||
|
|
||||||
/** public-post */
|
/** public-post */
|
||||||
let pub: any;
|
let pub: misskey.entities.Note;
|
||||||
/** home-post */
|
/** home-post */
|
||||||
let home: any;
|
let home: misskey.entities.Note;
|
||||||
/** followers-post */
|
/** followers-post */
|
||||||
let fol: any;
|
let fol: misskey.entities.Note;
|
||||||
/** specified-post */
|
/** specified-post */
|
||||||
let spe: any;
|
let spe: misskey.entities.Note;
|
||||||
|
|
||||||
/** public-reply to target's post */
|
/** public-reply to target's post */
|
||||||
let pubR: any;
|
let pubR: misskey.entities.Note;
|
||||||
/** home-reply to target's post */
|
/** home-reply to target's post */
|
||||||
let homeR: any;
|
let homeR: misskey.entities.Note;
|
||||||
/** followers-reply to target's post */
|
/** followers-reply to target's post */
|
||||||
let folR: any;
|
let folR: misskey.entities.Note;
|
||||||
/** specified-reply to target's post */
|
/** specified-reply to target's post */
|
||||||
let speR: any;
|
let speR: misskey.entities.Note;
|
||||||
|
|
||||||
/** public-mention to target */
|
/** public-mention to target */
|
||||||
let pubM: any;
|
let pubM: misskey.entities.Note;
|
||||||
/** home-mention to target */
|
/** home-mention to target */
|
||||||
let homeM: any;
|
let homeM: misskey.entities.Note;
|
||||||
/** followers-mention to target */
|
/** followers-mention to target */
|
||||||
let folM: any;
|
let folM: misskey.entities.Note;
|
||||||
/** specified-mention to target */
|
/** specified-mention to target */
|
||||||
let speM: any;
|
let speM: misskey.entities.Note;
|
||||||
|
|
||||||
/** reply target post */
|
/** reply target post */
|
||||||
let tgt: any;
|
let tgt: misskey.entities.Note;
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
const show = async (noteId: any, by: any) => {
|
const show = async (noteId: misskey.entities.Note['id'], by?: UserToken) => {
|
||||||
return await api('/notes/show', {
|
return await api('notes/show', {
|
||||||
noteId,
|
noteId,
|
||||||
}, by);
|
}, by);
|
||||||
};
|
};
|
||||||
|
@ -70,7 +70,7 @@ describe('API visibility', () => {
|
||||||
target2 = await signup({ username: 'target2' });
|
target2 = await signup({ username: 'target2' });
|
||||||
|
|
||||||
// follow alice <= follower
|
// follow alice <= follower
|
||||||
await api('/following/create', { userId: alice.id }, follower);
|
await api('following/create', { userId: alice.id }, follower);
|
||||||
|
|
||||||
// normal posts
|
// normal posts
|
||||||
pub = await post(alice, { text: 'x', visibility: 'public' });
|
pub = await post(alice, { text: 'x', visibility: 'public' });
|
||||||
|
@ -111,7 +111,7 @@ describe('API visibility', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[show] public-postを未認証が見れる', async () => {
|
test('[show] public-postを未認証が見れる', async () => {
|
||||||
const res = await show(pub.id, null);
|
const res = await show(pub.id);
|
||||||
assert.strictEqual(res.body.text, 'x');
|
assert.strictEqual(res.body.text, 'x');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ describe('API visibility', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[show] home-postを未認証が見れる', async () => {
|
test('[show] home-postを未認証が見れる', async () => {
|
||||||
const res = await show(home.id, null);
|
const res = await show(home.id);
|
||||||
assert.strictEqual(res.body.text, 'x');
|
assert.strictEqual(res.body.text, 'x');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -153,7 +153,7 @@ describe('API visibility', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[show] followers-postを未認証が見れない', async () => {
|
test('[show] followers-postを未認証が見れない', async () => {
|
||||||
const res = await show(fol.id, null);
|
const res = await show(fol.id);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.body.isHidden, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -179,7 +179,7 @@ describe('API visibility', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[show] specified-postを未認証が見れない', async () => {
|
test('[show] specified-postを未認証が見れない', async () => {
|
||||||
const res = await show(spe.id, null);
|
const res = await show(spe.id);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.body.isHidden, true);
|
||||||
});
|
});
|
||||||
//#endregion
|
//#endregion
|
||||||
|
@ -207,7 +207,7 @@ describe('API visibility', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[show] public-replyを未認証が見れる', async () => {
|
test('[show] public-replyを未認証が見れる', async () => {
|
||||||
const res = await show(pubR.id, null);
|
const res = await show(pubR.id);
|
||||||
assert.strictEqual(res.body.text, 'x');
|
assert.strictEqual(res.body.text, 'x');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -233,7 +233,7 @@ describe('API visibility', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[show] home-replyを未認証が見れる', async () => {
|
test('[show] home-replyを未認証が見れる', async () => {
|
||||||
const res = await show(homeR.id, null);
|
const res = await show(homeR.id);
|
||||||
assert.strictEqual(res.body.text, 'x');
|
assert.strictEqual(res.body.text, 'x');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -259,7 +259,7 @@ describe('API visibility', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[show] followers-replyを未認証が見れない', async () => {
|
test('[show] followers-replyを未認証が見れない', async () => {
|
||||||
const res = await show(folR.id, null);
|
const res = await show(folR.id);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.body.isHidden, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -290,7 +290,7 @@ describe('API visibility', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[show] specified-replyを未認証が見れない', async () => {
|
test('[show] specified-replyを未認証が見れない', async () => {
|
||||||
const res = await show(speR.id, null);
|
const res = await show(speR.id);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.body.isHidden, true);
|
||||||
});
|
});
|
||||||
//#endregion
|
//#endregion
|
||||||
|
@ -318,7 +318,7 @@ describe('API visibility', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[show] public-mentionを未認証が見れる', async () => {
|
test('[show] public-mentionを未認証が見れる', async () => {
|
||||||
const res = await show(pubM.id, null);
|
const res = await show(pubM.id);
|
||||||
assert.strictEqual(res.body.text, '@target x');
|
assert.strictEqual(res.body.text, '@target x');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -344,7 +344,7 @@ describe('API visibility', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[show] home-mentionを未認証が見れる', async () => {
|
test('[show] home-mentionを未認証が見れる', async () => {
|
||||||
const res = await show(homeM.id, null);
|
const res = await show(homeM.id);
|
||||||
assert.strictEqual(res.body.text, '@target x');
|
assert.strictEqual(res.body.text, '@target x');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -370,7 +370,7 @@ describe('API visibility', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[show] followers-mentionを未認証が見れない', async () => {
|
test('[show] followers-mentionを未認証が見れない', async () => {
|
||||||
const res = await show(folM.id, null);
|
const res = await show(folM.id);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.body.isHidden, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -401,28 +401,28 @@ describe('API visibility', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[show] specified-mentionを未認証が見れない', async () => {
|
test('[show] specified-mentionを未認証が見れない', async () => {
|
||||||
const res = await show(speM.id, null);
|
const res = await show(speM.id);
|
||||||
assert.strictEqual(res.body.isHidden, true);
|
assert.strictEqual(res.body.isHidden, true);
|
||||||
});
|
});
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region HTL
|
//#region HTL
|
||||||
test('[HTL] public-post が 自分が見れる', async () => {
|
test('[HTL] public-post が 自分が見れる', async () => {
|
||||||
const res = await api('/notes/timeline', { limit: 100 }, alice);
|
const res = await api('notes/timeline', { limit: 100 }, alice);
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
const notes = res.body.filter((n: any) => n.id === pub.id);
|
const notes = res.body.filter((n: any) => n.id === pub.id);
|
||||||
assert.strictEqual(notes[0].text, 'x');
|
assert.strictEqual(notes[0].text, 'x');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[HTL] public-post が 非フォロワーから見れない', async () => {
|
test('[HTL] public-post が 非フォロワーから見れない', async () => {
|
||||||
const res = await api('/notes/timeline', { limit: 100 }, other);
|
const res = await api('notes/timeline', { limit: 100 }, other);
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
const notes = res.body.filter((n: any) => n.id === pub.id);
|
const notes = res.body.filter((n: any) => n.id === pub.id);
|
||||||
assert.strictEqual(notes.length, 0);
|
assert.strictEqual(notes.length, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[HTL] followers-post が フォロワーから見れる', async () => {
|
test('[HTL] followers-post が フォロワーから見れる', async () => {
|
||||||
const res = await api('/notes/timeline', { limit: 100 }, follower);
|
const res = await api('notes/timeline', { limit: 100 }, follower);
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
const notes = res.body.filter((n: any) => n.id === fol.id);
|
const notes = res.body.filter((n: any) => n.id === fol.id);
|
||||||
assert.strictEqual(notes[0].text, 'x');
|
assert.strictEqual(notes[0].text, 'x');
|
||||||
|
@ -431,21 +431,21 @@ describe('API visibility', () => {
|
||||||
|
|
||||||
//#region RTL
|
//#region RTL
|
||||||
test('[replies] followers-reply が フォロワーから見れる', async () => {
|
test('[replies] followers-reply が フォロワーから見れる', async () => {
|
||||||
const res = await api('/notes/replies', { noteId: tgt.id, limit: 100 }, follower);
|
const res = await api('notes/replies', { noteId: tgt.id, limit: 100 }, follower);
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
const notes = res.body.filter((n: any) => n.id === folR.id);
|
const notes = res.body.filter((n: any) => n.id === folR.id);
|
||||||
assert.strictEqual(notes[0].text, 'x');
|
assert.strictEqual(notes[0].text, 'x');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[replies] followers-reply が 非フォロワー (リプライ先ではない) から見れない', async () => {
|
test('[replies] followers-reply が 非フォロワー (リプライ先ではない) から見れない', async () => {
|
||||||
const res = await api('/notes/replies', { noteId: tgt.id, limit: 100 }, other);
|
const res = await api('notes/replies', { noteId: tgt.id, limit: 100 }, other);
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
const notes = res.body.filter((n: any) => n.id === folR.id);
|
const notes = res.body.filter((n: any) => n.id === folR.id);
|
||||||
assert.strictEqual(notes.length, 0);
|
assert.strictEqual(notes.length, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[replies] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => {
|
test('[replies] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => {
|
||||||
const res = await api('/notes/replies', { noteId: tgt.id, limit: 100 }, target);
|
const res = await api('notes/replies', { noteId: tgt.id, limit: 100 }, target);
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
const notes = res.body.filter((n: any) => n.id === folR.id);
|
const notes = res.body.filter((n: any) => n.id === folR.id);
|
||||||
assert.strictEqual(notes[0].text, 'x');
|
assert.strictEqual(notes[0].text, 'x');
|
||||||
|
@ -454,14 +454,14 @@ describe('API visibility', () => {
|
||||||
|
|
||||||
//#region MTL
|
//#region MTL
|
||||||
test('[mentions] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => {
|
test('[mentions] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => {
|
||||||
const res = await api('/notes/mentions', { limit: 100 }, target);
|
const res = await api('notes/mentions', { limit: 100 }, target);
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
const notes = res.body.filter((n: any) => n.id === folR.id);
|
const notes = res.body.filter((n: any) => n.id === folR.id);
|
||||||
assert.strictEqual(notes[0].text, 'x');
|
assert.strictEqual(notes[0].text, 'x');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[mentions] followers-mention が 非フォロワー (メンション先である) から見れる', async () => {
|
test('[mentions] followers-mention が 非フォロワー (メンション先である) から見れる', async () => {
|
||||||
const res = await api('/notes/mentions', { limit: 100 }, target);
|
const res = await api('notes/mentions', { limit: 100 }, target);
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
const notes = res.body.filter((n: any) => n.id === folM.id);
|
const notes = res.body.filter((n: any) => n.id === folM.id);
|
||||||
assert.strictEqual(notes[0].text, '@target x');
|
assert.strictEqual(notes[0].text, '@target x');
|
||||||
|
|
|
@ -23,32 +23,32 @@ import type * as misskey from 'misskey-js';
|
||||||
describe('API', () => {
|
describe('API', () => {
|
||||||
let alice: misskey.entities.SignupResponse;
|
let alice: misskey.entities.SignupResponse;
|
||||||
let bob: misskey.entities.SignupResponse;
|
let bob: misskey.entities.SignupResponse;
|
||||||
let carol: misskey.entities.SignupResponse;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
alice = await signup({ username: 'alice' });
|
alice = await signup({ username: 'alice' });
|
||||||
bob = await signup({ username: 'bob' });
|
bob = await signup({ username: 'bob' });
|
||||||
carol = await signup({ username: 'carol' });
|
|
||||||
}, 1000 * 60 * 2);
|
}, 1000 * 60 * 2);
|
||||||
|
|
||||||
describe('General validation', () => {
|
describe('General validation', () => {
|
||||||
test('wrong type', async () => {
|
test('wrong type', async () => {
|
||||||
const res = await api('/test', {
|
const res = await api('test', {
|
||||||
required: true,
|
required: true,
|
||||||
|
// @ts-expect-error string must be string
|
||||||
string: 42,
|
string: 42,
|
||||||
});
|
});
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('missing require param', async () => {
|
test('missing require param', async () => {
|
||||||
const res = await api('/test', {
|
// @ts-expect-error required is required
|
||||||
|
const res = await api('test', {
|
||||||
string: 'a',
|
string: 'a',
|
||||||
});
|
});
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('invalid misskey:id (empty string)', async () => {
|
test('invalid misskey:id (empty string)', async () => {
|
||||||
const res = await api('/test', {
|
const res = await api('test', {
|
||||||
required: true,
|
required: true,
|
||||||
id: '',
|
id: '',
|
||||||
});
|
});
|
||||||
|
@ -56,7 +56,7 @@ describe('API', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('valid misskey:id', async () => {
|
test('valid misskey:id', async () => {
|
||||||
const res = await api('/test', {
|
const res = await api('test', {
|
||||||
required: true,
|
required: true,
|
||||||
id: '8wvhjghbxu',
|
id: '8wvhjghbxu',
|
||||||
});
|
});
|
||||||
|
@ -64,7 +64,7 @@ describe('API', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('default value', async () => {
|
test('default value', async () => {
|
||||||
const res = await api('/test', {
|
const res = await api('test', {
|
||||||
required: true,
|
required: true,
|
||||||
string: 'a',
|
string: 'a',
|
||||||
});
|
});
|
||||||
|
@ -73,7 +73,7 @@ describe('API', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can set null even if it has default value', async () => {
|
test('can set null even if it has default value', async () => {
|
||||||
const res = await api('/test', {
|
const res = await api('test', {
|
||||||
required: true,
|
required: true,
|
||||||
nullableDefault: null,
|
nullableDefault: null,
|
||||||
});
|
});
|
||||||
|
@ -82,7 +82,7 @@ describe('API', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cannot set undefined if it has default value', async () => {
|
test('cannot set undefined if it has default value', async () => {
|
||||||
const res = await api('/test', {
|
const res = await api('test', {
|
||||||
required: true,
|
required: true,
|
||||||
nullableDefault: undefined,
|
nullableDefault: undefined,
|
||||||
});
|
});
|
||||||
|
@ -99,14 +99,14 @@ describe('API', () => {
|
||||||
|
|
||||||
// aliceは管理者、APIを使える
|
// aliceは管理者、APIを使える
|
||||||
await successfulApiCall({
|
await successfulApiCall({
|
||||||
endpoint: '/admin/get-index-stats',
|
endpoint: 'admin/get-index-stats',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
user: alice,
|
user: alice,
|
||||||
});
|
});
|
||||||
|
|
||||||
// bobは一般ユーザーだからダメ
|
// bobは一般ユーザーだからダメ
|
||||||
await failedApiCall({
|
await failedApiCall({
|
||||||
endpoint: '/admin/get-index-stats',
|
endpoint: 'admin/get-index-stats',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
user: bob,
|
user: bob,
|
||||||
}, {
|
}, {
|
||||||
|
@ -117,7 +117,7 @@ describe('API', () => {
|
||||||
|
|
||||||
// publicアクセスももちろんダメ
|
// publicアクセスももちろんダメ
|
||||||
await failedApiCall({
|
await failedApiCall({
|
||||||
endpoint: '/admin/get-index-stats',
|
endpoint: 'admin/get-index-stats',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
user: undefined,
|
user: undefined,
|
||||||
}, {
|
}, {
|
||||||
|
@ -128,7 +128,7 @@ describe('API', () => {
|
||||||
|
|
||||||
// ごまがしもダメ
|
// ごまがしもダメ
|
||||||
await failedApiCall({
|
await failedApiCall({
|
||||||
endpoint: '/admin/get-index-stats',
|
endpoint: 'admin/get-index-stats',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
user: { token: 'tsukawasete' },
|
user: { token: 'tsukawasete' },
|
||||||
}, {
|
}, {
|
||||||
|
@ -138,13 +138,13 @@ describe('API', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
await successfulApiCall({
|
await successfulApiCall({
|
||||||
endpoint: '/admin/get-index-stats',
|
endpoint: 'admin/get-index-stats',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
user: { token: application2 },
|
user: { token: application2 },
|
||||||
});
|
});
|
||||||
|
|
||||||
await failedApiCall({
|
await failedApiCall({
|
||||||
endpoint: '/admin/get-index-stats',
|
endpoint: 'admin/get-index-stats',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
user: { token: application },
|
user: { token: application },
|
||||||
}, {
|
}, {
|
||||||
|
@ -154,7 +154,7 @@ describe('API', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
await failedApiCall({
|
await failedApiCall({
|
||||||
endpoint: '/admin/get-index-stats',
|
endpoint: 'admin/get-index-stats',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
user: { token: application3 },
|
user: { token: application3 },
|
||||||
}, {
|
}, {
|
||||||
|
@ -164,7 +164,7 @@ describe('API', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
await failedApiCall({
|
await failedApiCall({
|
||||||
endpoint: '/admin/get-index-stats',
|
endpoint: 'admin/get-index-stats',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
user: { token: application4 },
|
user: { token: application4 },
|
||||||
}, {
|
}, {
|
||||||
|
@ -177,7 +177,7 @@ describe('API', () => {
|
||||||
describe('Authentication header', () => {
|
describe('Authentication header', () => {
|
||||||
test('一般リクエスト', async () => {
|
test('一般リクエスト', async () => {
|
||||||
await successfulApiCall({
|
await successfulApiCall({
|
||||||
endpoint: '/admin/get-index-stats',
|
endpoint: 'admin/get-index-stats',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
user: {
|
user: {
|
||||||
token: alice.token,
|
token: alice.token,
|
||||||
|
@ -211,7 +211,7 @@ describe('API', () => {
|
||||||
describe('tokenエラー応答でWWW-Authenticate headerを送る', () => {
|
describe('tokenエラー応答でWWW-Authenticate headerを送る', () => {
|
||||||
describe('invalid_token', () => {
|
describe('invalid_token', () => {
|
||||||
test('一般リクエスト', async () => {
|
test('一般リクエスト', async () => {
|
||||||
const result = await api('/admin/get-index-stats', {}, {
|
const result = await api('admin/get-index-stats', {}, {
|
||||||
token: 'syuilo',
|
token: 'syuilo',
|
||||||
bearer: true,
|
bearer: true,
|
||||||
});
|
});
|
||||||
|
@ -246,7 +246,7 @@ describe('API', () => {
|
||||||
|
|
||||||
describe('tokenがないとrealmだけおくる', () => {
|
describe('tokenがないとrealmだけおくる', () => {
|
||||||
test('一般リクエスト', async () => {
|
test('一般リクエスト', async () => {
|
||||||
const result = await api('/admin/get-index-stats', {});
|
const result = await api('admin/get-index-stats', {});
|
||||||
assert.strictEqual(result.status, 401);
|
assert.strictEqual(result.status, 401);
|
||||||
assert.strictEqual(result.headers.get('WWW-Authenticate'), 'Bearer realm="Misskey"');
|
assert.strictEqual(result.headers.get('WWW-Authenticate'), 'Bearer realm="Misskey"');
|
||||||
});
|
});
|
||||||
|
@ -259,7 +259,8 @@ describe('API', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('invalid_request', async () => {
|
test('invalid_request', async () => {
|
||||||
const result = await api('/notes/create', { text: true }, {
|
// @ts-expect-error text must be string
|
||||||
|
const result = await api('notes/create', { text: true }, {
|
||||||
token: alice.token,
|
token: alice.token,
|
||||||
bearer: true,
|
bearer: true,
|
||||||
});
|
});
|
||||||
|
|
|
@ -22,7 +22,7 @@ describe('Block', () => {
|
||||||
}, 1000 * 60 * 2);
|
}, 1000 * 60 * 2);
|
||||||
|
|
||||||
test('Block作成', async () => {
|
test('Block作成', async () => {
|
||||||
const res = await api('/blocking/create', {
|
const res = await api('blocking/create', {
|
||||||
userId: bob.id,
|
userId: bob.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ describe('Block', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('ブロックされているユーザーをフォローできない', async () => {
|
test('ブロックされているユーザーをフォローできない', async () => {
|
||||||
const res = await api('/following/create', { userId: alice.id }, bob);
|
const res = await api('following/create', { userId: alice.id }, bob);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
assert.strictEqual(res.body.error.id, 'c4ab57cc-4e41-45e9-bfd9-584f61e35ce0');
|
assert.strictEqual(res.body.error.id, 'c4ab57cc-4e41-45e9-bfd9-584f61e35ce0');
|
||||||
|
@ -39,7 +39,7 @@ describe('Block', () => {
|
||||||
test('ブロックされているユーザーにリアクションできない', async () => {
|
test('ブロックされているユーザーにリアクションできない', async () => {
|
||||||
const note = await post(alice, { text: 'hello' });
|
const note = await post(alice, { text: 'hello' });
|
||||||
|
|
||||||
const res = await api('/notes/reactions/create', { noteId: note.id, reaction: '👍' }, bob);
|
const res = await api('notes/reactions/create', { noteId: note.id, reaction: '👍' }, bob);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
assert.strictEqual(res.body.error.id, '20ef5475-9f38-4e4c-bd33-de6d979498ec');
|
assert.strictEqual(res.body.error.id, '20ef5475-9f38-4e4c-bd33-de6d979498ec');
|
||||||
|
@ -48,7 +48,7 @@ describe('Block', () => {
|
||||||
test('ブロックされているユーザーに返信できない', async () => {
|
test('ブロックされているユーザーに返信できない', async () => {
|
||||||
const note = await post(alice, { text: 'hello' });
|
const note = await post(alice, { text: 'hello' });
|
||||||
|
|
||||||
const res = await api('/notes/create', { replyId: note.id, text: 'yo' }, bob);
|
const res = await api('notes/create', { replyId: note.id, text: 'yo' }, bob);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3');
|
assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3');
|
||||||
|
@ -57,7 +57,7 @@ describe('Block', () => {
|
||||||
test('ブロックされているユーザーのノートをRenoteできない', async () => {
|
test('ブロックされているユーザーのノートをRenoteできない', async () => {
|
||||||
const note = await post(alice, { text: 'hello' });
|
const note = await post(alice, { text: 'hello' });
|
||||||
|
|
||||||
const res = await api('/notes/create', { renoteId: note.id, text: 'yo' }, bob);
|
const res = await api('notes/create', { renoteId: note.id, text: 'yo' }, bob);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3');
|
assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3');
|
||||||
|
@ -72,12 +72,13 @@ describe('Block', () => {
|
||||||
const bobNote = await post(bob, { text: 'hi' });
|
const bobNote = await post(bob, { text: 'hi' });
|
||||||
const carolNote = await post(carol, { text: 'hi' });
|
const carolNote = await post(carol, { text: 'hi' });
|
||||||
|
|
||||||
const res = await api('/notes/local-timeline', {}, bob);
|
const res = await api('notes/local-timeline', {}, bob);
|
||||||
|
const body = res.body as misskey.entities.Note[];
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), false);
|
assert.strictEqual(body.some(note => note.id === aliceNote.id), false);
|
||||||
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
|
assert.strictEqual(body.some(note => note.id === bobNote.id), true);
|
||||||
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
|
assert.strictEqual(body.some(note => note.id === carolNote.id), true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,47 +6,34 @@
|
||||||
process.env.NODE_ENV = 'test';
|
process.env.NODE_ENV = 'test';
|
||||||
|
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import { JTDDataType } from 'ajv/dist/jtd';
|
|
||||||
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
|
||||||
import { paramDef as CreateParamDef } from '@/server/api/endpoints/clips/create.js';
|
|
||||||
import { paramDef as UpdateParamDef } from '@/server/api/endpoints/clips/update.js';
|
|
||||||
import { paramDef as DeleteParamDef } from '@/server/api/endpoints/clips/delete.js';
|
|
||||||
import { paramDef as ShowParamDef } from '@/server/api/endpoints/clips/show.js';
|
|
||||||
import { paramDef as FavoriteParamDef } from '@/server/api/endpoints/clips/favorite.js';
|
|
||||||
import { paramDef as UnfavoriteParamDef } from '@/server/api/endpoints/clips/unfavorite.js';
|
|
||||||
import { paramDef as AddNoteParamDef } from '@/server/api/endpoints/clips/add-note.js';
|
|
||||||
import { paramDef as RemoveNoteParamDef } from '@/server/api/endpoints/clips/remove-note.js';
|
|
||||||
import { paramDef as NotesParamDef } from '@/server/api/endpoints/clips/notes.js';
|
|
||||||
import { api, ApiRequest, failedApiCall, hiddenNote, post, signup, successfulApiCall } from '../utils.js';
|
import { api, ApiRequest, failedApiCall, hiddenNote, post, signup, successfulApiCall } from '../utils.js';
|
||||||
|
import type * as Misskey from 'misskey-js';
|
||||||
|
|
||||||
|
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
|
||||||
|
|
||||||
describe('クリップ', () => {
|
describe('クリップ', () => {
|
||||||
type User = Packed<'User'>;
|
let alice: Misskey.entities.SignupResponse;
|
||||||
type Note = Packed<'Note'>;
|
let bob: Misskey.entities.SignupResponse;
|
||||||
type Clip = Packed<'Clip'>;
|
let aliceNote: Misskey.entities.Note;
|
||||||
|
let aliceHomeNote: Misskey.entities.Note;
|
||||||
let alice: User;
|
let aliceFollowersNote: Misskey.entities.Note;
|
||||||
let bob: User;
|
let aliceSpecifiedNote: Misskey.entities.Note;
|
||||||
let aliceNote: Note;
|
let bobNote: Misskey.entities.Note;
|
||||||
let aliceHomeNote: Note;
|
let bobHomeNote: Misskey.entities.Note;
|
||||||
let aliceFollowersNote: Note;
|
let bobFollowersNote: Misskey.entities.Note;
|
||||||
let aliceSpecifiedNote: Note;
|
let bobSpecifiedNote: Misskey.entities.Note;
|
||||||
let bobNote: Note;
|
|
||||||
let bobHomeNote: Note;
|
|
||||||
let bobFollowersNote: Note;
|
|
||||||
let bobSpecifiedNote: Note;
|
|
||||||
|
|
||||||
const compareBy = <T extends { id: string }, >(selector: (s: T) => string = (s: T): string => s.id) => (a: T, b: T): number => {
|
const compareBy = <T extends { id: string }, >(selector: (s: T) => string = (s: T): string => s.id) => (a: T, b: T): number => {
|
||||||
return selector(a).localeCompare(selector(b));
|
return selector(a).localeCompare(selector(b));
|
||||||
};
|
};
|
||||||
|
|
||||||
type CreateParam = JTDDataType<typeof CreateParamDef>;
|
const defaultCreate = (): Pick<Misskey.entities.ClipsCreateRequest, 'name'> => ({
|
||||||
const defaultCreate = (): Partial<CreateParam> => ({
|
|
||||||
name: 'test',
|
name: 'test',
|
||||||
});
|
});
|
||||||
const create = async (parameters: Partial<CreateParam> = {}, request: Partial<ApiRequest> = {}): Promise<Clip> => {
|
const create = async (parameters: Partial<Misskey.entities.ClipsCreateRequest> = {}, request: Partial<ApiRequest<'clips/create'>> = {}): Promise<Misskey.entities.Clip> => {
|
||||||
const clip = await successfulApiCall<Clip>({
|
const clip = await successfulApiCall({
|
||||||
endpoint: '/clips/create',
|
endpoint: 'clips/create',
|
||||||
parameters: {
|
parameters: {
|
||||||
...defaultCreate(),
|
...defaultCreate(),
|
||||||
...parameters,
|
...parameters,
|
||||||
|
@ -64,17 +51,16 @@ describe('クリップ', () => {
|
||||||
return clip;
|
return clip;
|
||||||
};
|
};
|
||||||
|
|
||||||
const createMany = async (parameters: Partial<CreateParam>, count = 10, user = alice): Promise<Clip[]> => {
|
const createMany = async (parameters: Partial<Misskey.entities.ClipsCreateRequest>, count = 10, user = alice): Promise<Misskey.entities.Clip[]> => {
|
||||||
return await Promise.all([...Array(count)].map((_, i) => create({
|
return await Promise.all([...Array(count)].map((_, i) => create({
|
||||||
name: `test${i}`,
|
name: `test${i}`,
|
||||||
...parameters,
|
...parameters,
|
||||||
}, { user })));
|
}, { user })));
|
||||||
};
|
};
|
||||||
|
|
||||||
type UpdateParam = JTDDataType<typeof UpdateParamDef>;
|
const update = async (parameters: Optional<Misskey.entities.ClipsUpdateRequest, 'name'>, request: Partial<ApiRequest<'clips/update'>> = {}): Promise<Misskey.entities.Clip> => {
|
||||||
const update = async (parameters: Partial<UpdateParam>, request: Partial<ApiRequest> = {}): Promise<Clip> => {
|
const clip = await successfulApiCall({
|
||||||
const clip = await successfulApiCall<Clip>({
|
endpoint: 'clips/update',
|
||||||
endpoint: '/clips/update',
|
|
||||||
parameters: {
|
parameters: {
|
||||||
name: 'updated',
|
name: 'updated',
|
||||||
...parameters,
|
...parameters,
|
||||||
|
@ -92,41 +78,39 @@ describe('クリップ', () => {
|
||||||
return clip;
|
return clip;
|
||||||
};
|
};
|
||||||
|
|
||||||
type DeleteParam = JTDDataType<typeof DeleteParamDef>;
|
const deleteClip = async (parameters: Misskey.entities.ClipsDeleteRequest, request: Partial<ApiRequest<'clips/delete'>> = {}): Promise<void> => {
|
||||||
const deleteClip = async (parameters: DeleteParam, request: Partial<ApiRequest> = {}): Promise<void> => {
|
return await successfulApiCall({
|
||||||
return await successfulApiCall<void>({
|
endpoint: 'clips/delete',
|
||||||
endpoint: '/clips/delete',
|
|
||||||
parameters,
|
parameters,
|
||||||
user: alice,
|
user: alice,
|
||||||
...request,
|
...request,
|
||||||
}, {
|
}, {
|
||||||
status: 204,
|
status: 204,
|
||||||
});
|
}) as any as void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ShowParam = JTDDataType<typeof ShowParamDef>;
|
const show = async (parameters: Misskey.entities.ClipsShowRequest, request: Partial<ApiRequest<'clips/show'>> = {}): Promise<Misskey.entities.Clip> => {
|
||||||
const show = async (parameters: ShowParam, request: Partial<ApiRequest> = {}): Promise<Clip> => {
|
return await successfulApiCall({
|
||||||
return await successfulApiCall<Clip>({
|
endpoint: 'clips/show',
|
||||||
endpoint: '/clips/show',
|
|
||||||
parameters,
|
parameters,
|
||||||
user: alice,
|
user: alice,
|
||||||
...request,
|
...request,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const list = async (request: Partial<ApiRequest>): Promise<Clip[]> => {
|
const list = async (request: Partial<ApiRequest<'clips/list'>>): Promise<Misskey.entities.Clip[]> => {
|
||||||
return successfulApiCall<Clip[]>({
|
return successfulApiCall({
|
||||||
endpoint: '/clips/list',
|
endpoint: 'clips/list',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
user: alice,
|
user: alice,
|
||||||
...request,
|
...request,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const usersClips = async (request: Partial<ApiRequest>): Promise<Clip[]> => {
|
const usersClips = async (parameters: Misskey.entities.UsersClipsRequest, request: Partial<ApiRequest<'users/clips'>> = {}): Promise<Misskey.entities.Clip[]> => {
|
||||||
return await successfulApiCall<Clip[]>({
|
return await successfulApiCall({
|
||||||
endpoint: '/users/clips',
|
endpoint: 'users/clips',
|
||||||
parameters: {},
|
parameters,
|
||||||
user: alice,
|
user: alice,
|
||||||
...request,
|
...request,
|
||||||
});
|
});
|
||||||
|
@ -136,23 +120,22 @@ describe('クリップ', () => {
|
||||||
alice = await signup({ username: 'alice' });
|
alice = await signup({ username: 'alice' });
|
||||||
bob = await signup({ username: 'bob' });
|
bob = await signup({ username: 'bob' });
|
||||||
|
|
||||||
// FIXME: misskey-jsのNoteはoutdatedなので直接変換できない
|
aliceNote = await post(alice, { text: 'test' });
|
||||||
aliceNote = await post(alice, { text: 'test' }) as any;
|
aliceHomeNote = await post(alice, { text: 'home only', visibility: 'home' });
|
||||||
aliceHomeNote = await post(alice, { text: 'home only', visibility: 'home' }) as any;
|
aliceFollowersNote = await post(alice, { text: 'followers only', visibility: 'followers' });
|
||||||
aliceFollowersNote = await post(alice, { text: 'followers only', visibility: 'followers' }) as any;
|
aliceSpecifiedNote = await post(alice, { text: 'specified only', visibility: 'specified' });
|
||||||
aliceSpecifiedNote = await post(alice, { text: 'specified only', visibility: 'specified' }) as any;
|
bobNote = await post(bob, { text: 'test' });
|
||||||
bobNote = await post(bob, { text: 'test' }) as any;
|
bobHomeNote = await post(bob, { text: 'home only', visibility: 'home' });
|
||||||
bobHomeNote = await post(bob, { text: 'home only', visibility: 'home' }) as any;
|
bobFollowersNote = await post(bob, { text: 'followers only', visibility: 'followers' });
|
||||||
bobFollowersNote = await post(bob, { text: 'followers only', visibility: 'followers' }) as any;
|
bobSpecifiedNote = await post(bob, { text: 'specified only', visibility: 'specified' });
|
||||||
bobSpecifiedNote = await post(bob, { text: 'specified only', visibility: 'specified' }) as any;
|
|
||||||
}, 1000 * 60 * 2);
|
}, 1000 * 60 * 2);
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
// テスト間で影響し合わないように毎回全部消す。
|
// テスト間で影響し合わないように毎回全部消す。
|
||||||
for (const user of [alice, bob]) {
|
for (const user of [alice, bob]) {
|
||||||
const list = await api('/clips/list', { limit: 11 }, user);
|
const list = await api('clips/list', { limit: 11 }, user);
|
||||||
for (const clip of list.body) {
|
for (const clip of list.body) {
|
||||||
await api('/clips/delete', { clipId: clip.id }, user);
|
await api('clips/delete', { clipId: clip.id }, user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -177,7 +160,7 @@ describe('クリップ', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
await failedApiCall({
|
await failedApiCall({
|
||||||
endpoint: '/clips/create',
|
endpoint: 'clips/create',
|
||||||
parameters: defaultCreate(),
|
parameters: defaultCreate(),
|
||||||
user: alice,
|
user: alice,
|
||||||
}, {
|
}, {
|
||||||
|
@ -204,7 +187,8 @@ describe('クリップ', () => {
|
||||||
{ label: 'descriptionが最大長+1', parameters: { description: 'a'.repeat(2049) } },
|
{ label: 'descriptionが最大長+1', parameters: { description: 'a'.repeat(2049) } },
|
||||||
];
|
];
|
||||||
test.each(createClipDenyPattern)('の作成は$labelならできない', async ({ parameters }) => failedApiCall({
|
test.each(createClipDenyPattern)('の作成は$labelならできない', async ({ parameters }) => failedApiCall({
|
||||||
endpoint: '/clips/create',
|
endpoint: 'clips/create',
|
||||||
|
// @ts-expect-error invalid params
|
||||||
parameters: {
|
parameters: {
|
||||||
...defaultCreate(),
|
...defaultCreate(),
|
||||||
...parameters,
|
...parameters,
|
||||||
|
@ -246,15 +230,15 @@ describe('クリップ', () => {
|
||||||
code: 'NO_SUCH_CLIP',
|
code: 'NO_SUCH_CLIP',
|
||||||
id: 'b4d92d70-b216-46fa-9a3f-a8c811699257',
|
id: 'b4d92d70-b216-46fa-9a3f-a8c811699257',
|
||||||
} },
|
} },
|
||||||
{ label: '他人のクリップ', user: (): User => bob, assertion: {
|
{ label: '他人のクリップ', user: () => bob, assertion: {
|
||||||
code: 'NO_SUCH_CLIP',
|
code: 'NO_SUCH_CLIP',
|
||||||
id: 'b4d92d70-b216-46fa-9a3f-a8c811699257',
|
id: 'b4d92d70-b216-46fa-9a3f-a8c811699257',
|
||||||
} },
|
} },
|
||||||
...createClipDenyPattern as any,
|
...createClipDenyPattern as any,
|
||||||
])('の更新は$labelならできない', async ({ parameters, user, assertion }) => failedApiCall({
|
])('の更新は$labelならできない', async ({ parameters, user, assertion }) => failedApiCall({
|
||||||
endpoint: '/clips/update',
|
endpoint: 'clips/update',
|
||||||
parameters: {
|
parameters: {
|
||||||
clipId: (await create({}, { user: (user ?? ((): User => alice))() })).id,
|
clipId: (await create({}, { user: (user ?? (() => alice))() })).id,
|
||||||
name: 'updated',
|
name: 'updated',
|
||||||
...parameters,
|
...parameters,
|
||||||
},
|
},
|
||||||
|
@ -279,14 +263,15 @@ describe('クリップ', () => {
|
||||||
code: 'NO_SUCH_CLIP',
|
code: 'NO_SUCH_CLIP',
|
||||||
id: '70ca08ba-6865-4630-b6fb-8494759aa754',
|
id: '70ca08ba-6865-4630-b6fb-8494759aa754',
|
||||||
} },
|
} },
|
||||||
{ label: '他人のクリップ', user: (): User => bob, assertion: {
|
{ label: '他人のクリップ', user: () => bob, assertion: {
|
||||||
code: 'NO_SUCH_CLIP',
|
code: 'NO_SUCH_CLIP',
|
||||||
id: '70ca08ba-6865-4630-b6fb-8494759aa754',
|
id: '70ca08ba-6865-4630-b6fb-8494759aa754',
|
||||||
} },
|
} },
|
||||||
])('の削除は$labelならできない', async ({ parameters, user, assertion }) => failedApiCall({
|
])('の削除は$labelならできない', async ({ parameters, user, assertion }) => failedApiCall({
|
||||||
endpoint: '/clips/delete',
|
endpoint: 'clips/delete',
|
||||||
parameters: {
|
parameters: {
|
||||||
clipId: (await create({}, { user: (user ?? ((): User => alice))() })).id,
|
// @ts-expect-error clipId must not be null
|
||||||
|
clipId: (await create({}, { user: (user ?? (() => alice))() })).id,
|
||||||
...parameters,
|
...parameters,
|
||||||
},
|
},
|
||||||
user: alice,
|
user: alice,
|
||||||
|
@ -306,7 +291,7 @@ describe('クリップ', () => {
|
||||||
test('のID指定取得は他人のPrivateなクリップは取得できない', async () => {
|
test('のID指定取得は他人のPrivateなクリップは取得できない', async () => {
|
||||||
const clip = await create({ isPublic: false }, { user: bob } );
|
const clip = await create({ isPublic: false }, { user: bob } );
|
||||||
failedApiCall({
|
failedApiCall({
|
||||||
endpoint: '/clips/show',
|
endpoint: 'clips/show',
|
||||||
parameters: { clipId: clip.id },
|
parameters: { clipId: clip.id },
|
||||||
user: alice,
|
user: alice,
|
||||||
}, {
|
}, {
|
||||||
|
@ -323,7 +308,8 @@ describe('クリップ', () => {
|
||||||
id: 'c3c5fe33-d62c-44d2-9ea5-d997703f5c20',
|
id: 'c3c5fe33-d62c-44d2-9ea5-d997703f5c20',
|
||||||
} },
|
} },
|
||||||
])('のID指定取得は$labelならできない', async ({ parameters, assetion }) => failedApiCall({
|
])('のID指定取得は$labelならできない', async ({ parameters, assetion }) => failedApiCall({
|
||||||
endpoint: '/clips/show',
|
endpoint: 'clips/show',
|
||||||
|
// @ts-expect-error clipId must not be undefined
|
||||||
parameters: {
|
parameters: {
|
||||||
...parameters,
|
...parameters,
|
||||||
},
|
},
|
||||||
|
@ -356,27 +342,23 @@ describe('クリップ', () => {
|
||||||
|
|
||||||
test('の一覧が取得できる(空)', async () => {
|
test('の一覧が取得できる(空)', async () => {
|
||||||
const res = await usersClips({
|
const res = await usersClips({
|
||||||
parameters: {
|
userId: alice.id,
|
||||||
userId: alice.id,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
assert.deepStrictEqual(res, []);
|
assert.deepStrictEqual(res, []);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.each([
|
test.each([
|
||||||
{ label: '' },
|
{ label: '' },
|
||||||
{ label: '他人アカウントから', user: (): User => bob },
|
{ label: '他人アカウントから', user: () => bob },
|
||||||
])('の一覧が$label取得できる', async () => {
|
])('の一覧が$label取得できる', async () => {
|
||||||
const clips = await createMany({ isPublic: true });
|
const clips = await createMany({ isPublic: true });
|
||||||
const res = await usersClips({
|
const res = await usersClips({
|
||||||
parameters: {
|
userId: alice.id,
|
||||||
userId: alice.id,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 返ってくる配列には順序保障がないのでidでソートして厳密比較
|
// 返ってくる配列には順序保障がないのでidでソートして厳密比較
|
||||||
assert.deepStrictEqual(
|
assert.deepStrictEqual(
|
||||||
res.sort(compareBy<Clip>(s => s.id)),
|
res.sort(compareBy<Misskey.entities.Clip>(s => s.id)),
|
||||||
clips.sort(compareBy(s => s.id)));
|
clips.sort(compareBy(s => s.id)));
|
||||||
|
|
||||||
// 認証状態で見たときだけisFavoritedが入っている
|
// 認証状態で見たときだけisFavoritedが入っている
|
||||||
|
@ -386,17 +368,16 @@ describe('クリップ', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test.each([
|
test.each([
|
||||||
{ label: '未認証', user: (): undefined => undefined },
|
{ label: '未認証', user: () => undefined },
|
||||||
{ label: '存在しないユーザーのもの', parameters: { userId: 'xxxxxxx' } },
|
{ label: '存在しないユーザーのもの', parameters: { userId: 'xxxxxxx' } },
|
||||||
])('の一覧は$labelでも取得できる', async ({ parameters, user }) => {
|
])('の一覧は$labelでも取得できる', async ({ parameters, user }) => {
|
||||||
const clips = await createMany({ isPublic: true });
|
const clips = await createMany({ isPublic: true });
|
||||||
const res = await usersClips({
|
const res = await usersClips({
|
||||||
parameters: {
|
userId: alice.id,
|
||||||
userId: alice.id,
|
limit: clips.length,
|
||||||
limit: clips.length,
|
...parameters,
|
||||||
...parameters,
|
}, {
|
||||||
},
|
user: (user ?? (() => alice))(),
|
||||||
user: (user ?? ((): User => alice))(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 未認証で見たときはisFavoritedは入らない
|
// 未認証で見たときはisFavoritedは入らない
|
||||||
|
@ -409,10 +390,8 @@ describe('クリップ', () => {
|
||||||
await create({ isPublic: false });
|
await create({ isPublic: false });
|
||||||
const aliceClip = await create({ isPublic: true });
|
const aliceClip = await create({ isPublic: true });
|
||||||
const res = await usersClips({
|
const res = await usersClips({
|
||||||
parameters: {
|
userId: alice.id,
|
||||||
userId: alice.id,
|
limit: 2,
|
||||||
limit: 2,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
assert.deepStrictEqual(res, [aliceClip]);
|
assert.deepStrictEqual(res, [aliceClip]);
|
||||||
});
|
});
|
||||||
|
@ -421,17 +400,15 @@ describe('クリップ', () => {
|
||||||
const clips = await createMany({ isPublic: true }, 7);
|
const clips = await createMany({ isPublic: true }, 7);
|
||||||
clips.sort(compareBy(s => s.id));
|
clips.sort(compareBy(s => s.id));
|
||||||
const res = await usersClips({
|
const res = await usersClips({
|
||||||
parameters: {
|
userId: alice.id,
|
||||||
userId: alice.id,
|
sinceId: clips[1].id,
|
||||||
sinceId: clips[1].id,
|
untilId: clips[5].id,
|
||||||
untilId: clips[5].id,
|
limit: 4,
|
||||||
limit: 4,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Promise.allで返ってくる配列には順序保障がないのでidでソートして厳密比較
|
// Promise.allで返ってくる配列には順序保障がないのでidでソートして厳密比較
|
||||||
assert.deepStrictEqual(
|
assert.deepStrictEqual(
|
||||||
res.sort(compareBy<Clip>(s => s.id)),
|
res.sort(compareBy<Misskey.entities.Clip>(s => s.id)),
|
||||||
[clips[2], clips[3], clips[4]], // sinceIdとuntilId自体は結果に含まれない
|
[clips[2], clips[3], clips[4]], // sinceIdとuntilId自体は結果に含まれない
|
||||||
clips[1].id + ' ... ' + clips[3].id + ' with ' + clips.map(s => s.id) + ' vs. ' + res.map(s => s.id));
|
clips[1].id + ' ... ' + clips[3].id + ' with ' + clips.map(s => s.id) + ' vs. ' + res.map(s => s.id));
|
||||||
});
|
});
|
||||||
|
@ -441,8 +418,9 @@ describe('クリップ', () => {
|
||||||
{ label: 'limitゼロ', parameters: { limit: 0 } },
|
{ label: 'limitゼロ', parameters: { limit: 0 } },
|
||||||
{ label: 'limit最大+1', parameters: { limit: 101 } },
|
{ label: 'limit最大+1', parameters: { limit: 101 } },
|
||||||
])('の一覧は$labelだと取得できない', async ({ parameters }) => failedApiCall({
|
])('の一覧は$labelだと取得できない', async ({ parameters }) => failedApiCall({
|
||||||
endpoint: '/users/clips',
|
endpoint: 'users/clips',
|
||||||
parameters: {
|
parameters: {
|
||||||
|
// @ts-expect-error userId must not be undefined
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
...parameters,
|
...parameters,
|
||||||
},
|
},
|
||||||
|
@ -454,15 +432,15 @@ describe('クリップ', () => {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
test.each([
|
test.each([
|
||||||
{ label: '作成', endpoint: '/clips/create' },
|
{ label: '作成', endpoint: 'clips/create' as const },
|
||||||
{ label: '更新', endpoint: '/clips/update' },
|
{ label: '更新', endpoint: 'clips/update' as const },
|
||||||
{ label: '削除', endpoint: '/clips/delete' },
|
{ label: '削除', endpoint: 'clips/delete' as const },
|
||||||
{ label: '取得', endpoint: '/clips/list' },
|
{ label: '取得', endpoint: 'clips/list' as const },
|
||||||
{ label: 'お気に入り設定', endpoint: '/clips/favorite' },
|
{ label: 'お気に入り設定', endpoint: 'clips/favorite' as const },
|
||||||
{ label: 'お気に入り解除', endpoint: '/clips/unfavorite' },
|
{ label: 'お気に入り解除', endpoint: 'clips/unfavorite' as const },
|
||||||
{ label: 'お気に入り取得', endpoint: '/clips/my-favorites' },
|
{ label: 'お気に入り取得', endpoint: 'clips/my-favorites' as const },
|
||||||
{ label: 'ノート追加', endpoint: '/clips/add-note' },
|
{ label: 'ノート追加', endpoint: 'clips/add-note' as const },
|
||||||
{ label: 'ノート削除', endpoint: '/clips/remove-note' },
|
{ label: 'ノート削除', endpoint: 'clips/remove-note' as const },
|
||||||
])('の$labelは未認証ではできない', async ({ endpoint }) => await failedApiCall({
|
])('の$labelは未認証ではできない', async ({ endpoint }) => await failedApiCall({
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
parameters: {},
|
parameters: {},
|
||||||
|
@ -474,35 +452,33 @@ describe('クリップ', () => {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('のお気に入り', () => {
|
describe('のお気に入り', () => {
|
||||||
let aliceClip: Clip;
|
let aliceClip: Misskey.entities.Clip;
|
||||||
|
|
||||||
type FavoriteParam = JTDDataType<typeof FavoriteParamDef>;
|
const favorite = async (parameters: Misskey.entities.ClipsFavoriteRequest, request: Partial<ApiRequest<'clips/favorite'>> = {}): Promise<void> => {
|
||||||
const favorite = async (parameters: FavoriteParam, request: Partial<ApiRequest> = {}): Promise<void> => {
|
return successfulApiCall({
|
||||||
return successfulApiCall<void>({
|
endpoint: 'clips/favorite',
|
||||||
endpoint: '/clips/favorite',
|
|
||||||
parameters,
|
parameters,
|
||||||
user: alice,
|
user: alice,
|
||||||
...request,
|
...request,
|
||||||
}, {
|
}, {
|
||||||
status: 204,
|
status: 204,
|
||||||
});
|
}) as any as void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type UnfavoriteParam = JTDDataType<typeof UnfavoriteParamDef>;
|
const unfavorite = async (parameters: Misskey.entities.ClipsUnfavoriteRequest, request: Partial<ApiRequest<'clips/unfavorite'>> = {}): Promise<void> => {
|
||||||
const unfavorite = async (parameters: UnfavoriteParam, request: Partial<ApiRequest> = {}): Promise<void> => {
|
return successfulApiCall({
|
||||||
return successfulApiCall<void>({
|
endpoint: 'clips/unfavorite',
|
||||||
endpoint: '/clips/unfavorite',
|
|
||||||
parameters,
|
parameters,
|
||||||
user: alice,
|
user: alice,
|
||||||
...request,
|
...request,
|
||||||
}, {
|
}, {
|
||||||
status: 204,
|
status: 204,
|
||||||
});
|
}) as any as void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const myFavorites = async (request: Partial<ApiRequest> = {}): Promise<Clip[]> => {
|
const myFavorites = async (request: Partial<ApiRequest<'clips/my-favorites'>> = {}): Promise<Misskey.entities.Clip[]> => {
|
||||||
return successfulApiCall<Clip[]>({
|
return successfulApiCall({
|
||||||
endpoint: '/clips/my-favorites',
|
endpoint: 'clips/my-favorites',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
user: alice,
|
user: alice,
|
||||||
...request,
|
...request,
|
||||||
|
@ -568,7 +544,7 @@ describe('クリップ', () => {
|
||||||
test('は同じクリップに対して二回設定できない。', async () => {
|
test('は同じクリップに対して二回設定できない。', async () => {
|
||||||
await favorite({ clipId: aliceClip.id });
|
await favorite({ clipId: aliceClip.id });
|
||||||
await failedApiCall({
|
await failedApiCall({
|
||||||
endpoint: '/clips/favorite',
|
endpoint: 'clips/favorite',
|
||||||
parameters: {
|
parameters: {
|
||||||
clipId: aliceClip.id,
|
clipId: aliceClip.id,
|
||||||
},
|
},
|
||||||
|
@ -586,14 +562,15 @@ describe('クリップ', () => {
|
||||||
code: 'NO_SUCH_CLIP',
|
code: 'NO_SUCH_CLIP',
|
||||||
id: '4c2aaeae-80d8-4250-9606-26cb1fdb77a5',
|
id: '4c2aaeae-80d8-4250-9606-26cb1fdb77a5',
|
||||||
} },
|
} },
|
||||||
{ label: '他人のクリップ', user: (): User => bob, assertion: {
|
{ label: '他人のクリップ', user: () => bob, assertion: {
|
||||||
code: 'NO_SUCH_CLIP',
|
code: 'NO_SUCH_CLIP',
|
||||||
id: '4c2aaeae-80d8-4250-9606-26cb1fdb77a5',
|
id: '4c2aaeae-80d8-4250-9606-26cb1fdb77a5',
|
||||||
} },
|
} },
|
||||||
])('の設定は$labelならできない', async ({ parameters, user, assertion }) => failedApiCall({
|
])('の設定は$labelならできない', async ({ parameters, user, assertion }) => failedApiCall({
|
||||||
endpoint: '/clips/favorite',
|
endpoint: 'clips/favorite',
|
||||||
parameters: {
|
parameters: {
|
||||||
clipId: (await create({}, { user: (user ?? ((): User => alice))() })).id,
|
// @ts-expect-error clipId must not be null
|
||||||
|
clipId: (await create({}, { user: (user ?? (() => alice))() })).id,
|
||||||
...parameters,
|
...parameters,
|
||||||
},
|
},
|
||||||
user: alice,
|
user: alice,
|
||||||
|
@ -619,7 +596,7 @@ describe('クリップ', () => {
|
||||||
code: 'NO_SUCH_CLIP',
|
code: 'NO_SUCH_CLIP',
|
||||||
id: '2603966e-b865-426c-94a7-af4a01241dc1',
|
id: '2603966e-b865-426c-94a7-af4a01241dc1',
|
||||||
} },
|
} },
|
||||||
{ label: '他人のクリップ', user: (): User => bob, assertion: {
|
{ label: '他人のクリップ', user: () => bob, assertion: {
|
||||||
code: 'NOT_FAVORITED',
|
code: 'NOT_FAVORITED',
|
||||||
id: '90c3a9e8-b321-4dae-bf57-2bf79bbcc187',
|
id: '90c3a9e8-b321-4dae-bf57-2bf79bbcc187',
|
||||||
} },
|
} },
|
||||||
|
@ -628,9 +605,10 @@ describe('クリップ', () => {
|
||||||
id: '90c3a9e8-b321-4dae-bf57-2bf79bbcc187',
|
id: '90c3a9e8-b321-4dae-bf57-2bf79bbcc187',
|
||||||
} },
|
} },
|
||||||
])('の設定解除は$labelならできない', async ({ parameters, user, assertion }) => failedApiCall({
|
])('の設定解除は$labelならできない', async ({ parameters, user, assertion }) => failedApiCall({
|
||||||
endpoint: '/clips/unfavorite',
|
endpoint: 'clips/unfavorite',
|
||||||
parameters: {
|
parameters: {
|
||||||
clipId: (await create({}, { user: (user ?? ((): User => alice))() })).id,
|
// @ts-expect-error clipId must not be null
|
||||||
|
clipId: (await create({}, { user: (user ?? (() => alice))() })).id,
|
||||||
...parameters,
|
...parameters,
|
||||||
},
|
},
|
||||||
user: alice,
|
user: alice,
|
||||||
|
@ -655,41 +633,38 @@ describe('クリップ', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('に紐づくノート', () => {
|
describe('に紐づくノート', () => {
|
||||||
let aliceClip: Clip;
|
let aliceClip: Misskey.entities.Clip;
|
||||||
|
|
||||||
const sampleNotes = (): Note[] => [
|
const sampleNotes = (): Misskey.entities.Note[] => [
|
||||||
aliceNote, aliceHomeNote, aliceFollowersNote, aliceSpecifiedNote,
|
aliceNote, aliceHomeNote, aliceFollowersNote, aliceSpecifiedNote,
|
||||||
bobNote, bobHomeNote, bobFollowersNote, bobSpecifiedNote,
|
bobNote, bobHomeNote, bobFollowersNote, bobSpecifiedNote,
|
||||||
];
|
];
|
||||||
|
|
||||||
type AddNoteParam = JTDDataType<typeof AddNoteParamDef>;
|
const addNote = async (parameters: Misskey.entities.ClipsAddNoteRequest, request: Partial<ApiRequest<'clips/add-note'>> = {}): Promise<void> => {
|
||||||
const addNote = async (parameters: AddNoteParam, request: Partial<ApiRequest> = {}): Promise<void> => {
|
return successfulApiCall({
|
||||||
return successfulApiCall<void>({
|
endpoint: 'clips/add-note',
|
||||||
endpoint: '/clips/add-note',
|
|
||||||
parameters,
|
parameters,
|
||||||
user: alice,
|
user: alice,
|
||||||
...request,
|
...request,
|
||||||
}, {
|
}, {
|
||||||
status: 204,
|
status: 204,
|
||||||
});
|
}) as any as void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type RemoveNoteParam = JTDDataType<typeof RemoveNoteParamDef>;
|
const removeNote = async (parameters: Misskey.entities.ClipsRemoveNoteRequest, request: Partial<ApiRequest<'clips/remove-note'>> = {}): Promise<void> => {
|
||||||
const removeNote = async (parameters: RemoveNoteParam, request: Partial<ApiRequest> = {}): Promise<void> => {
|
return successfulApiCall({
|
||||||
return successfulApiCall<void>({
|
endpoint: 'clips/remove-note',
|
||||||
endpoint: '/clips/remove-note',
|
|
||||||
parameters,
|
parameters,
|
||||||
user: alice,
|
user: alice,
|
||||||
...request,
|
...request,
|
||||||
}, {
|
}, {
|
||||||
status: 204,
|
status: 204,
|
||||||
});
|
}) as any as void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type NotesParam = JTDDataType<typeof NotesParamDef>;
|
const notes = async (parameters: Misskey.entities.ClipsNotesRequest, request: Partial<ApiRequest<'clips/notes'>> = {}): Promise<Misskey.entities.Note[]> => {
|
||||||
const notes = async (parameters: Partial<NotesParam>, request: Partial<ApiRequest> = {}): Promise<Note[]> => {
|
return successfulApiCall({
|
||||||
return successfulApiCall<Note[]>({
|
endpoint: 'clips/notes',
|
||||||
endpoint: '/clips/notes',
|
|
||||||
parameters,
|
parameters,
|
||||||
user: alice,
|
user: alice,
|
||||||
...request,
|
...request,
|
||||||
|
@ -715,7 +690,7 @@ describe('クリップ', () => {
|
||||||
test('として同じノートを二回紐づけることはできない', async () => {
|
test('として同じノートを二回紐づけることはできない', async () => {
|
||||||
await addNote({ clipId: aliceClip.id, noteId: aliceNote.id });
|
await addNote({ clipId: aliceClip.id, noteId: aliceNote.id });
|
||||||
await failedApiCall({
|
await failedApiCall({
|
||||||
endpoint: '/clips/add-note',
|
endpoint: 'clips/add-note',
|
||||||
parameters: {
|
parameters: {
|
||||||
clipId: aliceClip.id,
|
clipId: aliceClip.id,
|
||||||
noteId: aliceNote.id,
|
noteId: aliceNote.id,
|
||||||
|
@ -733,11 +708,11 @@ describe('クリップ', () => {
|
||||||
const noteLimit = DEFAULT_POLICIES.noteEachClipsLimit + 1;
|
const noteLimit = DEFAULT_POLICIES.noteEachClipsLimit + 1;
|
||||||
const noteList = await Promise.all([...Array(noteLimit)].map((_, i) => post(alice, {
|
const noteList = await Promise.all([...Array(noteLimit)].map((_, i) => post(alice, {
|
||||||
text: `test ${i}`,
|
text: `test ${i}`,
|
||||||
}) as unknown)) as Note[];
|
}) as unknown)) as Misskey.entities.Note[];
|
||||||
await Promise.all(noteList.map(s => addNote({ clipId: aliceClip.id, noteId: s.id })));
|
await Promise.all(noteList.map(s => addNote({ clipId: aliceClip.id, noteId: s.id })));
|
||||||
|
|
||||||
await failedApiCall({
|
await failedApiCall({
|
||||||
endpoint: '/clips/add-note',
|
endpoint: 'clips/add-note',
|
||||||
parameters: {
|
parameters: {
|
||||||
clipId: aliceClip.id,
|
clipId: aliceClip.id,
|
||||||
noteId: aliceNote.id,
|
noteId: aliceNote.id,
|
||||||
|
@ -751,7 +726,7 @@ describe('クリップ', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('は他人のクリップへ追加できない。', async () => await failedApiCall({
|
test('は他人のクリップへ追加できない。', async () => await failedApiCall({
|
||||||
endpoint: '/clips/add-note',
|
endpoint: 'clips/add-note',
|
||||||
parameters: {
|
parameters: {
|
||||||
clipId: aliceClip.id,
|
clipId: aliceClip.id,
|
||||||
noteId: aliceNote.id,
|
noteId: aliceNote.id,
|
||||||
|
@ -774,18 +749,20 @@ describe('クリップ', () => {
|
||||||
code: 'NO_SUCH_NOTE',
|
code: 'NO_SUCH_NOTE',
|
||||||
id: 'fc8c0b49-c7a3-4664-a0a6-b418d386bb8b',
|
id: 'fc8c0b49-c7a3-4664-a0a6-b418d386bb8b',
|
||||||
} },
|
} },
|
||||||
{ label: '他人のクリップ', user: (): object => bob, assetion: {
|
{ label: '他人のクリップ', user: () => bob, assetion: {
|
||||||
code: 'NO_SUCH_CLIP',
|
code: 'NO_SUCH_CLIP',
|
||||||
id: 'd6e76cc0-a1b5-4c7c-a287-73fa9c716dcf',
|
id: 'd6e76cc0-a1b5-4c7c-a287-73fa9c716dcf',
|
||||||
} },
|
} },
|
||||||
])('の追加は$labelだとできない', async ({ parameters, user, assetion }) => failedApiCall({
|
])('の追加は$labelだとできない', async ({ parameters, user, assetion }) => failedApiCall({
|
||||||
endpoint: '/clips/add-note',
|
endpoint: 'clips/add-note',
|
||||||
parameters: {
|
parameters: {
|
||||||
|
// @ts-expect-error clipId must not be undefined
|
||||||
clipId: aliceClip.id,
|
clipId: aliceClip.id,
|
||||||
|
// @ts-expect-error noteId must not be undefined
|
||||||
noteId: aliceNote.id,
|
noteId: aliceNote.id,
|
||||||
...parameters,
|
...parameters,
|
||||||
},
|
},
|
||||||
user: (user ?? ((): User => alice))(),
|
user: (user ?? (() => alice))(),
|
||||||
}, {
|
}, {
|
||||||
status: 400,
|
status: 400,
|
||||||
code: 'INVALID_PARAM',
|
code: 'INVALID_PARAM',
|
||||||
|
@ -810,18 +787,20 @@ describe('クリップ', () => {
|
||||||
code: 'NO_SUCH_NOTE',
|
code: 'NO_SUCH_NOTE',
|
||||||
id: 'aff017de-190e-434b-893e-33a9ff5049d8', // add-noteと異なる
|
id: 'aff017de-190e-434b-893e-33a9ff5049d8', // add-noteと異なる
|
||||||
} },
|
} },
|
||||||
{ label: '他人のクリップ', user: (): object => bob, assetion: {
|
{ label: '他人のクリップ', user: () => bob, assetion: {
|
||||||
code: 'NO_SUCH_CLIP',
|
code: 'NO_SUCH_CLIP',
|
||||||
id: 'b80525c6-97f7-49d7-a42d-ebccd49cfd52', // add-noteと異なる
|
id: 'b80525c6-97f7-49d7-a42d-ebccd49cfd52', // add-noteと異なる
|
||||||
} },
|
} },
|
||||||
])('の削除は$labelだとできない', async ({ parameters, user, assetion }) => failedApiCall({
|
])('の削除は$labelだとできない', async ({ parameters, user, assetion }) => failedApiCall({
|
||||||
endpoint: '/clips/remove-note',
|
endpoint: 'clips/remove-note',
|
||||||
parameters: {
|
parameters: {
|
||||||
|
// @ts-expect-error clipId must not be undefined
|
||||||
clipId: aliceClip.id,
|
clipId: aliceClip.id,
|
||||||
|
// @ts-expect-error noteId must not be undefined
|
||||||
noteId: aliceNote.id,
|
noteId: aliceNote.id,
|
||||||
...parameters,
|
...parameters,
|
||||||
},
|
},
|
||||||
user: (user ?? ((): User => alice))(),
|
user: (user ?? (() => alice))(),
|
||||||
}, {
|
}, {
|
||||||
status: 400,
|
status: 400,
|
||||||
code: 'INVALID_PARAM',
|
code: 'INVALID_PARAM',
|
||||||
|
@ -925,21 +904,22 @@ describe('クリップ', () => {
|
||||||
code: 'NO_SUCH_CLIP',
|
code: 'NO_SUCH_CLIP',
|
||||||
id: '1d7645e6-2b6d-4635-b0fe-fe22b0e72e00',
|
id: '1d7645e6-2b6d-4635-b0fe-fe22b0e72e00',
|
||||||
} },
|
} },
|
||||||
{ label: '他人のPrivateなクリップから', user: (): object => bob, assertion: {
|
{ label: '他人のPrivateなクリップから', user: () => bob, assertion: {
|
||||||
code: 'NO_SUCH_CLIP',
|
code: 'NO_SUCH_CLIP',
|
||||||
id: '1d7645e6-2b6d-4635-b0fe-fe22b0e72e00',
|
id: '1d7645e6-2b6d-4635-b0fe-fe22b0e72e00',
|
||||||
} },
|
} },
|
||||||
{ label: '未認証でPrivateなクリップから', user: (): undefined => undefined, assertion: {
|
{ label: '未認証でPrivateなクリップから', user: () => undefined, assertion: {
|
||||||
code: 'NO_SUCH_CLIP',
|
code: 'NO_SUCH_CLIP',
|
||||||
id: '1d7645e6-2b6d-4635-b0fe-fe22b0e72e00',
|
id: '1d7645e6-2b6d-4635-b0fe-fe22b0e72e00',
|
||||||
} },
|
} },
|
||||||
])('は$labelだと取得できない', async ({ parameters, user, assertion }) => failedApiCall({
|
])('は$labelだと取得できない', async ({ parameters, user, assertion }) => failedApiCall({
|
||||||
endpoint: '/clips/notes',
|
endpoint: 'clips/notes',
|
||||||
parameters: {
|
parameters: {
|
||||||
|
// @ts-expect-error clipId must not be undefined
|
||||||
clipId: aliceClip.id,
|
clipId: aliceClip.id,
|
||||||
...parameters,
|
...parameters,
|
||||||
},
|
},
|
||||||
user: (user ?? ((): User => alice))(),
|
user: (user ?? (() => alice))(),
|
||||||
}, {
|
}, {
|
||||||
status: 400,
|
status: 400,
|
||||||
code: 'INVALID_PARAM',
|
code: 'INVALID_PARAM',
|
||||||
|
|
|
@ -6,22 +6,14 @@
|
||||||
process.env.NODE_ENV = 'test';
|
process.env.NODE_ENV = 'test';
|
||||||
|
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import { MiNote } from '@/models/Note.js';
|
import { api, makeStreamCatcher, post, signup, uploadFile } from '../utils.js';
|
||||||
import { api, initTestDb, makeStreamCatcher, post, signup, uploadFile } from '../utils.js';
|
|
||||||
import type * as misskey from 'misskey-js';
|
import type * as misskey from 'misskey-js';
|
||||||
import type{ Repository } from 'typeorm'
|
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
|
||||||
|
|
||||||
|
|
||||||
describe('Drive', () => {
|
describe('Drive', () => {
|
||||||
let Notes: Repository<MiNote>;
|
|
||||||
|
|
||||||
let alice: misskey.entities.SignupResponse;
|
let alice: misskey.entities.SignupResponse;
|
||||||
let bob: misskey.entities.SignupResponse;
|
let bob: misskey.entities.SignupResponse;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const connection = await initTestDb(true);
|
|
||||||
Notes = connection.getRepository(MiNote);
|
|
||||||
alice = await signup({ username: 'alice' });
|
alice = await signup({ username: 'alice' });
|
||||||
bob = await signup({ username: 'bob' });
|
bob = await signup({ username: 'bob' });
|
||||||
}, 1000 * 60 * 2);
|
}, 1000 * 60 * 2);
|
||||||
|
@ -31,13 +23,13 @@ describe('Drive', () => {
|
||||||
|
|
||||||
const marker = Math.random().toString();
|
const marker = Math.random().toString();
|
||||||
|
|
||||||
const url = 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg'
|
const url = 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg';
|
||||||
|
|
||||||
const catcher = makeStreamCatcher(
|
const catcher = makeStreamCatcher(
|
||||||
alice,
|
alice,
|
||||||
'main',
|
'main',
|
||||||
(msg) => msg.type === 'urlUploadFinished' && msg.body.marker === marker,
|
(msg) => msg.type === 'urlUploadFinished' && msg.body.marker === marker,
|
||||||
(msg) => msg.body.file as Packed<'DriveFile'>,
|
(msg) => msg.body.file,
|
||||||
10 * 1000);
|
10 * 1000);
|
||||||
|
|
||||||
const res = await api('drive/files/upload-from-url', {
|
const res = await api('drive/files/upload-from-url', {
|
||||||
|
@ -51,7 +43,7 @@ describe('Drive', () => {
|
||||||
assert.strictEqual(res.status, 204);
|
assert.strictEqual(res.status, 204);
|
||||||
assert.strictEqual(file.name, 'Lenna.jpg');
|
assert.strictEqual(file.name, 'Lenna.jpg');
|
||||||
assert.strictEqual(file.type, 'image/jpeg');
|
assert.strictEqual(file.type, 'image/jpeg');
|
||||||
})
|
});
|
||||||
|
|
||||||
test('ローカルからアップロードできる', async () => {
|
test('ローカルからアップロードできる', async () => {
|
||||||
// APIレスポンスを直接使用するので utils.js uploadFile が通過することで成功とする
|
// APIレスポンスを直接使用するので utils.js uploadFile が通過することで成功とする
|
||||||
|
@ -59,27 +51,27 @@ describe('Drive', () => {
|
||||||
const res = await uploadFile(alice, { path: 'Lenna.jpg', name: 'テスト画像' });
|
const res = await uploadFile(alice, { path: 'Lenna.jpg', name: 'テスト画像' });
|
||||||
|
|
||||||
assert.strictEqual(res.body?.name, 'テスト画像.jpg');
|
assert.strictEqual(res.body?.name, 'テスト画像.jpg');
|
||||||
assert.strictEqual(res.body?.type, 'image/jpeg');
|
assert.strictEqual(res.body.type, 'image/jpeg');
|
||||||
})
|
});
|
||||||
|
|
||||||
test('添付ノート一覧を取得できる', async () => {
|
test('添付ノート一覧を取得できる', async () => {
|
||||||
const ids = (await Promise.all([uploadFile(alice), uploadFile(alice), uploadFile(alice)])).map(elm => elm.body!.id)
|
const ids = (await Promise.all([uploadFile(alice), uploadFile(alice), uploadFile(alice)])).map(elm => elm.body!.id);
|
||||||
|
|
||||||
const note0 = await post(alice, { fileIds: [ids[0]] });
|
const note0 = await post(alice, { fileIds: [ids[0]] });
|
||||||
const note1 = await post(alice, { fileIds: [ids[0], ids[1]] });
|
const note1 = await post(alice, { fileIds: [ids[0], ids[1]] });
|
||||||
|
|
||||||
const attached0 = await api('drive/files/attached-notes', { fileId: ids[0] }, alice);
|
const attached0 = await api('drive/files/attached-notes', { fileId: ids[0] }, alice);
|
||||||
assert.strictEqual(attached0.body.length, 2);
|
assert.strictEqual(attached0.body.length, 2);
|
||||||
assert.strictEqual(attached0.body[0].id, note1.id)
|
assert.strictEqual(attached0.body[0].id, note1.id);
|
||||||
assert.strictEqual(attached0.body[1].id, note0.id)
|
assert.strictEqual(attached0.body[1].id, note0.id);
|
||||||
|
|
||||||
const attached1 = await api('drive/files/attached-notes', { fileId: ids[1] }, alice);
|
const attached1 = await api('drive/files/attached-notes', { fileId: ids[1] }, alice);
|
||||||
assert.strictEqual(attached1.body.length, 1);
|
assert.strictEqual(attached1.body.length, 1);
|
||||||
assert.strictEqual(attached1.body[0].id, note1.id)
|
assert.strictEqual(attached1.body[0].id, note1.id);
|
||||||
|
|
||||||
const attached2 = await api('drive/files/attached-notes', { fileId: ids[2] }, alice);
|
const attached2 = await api('drive/files/attached-notes', { fileId: ids[2] }, alice);
|
||||||
assert.strictEqual(attached2.body.length, 0)
|
assert.strictEqual(attached2.body.length, 0);
|
||||||
})
|
});
|
||||||
|
|
||||||
test('添付ノート一覧は他の人から見えない', async () => {
|
test('添付ノート一覧は他の人から見えない', async () => {
|
||||||
const file = await uploadFile(alice);
|
const file = await uploadFile(alice);
|
||||||
|
@ -89,7 +81,5 @@ describe('Drive', () => {
|
||||||
const res = await api('drive/files/attached-notes', { fileId: file.body!.id }, bob);
|
const res = await api('drive/files/attached-notes', { fileId: file.body!.id }, bob);
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
assert.strictEqual('error' in res.body, true);
|
assert.strictEqual('error' in res.body, true);
|
||||||
|
});
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,7 @@ describe('Endpoints', () => {
|
||||||
test('クエリをインジェクションできない', async () => {
|
test('クエリをインジェクションできない', async () => {
|
||||||
const res = await api('signin', {
|
const res = await api('signin', {
|
||||||
username: 'test1',
|
username: 'test1',
|
||||||
|
// @ts-expect-error password must be string
|
||||||
password: {
|
password: {
|
||||||
$gt: '',
|
$gt: '',
|
||||||
},
|
},
|
||||||
|
@ -103,7 +104,7 @@ describe('Endpoints', () => {
|
||||||
const myLocation = '七森中';
|
const myLocation = '七森中';
|
||||||
const myBirthday = '2000-09-07';
|
const myBirthday = '2000-09-07';
|
||||||
|
|
||||||
const res = await api('/i/update', {
|
const res = await api('i/update', {
|
||||||
name: myName,
|
name: myName,
|
||||||
location: myLocation,
|
location: myLocation,
|
||||||
birthday: myBirthday,
|
birthday: myBirthday,
|
||||||
|
@ -117,7 +118,7 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('名前を空白にできる', async () => {
|
test('名前を空白にできる', async () => {
|
||||||
const res = await api('/i/update', {
|
const res = await api('i/update', {
|
||||||
name: ' ',
|
name: ' ',
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
|
@ -125,11 +126,11 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('誕生日の設定を削除できる', async () => {
|
test('誕生日の設定を削除できる', async () => {
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
birthday: '2000-09-07',
|
birthday: '2000-09-07',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const res = await api('/i/update', {
|
const res = await api('i/update', {
|
||||||
birthday: null,
|
birthday: null,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -139,7 +140,7 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('不正な誕生日の形式で怒られる', async () => {
|
test('不正な誕生日の形式で怒られる', async () => {
|
||||||
const res = await api('/i/update', {
|
const res = await api('i/update', {
|
||||||
birthday: '2000/09/07',
|
birthday: '2000/09/07',
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
|
@ -148,7 +149,7 @@ describe('Endpoints', () => {
|
||||||
|
|
||||||
describe('users/show', () => {
|
describe('users/show', () => {
|
||||||
test('ユーザーが取得できる', async () => {
|
test('ユーザーが取得できる', async () => {
|
||||||
const res = await api('/users/show', {
|
const res = await api('users/show', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -158,14 +159,14 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('ユーザーが存在しなかったら怒る', async () => {
|
test('ユーザーが存在しなかったら怒る', async () => {
|
||||||
const res = await api('/users/show', {
|
const res = await api('users/show', {
|
||||||
userId: '000000000000000000000000',
|
userId: '000000000000000000000000',
|
||||||
});
|
});
|
||||||
assert.strictEqual(res.status, 404);
|
assert.strictEqual(res.status, 404);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('間違ったIDで怒られる', async () => {
|
test('間違ったIDで怒られる', async () => {
|
||||||
const res = await api('/users/show', {
|
const res = await api('users/show', {
|
||||||
userId: 'kyoppie',
|
userId: 'kyoppie',
|
||||||
});
|
});
|
||||||
assert.strictEqual(res.status, 404);
|
assert.strictEqual(res.status, 404);
|
||||||
|
@ -178,7 +179,7 @@ describe('Endpoints', () => {
|
||||||
text: 'test',
|
text: 'test',
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = await api('/notes/show', {
|
const res = await api('notes/show', {
|
||||||
noteId: myPost.id,
|
noteId: myPost.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -189,14 +190,14 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('投稿が存在しなかったら怒る', async () => {
|
test('投稿が存在しなかったら怒る', async () => {
|
||||||
const res = await api('/notes/show', {
|
const res = await api('notes/show', {
|
||||||
noteId: '000000000000000000000000',
|
noteId: '000000000000000000000000',
|
||||||
});
|
});
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('間違ったIDで怒られる', async () => {
|
test('間違ったIDで怒られる', async () => {
|
||||||
const res = await api('/notes/show', {
|
const res = await api('notes/show', {
|
||||||
noteId: 'kyoppie',
|
noteId: 'kyoppie',
|
||||||
});
|
});
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
|
@ -207,14 +208,14 @@ describe('Endpoints', () => {
|
||||||
test('リアクションできる', async () => {
|
test('リアクションできる', async () => {
|
||||||
const bobPost = await post(bob, { text: 'hi' });
|
const bobPost = await post(bob, { text: 'hi' });
|
||||||
|
|
||||||
const res = await api('/notes/reactions/create', {
|
const res = await api('notes/reactions/create', {
|
||||||
noteId: bobPost.id,
|
noteId: bobPost.id,
|
||||||
reaction: '🚀',
|
reaction: '🚀',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 204);
|
assert.strictEqual(res.status, 204);
|
||||||
|
|
||||||
const resNote = await api('/notes/show', {
|
const resNote = await api('notes/show', {
|
||||||
noteId: bobPost.id,
|
noteId: bobPost.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -225,7 +226,7 @@ describe('Endpoints', () => {
|
||||||
test('自分の投稿にもリアクションできる', async () => {
|
test('自分の投稿にもリアクションできる', async () => {
|
||||||
const myPost = await post(alice, { text: 'hi' });
|
const myPost = await post(alice, { text: 'hi' });
|
||||||
|
|
||||||
const res = await api('/notes/reactions/create', {
|
const res = await api('notes/reactions/create', {
|
||||||
noteId: myPost.id,
|
noteId: myPost.id,
|
||||||
reaction: '🚀',
|
reaction: '🚀',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -236,19 +237,19 @@ describe('Endpoints', () => {
|
||||||
test('二重にリアクションすると上書きされる', async () => {
|
test('二重にリアクションすると上書きされる', async () => {
|
||||||
const bobPost = await post(bob, { text: 'hi' });
|
const bobPost = await post(bob, { text: 'hi' });
|
||||||
|
|
||||||
await api('/notes/reactions/create', {
|
await api('notes/reactions/create', {
|
||||||
noteId: bobPost.id,
|
noteId: bobPost.id,
|
||||||
reaction: '🥰',
|
reaction: '🥰',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const res = await api('/notes/reactions/create', {
|
const res = await api('notes/reactions/create', {
|
||||||
noteId: bobPost.id,
|
noteId: bobPost.id,
|
||||||
reaction: '🚀',
|
reaction: '🚀',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 204);
|
assert.strictEqual(res.status, 204);
|
||||||
|
|
||||||
const resNote = await api('/notes/show', {
|
const resNote = await api('notes/show', {
|
||||||
noteId: bobPost.id,
|
noteId: bobPost.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -257,7 +258,7 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('存在しない投稿にはリアクションできない', async () => {
|
test('存在しない投稿にはリアクションできない', async () => {
|
||||||
const res = await api('/notes/reactions/create', {
|
const res = await api('notes/reactions/create', {
|
||||||
noteId: '000000000000000000000000',
|
noteId: '000000000000000000000000',
|
||||||
reaction: '🚀',
|
reaction: '🚀',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -266,13 +267,14 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('空のパラメータで怒られる', async () => {
|
test('空のパラメータで怒られる', async () => {
|
||||||
const res = await api('/notes/reactions/create', {}, alice);
|
// @ts-expect-error param must not be empty
|
||||||
|
const res = await api('notes/reactions/create', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('間違ったIDで怒られる', async () => {
|
test('間違ったIDで怒られる', async () => {
|
||||||
const res = await api('/notes/reactions/create', {
|
const res = await api('notes/reactions/create', {
|
||||||
noteId: 'kyoppie',
|
noteId: 'kyoppie',
|
||||||
reaction: '🚀',
|
reaction: '🚀',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -283,7 +285,7 @@ describe('Endpoints', () => {
|
||||||
|
|
||||||
describe('following/create', () => {
|
describe('following/create', () => {
|
||||||
test('フォローできる', async () => {
|
test('フォローできる', async () => {
|
||||||
const res = await api('/following/create', {
|
const res = await api('following/create', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -301,7 +303,7 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('既にフォローしている場合は怒る', async () => {
|
test('既にフォローしている場合は怒る', async () => {
|
||||||
const res = await api('/following/create', {
|
const res = await api('following/create', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -309,7 +311,7 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('存在しないユーザーはフォローできない', async () => {
|
test('存在しないユーザーはフォローできない', async () => {
|
||||||
const res = await api('/following/create', {
|
const res = await api('following/create', {
|
||||||
userId: '000000000000000000000000',
|
userId: '000000000000000000000000',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -317,7 +319,7 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('自分自身はフォローできない', async () => {
|
test('自分自身はフォローできない', async () => {
|
||||||
const res = await api('/following/create', {
|
const res = await api('following/create', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -325,13 +327,14 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('空のパラメータで怒られる', async () => {
|
test('空のパラメータで怒られる', async () => {
|
||||||
const res = await api('/following/create', {}, alice);
|
// @ts-expect-error params must not be empty
|
||||||
|
const res = await api('following/create', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('間違ったIDで怒られる', async () => {
|
test('間違ったIDで怒られる', async () => {
|
||||||
const res = await api('/following/create', {
|
const res = await api('following/create', {
|
||||||
userId: 'foo',
|
userId: 'foo',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -341,11 +344,11 @@ describe('Endpoints', () => {
|
||||||
|
|
||||||
describe('following/delete', () => {
|
describe('following/delete', () => {
|
||||||
test('フォロー解除できる', async () => {
|
test('フォロー解除できる', async () => {
|
||||||
await api('/following/create', {
|
await api('following/create', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
const res = await api('/following/delete', {
|
const res = await api('following/delete', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -363,7 +366,7 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('フォローしていない場合は怒る', async () => {
|
test('フォローしていない場合は怒る', async () => {
|
||||||
const res = await api('/following/delete', {
|
const res = await api('following/delete', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -371,7 +374,7 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('存在しないユーザーはフォロー解除できない', async () => {
|
test('存在しないユーザーはフォロー解除できない', async () => {
|
||||||
const res = await api('/following/delete', {
|
const res = await api('following/delete', {
|
||||||
userId: '000000000000000000000000',
|
userId: '000000000000000000000000',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -379,7 +382,7 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('自分自身はフォロー解除できない', async () => {
|
test('自分自身はフォロー解除できない', async () => {
|
||||||
const res = await api('/following/delete', {
|
const res = await api('following/delete', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -387,13 +390,14 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('空のパラメータで怒られる', async () => {
|
test('空のパラメータで怒られる', async () => {
|
||||||
const res = await api('/following/delete', {}, alice);
|
// @ts-expect-error params must not be empty
|
||||||
|
const res = await api('following/delete', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('間違ったIDで怒られる', async () => {
|
test('間違ったIDで怒られる', async () => {
|
||||||
const res = await api('/following/delete', {
|
const res = await api('following/delete', {
|
||||||
userId: 'kyoppie',
|
userId: 'kyoppie',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -403,20 +407,20 @@ describe('Endpoints', () => {
|
||||||
|
|
||||||
describe('channels/search', () => {
|
describe('channels/search', () => {
|
||||||
test('空白検索で一覧を取得できる', async () => {
|
test('空白検索で一覧を取得できる', async () => {
|
||||||
await api('/channels/create', {
|
await api('channels/create', {
|
||||||
name: 'aaa',
|
name: 'aaa',
|
||||||
description: 'bbb',
|
description: 'bbb',
|
||||||
}, bob);
|
}, bob);
|
||||||
await api('/channels/create', {
|
await api('channels/create', {
|
||||||
name: 'ccc1',
|
name: 'ccc1',
|
||||||
description: 'ddd1',
|
description: 'ddd1',
|
||||||
}, bob);
|
}, bob);
|
||||||
await api('/channels/create', {
|
await api('channels/create', {
|
||||||
name: 'ccc2',
|
name: 'ccc2',
|
||||||
description: 'ddd2',
|
description: 'ddd2',
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
const res = await api('/channels/search', {
|
const res = await api('channels/search', {
|
||||||
query: '',
|
query: '',
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -425,7 +429,7 @@ describe('Endpoints', () => {
|
||||||
assert.strictEqual(res.body.length, 3);
|
assert.strictEqual(res.body.length, 3);
|
||||||
});
|
});
|
||||||
test('名前のみの検索で名前を検索できる', async () => {
|
test('名前のみの検索で名前を検索できる', async () => {
|
||||||
const res = await api('/channels/search', {
|
const res = await api('channels/search', {
|
||||||
query: 'aaa',
|
query: 'aaa',
|
||||||
type: 'nameOnly',
|
type: 'nameOnly',
|
||||||
}, bob);
|
}, bob);
|
||||||
|
@ -436,7 +440,7 @@ describe('Endpoints', () => {
|
||||||
assert.strictEqual(res.body[0].name, 'aaa');
|
assert.strictEqual(res.body[0].name, 'aaa');
|
||||||
});
|
});
|
||||||
test('名前のみの検索で名前を複数検索できる', async () => {
|
test('名前のみの検索で名前を複数検索できる', async () => {
|
||||||
const res = await api('/channels/search', {
|
const res = await api('channels/search', {
|
||||||
query: 'ccc',
|
query: 'ccc',
|
||||||
type: 'nameOnly',
|
type: 'nameOnly',
|
||||||
}, bob);
|
}, bob);
|
||||||
|
@ -446,7 +450,7 @@ describe('Endpoints', () => {
|
||||||
assert.strictEqual(res.body.length, 2);
|
assert.strictEqual(res.body.length, 2);
|
||||||
});
|
});
|
||||||
test('名前のみの検索で説明は検索できない', async () => {
|
test('名前のみの検索で説明は検索できない', async () => {
|
||||||
const res = await api('/channels/search', {
|
const res = await api('channels/search', {
|
||||||
query: 'bbb',
|
query: 'bbb',
|
||||||
type: 'nameOnly',
|
type: 'nameOnly',
|
||||||
}, bob);
|
}, bob);
|
||||||
|
@ -456,7 +460,7 @@ describe('Endpoints', () => {
|
||||||
assert.strictEqual(res.body.length, 0);
|
assert.strictEqual(res.body.length, 0);
|
||||||
});
|
});
|
||||||
test('名前と説明の検索で名前を検索できる', async () => {
|
test('名前と説明の検索で名前を検索できる', async () => {
|
||||||
const res = await api('/channels/search', {
|
const res = await api('channels/search', {
|
||||||
query: 'ccc1',
|
query: 'ccc1',
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -466,7 +470,7 @@ describe('Endpoints', () => {
|
||||||
assert.strictEqual(res.body[0].name, 'ccc1');
|
assert.strictEqual(res.body[0].name, 'ccc1');
|
||||||
});
|
});
|
||||||
test('名前と説明での検索で説明を検索できる', async () => {
|
test('名前と説明での検索で説明を検索できる', async () => {
|
||||||
const res = await api('/channels/search', {
|
const res = await api('channels/search', {
|
||||||
query: 'ddd1',
|
query: 'ddd1',
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -476,7 +480,7 @@ describe('Endpoints', () => {
|
||||||
assert.strictEqual(res.body[0].name, 'ccc1');
|
assert.strictEqual(res.body[0].name, 'ccc1');
|
||||||
});
|
});
|
||||||
test('名前と説明の検索で名前を複数検索できる', async () => {
|
test('名前と説明の検索で名前を複数検索できる', async () => {
|
||||||
const res = await api('/channels/search', {
|
const res = await api('channels/search', {
|
||||||
query: 'ccc',
|
query: 'ccc',
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -485,7 +489,7 @@ describe('Endpoints', () => {
|
||||||
assert.strictEqual(res.body.length, 2);
|
assert.strictEqual(res.body.length, 2);
|
||||||
});
|
});
|
||||||
test('名前と説明での検索で説明を複数検索できる', async () => {
|
test('名前と説明での検索で説明を複数検索できる', async () => {
|
||||||
const res = await api('/channels/search', {
|
const res = await api('channels/search', {
|
||||||
query: 'ddd',
|
query: 'ddd',
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -506,7 +510,7 @@ describe('Endpoints', () => {
|
||||||
await uploadFile(alice, {
|
await uploadFile(alice, {
|
||||||
blob: new Blob([new Uint8Array(1024)]),
|
blob: new Blob([new Uint8Array(1024)]),
|
||||||
});
|
});
|
||||||
const res = await api('/drive', {}, alice);
|
const res = await api('drive', {}, alice);
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||||
expect(res.body).toHaveProperty('usage', 1792);
|
expect(res.body).toHaveProperty('usage', 1792);
|
||||||
|
@ -519,7 +523,7 @@ describe('Endpoints', () => {
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||||
assert.strictEqual(res.body.name, 'Lenna.jpg');
|
assert.strictEqual(res.body!.name, 'Lenna.jpg');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('ファイルに名前を付けられる', async () => {
|
test('ファイルに名前を付けられる', async () => {
|
||||||
|
@ -527,7 +531,7 @@ describe('Endpoints', () => {
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||||
assert.strictEqual(res.body.name, 'Belmond.jpg');
|
assert.strictEqual(res.body!.name, 'Belmond.jpg');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('ファイルに名前を付けられるが、拡張子は正しいものになる', async () => {
|
test('ファイルに名前を付けられるが、拡張子は正しいものになる', async () => {
|
||||||
|
@ -535,11 +539,12 @@ describe('Endpoints', () => {
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||||
assert.strictEqual(res.body.name, 'Belmond.png.jpg');
|
assert.strictEqual(res.body!.name, 'Belmond.png.jpg');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('ファイル無しで怒られる', async () => {
|
test('ファイル無しで怒られる', async () => {
|
||||||
const res = await api('/drive/files/create', {}, alice);
|
// @ts-expect-error params must not be empty
|
||||||
|
const res = await api('drive/files/create', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
});
|
});
|
||||||
|
@ -549,14 +554,14 @@ describe('Endpoints', () => {
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||||
assert.strictEqual(res.body.name, 'image.svg');
|
assert.strictEqual(res.body!.name, 'image.svg');
|
||||||
assert.strictEqual(res.body.type, 'image/svg+xml');
|
assert.strictEqual(res.body!.type, 'image/svg+xml');
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const type of ['webp', 'avif']) {
|
for (const type of ['webp', 'avif']) {
|
||||||
const mediaType = `image/${type}`;
|
const mediaType = `image/${type}`;
|
||||||
|
|
||||||
const getWebpublicType = async (user: any, fileId: string): Promise<string> => {
|
const getWebpublicType = async (user: misskey.entities.SignupResponse, fileId: string): Promise<string> => {
|
||||||
// drive/files/create does not expose webpublicType directly, so get it by posting it
|
// drive/files/create does not expose webpublicType directly, so get it by posting it
|
||||||
const res = await post(user, {
|
const res = await post(user, {
|
||||||
text: mediaType,
|
text: mediaType,
|
||||||
|
@ -573,10 +578,10 @@ describe('Endpoints', () => {
|
||||||
const res = await uploadFile(alice, { path });
|
const res = await uploadFile(alice, { path });
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(res.body.name, path);
|
assert.strictEqual(res.body!.name, path);
|
||||||
assert.strictEqual(res.body.type, mediaType);
|
assert.strictEqual(res.body!.type, mediaType);
|
||||||
|
|
||||||
const webpublicType = await getWebpublicType(alice, res.body.id);
|
const webpublicType = await getWebpublicType(alice, res.body!.id);
|
||||||
assert.strictEqual(webpublicType, 'image/webp');
|
assert.strictEqual(webpublicType, 'image/webp');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -584,10 +589,10 @@ describe('Endpoints', () => {
|
||||||
const path = `without-alpha.${type}`;
|
const path = `without-alpha.${type}`;
|
||||||
const res = await uploadFile(alice, { path });
|
const res = await uploadFile(alice, { path });
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(res.body.name, path);
|
assert.strictEqual(res.body!.name, path);
|
||||||
assert.strictEqual(res.body.type, mediaType);
|
assert.strictEqual(res.body!.type, mediaType);
|
||||||
|
|
||||||
const webpublicType = await getWebpublicType(alice, res.body.id);
|
const webpublicType = await getWebpublicType(alice, res.body!.id);
|
||||||
assert.strictEqual(webpublicType, 'image/webp');
|
assert.strictEqual(webpublicType, 'image/webp');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -598,8 +603,8 @@ describe('Endpoints', () => {
|
||||||
const file = (await uploadFile(alice)).body;
|
const file = (await uploadFile(alice)).body;
|
||||||
const newName = 'いちごパスタ.png';
|
const newName = 'いちごパスタ.png';
|
||||||
|
|
||||||
const res = await api('/drive/files/update', {
|
const res = await api('drive/files/update', {
|
||||||
fileId: file.id,
|
fileId: file!.id,
|
||||||
name: newName,
|
name: newName,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -611,8 +616,8 @@ describe('Endpoints', () => {
|
||||||
test('他人のファイルは更新できない', async () => {
|
test('他人のファイルは更新できない', async () => {
|
||||||
const file = (await uploadFile(alice)).body;
|
const file = (await uploadFile(alice)).body;
|
||||||
|
|
||||||
const res = await api('/drive/files/update', {
|
const res = await api('drive/files/update', {
|
||||||
fileId: file.id,
|
fileId: file!.id,
|
||||||
name: 'いちごパスタ.png',
|
name: 'いちごパスタ.png',
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -621,12 +626,12 @@ describe('Endpoints', () => {
|
||||||
|
|
||||||
test('親フォルダを更新できる', async () => {
|
test('親フォルダを更新できる', async () => {
|
||||||
const file = (await uploadFile(alice)).body;
|
const file = (await uploadFile(alice)).body;
|
||||||
const folder = (await api('/drive/folders/create', {
|
const folder = (await api('drive/folders/create', {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
}, alice)).body;
|
}, alice)).body;
|
||||||
|
|
||||||
const res = await api('/drive/files/update', {
|
const res = await api('drive/files/update', {
|
||||||
fileId: file.id,
|
fileId: file!.id,
|
||||||
folderId: folder.id,
|
folderId: folder.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -638,17 +643,17 @@ describe('Endpoints', () => {
|
||||||
test('親フォルダを無しにできる', async () => {
|
test('親フォルダを無しにできる', async () => {
|
||||||
const file = (await uploadFile(alice)).body;
|
const file = (await uploadFile(alice)).body;
|
||||||
|
|
||||||
const folder = (await api('/drive/folders/create', {
|
const folder = (await api('drive/folders/create', {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
}, alice)).body;
|
}, alice)).body;
|
||||||
|
|
||||||
await api('/drive/files/update', {
|
await api('drive/files/update', {
|
||||||
fileId: file.id,
|
fileId: file!.id,
|
||||||
folderId: folder.id,
|
folderId: folder.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const res = await api('/drive/files/update', {
|
const res = await api('drive/files/update', {
|
||||||
fileId: file.id,
|
fileId: file!.id,
|
||||||
folderId: null,
|
folderId: null,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -659,12 +664,12 @@ describe('Endpoints', () => {
|
||||||
|
|
||||||
test('他人のフォルダには入れられない', async () => {
|
test('他人のフォルダには入れられない', async () => {
|
||||||
const file = (await uploadFile(alice)).body;
|
const file = (await uploadFile(alice)).body;
|
||||||
const folder = (await api('/drive/folders/create', {
|
const folder = (await api('drive/folders/create', {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
}, bob)).body;
|
}, bob)).body;
|
||||||
|
|
||||||
const res = await api('/drive/files/update', {
|
const res = await api('drive/files/update', {
|
||||||
fileId: file.id,
|
fileId: file!.id,
|
||||||
folderId: folder.id,
|
folderId: folder.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -674,8 +679,8 @@ describe('Endpoints', () => {
|
||||||
test('存在しないフォルダで怒られる', async () => {
|
test('存在しないフォルダで怒られる', async () => {
|
||||||
const file = (await uploadFile(alice)).body;
|
const file = (await uploadFile(alice)).body;
|
||||||
|
|
||||||
const res = await api('/drive/files/update', {
|
const res = await api('drive/files/update', {
|
||||||
fileId: file.id,
|
fileId: file!.id,
|
||||||
folderId: '000000000000000000000000',
|
folderId: '000000000000000000000000',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -685,8 +690,8 @@ describe('Endpoints', () => {
|
||||||
test('不正なフォルダIDで怒られる', async () => {
|
test('不正なフォルダIDで怒られる', async () => {
|
||||||
const file = (await uploadFile(alice)).body;
|
const file = (await uploadFile(alice)).body;
|
||||||
|
|
||||||
const res = await api('/drive/files/update', {
|
const res = await api('drive/files/update', {
|
||||||
fileId: file.id,
|
fileId: file!.id,
|
||||||
folderId: 'foo',
|
folderId: 'foo',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -694,7 +699,7 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('ファイルが存在しなかったら怒る', async () => {
|
test('ファイルが存在しなかったら怒る', async () => {
|
||||||
const res = await api('/drive/files/update', {
|
const res = await api('drive/files/update', {
|
||||||
fileId: '000000000000000000000000',
|
fileId: '000000000000000000000000',
|
||||||
name: 'いちごパスタ.png',
|
name: 'いちごパスタ.png',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -706,8 +711,8 @@ describe('Endpoints', () => {
|
||||||
const file = (await uploadFile(alice)).body;
|
const file = (await uploadFile(alice)).body;
|
||||||
const newName = '';
|
const newName = '';
|
||||||
|
|
||||||
const res = await api('/drive/files/update', {
|
const res = await api('drive/files/update', {
|
||||||
fileId: file.id,
|
fileId: file!.id,
|
||||||
name: newName,
|
name: newName,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -715,7 +720,7 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('間違ったIDで怒られる', async () => {
|
test('間違ったIDで怒られる', async () => {
|
||||||
const res = await api('/drive/files/update', {
|
const res = await api('drive/files/update', {
|
||||||
fileId: 'kyoppie',
|
fileId: 'kyoppie',
|
||||||
name: 'いちごパスタ.png',
|
name: 'いちごパスタ.png',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -726,7 +731,7 @@ describe('Endpoints', () => {
|
||||||
|
|
||||||
describe('drive/folders/create', () => {
|
describe('drive/folders/create', () => {
|
||||||
test('フォルダを作成できる', async () => {
|
test('フォルダを作成できる', async () => {
|
||||||
const res = await api('/drive/folders/create', {
|
const res = await api('drive/folders/create', {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -738,11 +743,11 @@ describe('Endpoints', () => {
|
||||||
|
|
||||||
describe('drive/folders/update', () => {
|
describe('drive/folders/update', () => {
|
||||||
test('名前を更新できる', async () => {
|
test('名前を更新できる', async () => {
|
||||||
const folder = (await api('/drive/folders/create', {
|
const folder = (await api('drive/folders/create', {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
}, alice)).body;
|
}, alice)).body;
|
||||||
|
|
||||||
const res = await api('/drive/folders/update', {
|
const res = await api('drive/folders/update', {
|
||||||
folderId: folder.id,
|
folderId: folder.id,
|
||||||
name: 'new name',
|
name: 'new name',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -753,11 +758,11 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('他人のフォルダを更新できない', async () => {
|
test('他人のフォルダを更新できない', async () => {
|
||||||
const folder = (await api('/drive/folders/create', {
|
const folder = (await api('drive/folders/create', {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
}, bob)).body;
|
}, bob)).body;
|
||||||
|
|
||||||
const res = await api('/drive/folders/update', {
|
const res = await api('drive/folders/update', {
|
||||||
folderId: folder.id,
|
folderId: folder.id,
|
||||||
name: 'new name',
|
name: 'new name',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -766,14 +771,14 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('親フォルダを更新できる', async () => {
|
test('親フォルダを更新できる', async () => {
|
||||||
const folder = (await api('/drive/folders/create', {
|
const folder = (await api('drive/folders/create', {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
}, alice)).body;
|
}, alice)).body;
|
||||||
const parentFolder = (await api('/drive/folders/create', {
|
const parentFolder = (await api('drive/folders/create', {
|
||||||
name: 'parent',
|
name: 'parent',
|
||||||
}, alice)).body;
|
}, alice)).body;
|
||||||
|
|
||||||
const res = await api('/drive/folders/update', {
|
const res = await api('drive/folders/update', {
|
||||||
folderId: folder.id,
|
folderId: folder.id,
|
||||||
parentId: parentFolder.id,
|
parentId: parentFolder.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -784,18 +789,18 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('親フォルダを無しに更新できる', async () => {
|
test('親フォルダを無しに更新できる', async () => {
|
||||||
const folder = (await api('/drive/folders/create', {
|
const folder = (await api('drive/folders/create', {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
}, alice)).body;
|
}, alice)).body;
|
||||||
const parentFolder = (await api('/drive/folders/create', {
|
const parentFolder = (await api('drive/folders/create', {
|
||||||
name: 'parent',
|
name: 'parent',
|
||||||
}, alice)).body;
|
}, alice)).body;
|
||||||
await api('/drive/folders/update', {
|
await api('drive/folders/update', {
|
||||||
folderId: folder.id,
|
folderId: folder.id,
|
||||||
parentId: parentFolder.id,
|
parentId: parentFolder.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const res = await api('/drive/folders/update', {
|
const res = await api('drive/folders/update', {
|
||||||
folderId: folder.id,
|
folderId: folder.id,
|
||||||
parentId: null,
|
parentId: null,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -806,14 +811,14 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('他人のフォルダを親フォルダに設定できない', async () => {
|
test('他人のフォルダを親フォルダに設定できない', async () => {
|
||||||
const folder = (await api('/drive/folders/create', {
|
const folder = (await api('drive/folders/create', {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
}, alice)).body;
|
}, alice)).body;
|
||||||
const parentFolder = (await api('/drive/folders/create', {
|
const parentFolder = (await api('drive/folders/create', {
|
||||||
name: 'parent',
|
name: 'parent',
|
||||||
}, bob)).body;
|
}, bob)).body;
|
||||||
|
|
||||||
const res = await api('/drive/folders/update', {
|
const res = await api('drive/folders/update', {
|
||||||
folderId: folder.id,
|
folderId: folder.id,
|
||||||
parentId: parentFolder.id,
|
parentId: parentFolder.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -822,18 +827,18 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('フォルダが循環するような構造にできない', async () => {
|
test('フォルダが循環するような構造にできない', async () => {
|
||||||
const folder = (await api('/drive/folders/create', {
|
const folder = (await api('drive/folders/create', {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
}, alice)).body;
|
}, alice)).body;
|
||||||
const parentFolder = (await api('/drive/folders/create', {
|
const parentFolder = (await api('drive/folders/create', {
|
||||||
name: 'parent',
|
name: 'parent',
|
||||||
}, alice)).body;
|
}, alice)).body;
|
||||||
await api('/drive/folders/update', {
|
await api('drive/folders/update', {
|
||||||
folderId: parentFolder.id,
|
folderId: parentFolder.id,
|
||||||
parentId: folder.id,
|
parentId: folder.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const res = await api('/drive/folders/update', {
|
const res = await api('drive/folders/update', {
|
||||||
folderId: folder.id,
|
folderId: folder.id,
|
||||||
parentId: parentFolder.id,
|
parentId: parentFolder.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -842,25 +847,25 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('フォルダが循環するような構造にできない(再帰的)', async () => {
|
test('フォルダが循環するような構造にできない(再帰的)', async () => {
|
||||||
const folderA = (await api('/drive/folders/create', {
|
const folderA = (await api('drive/folders/create', {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
}, alice)).body;
|
}, alice)).body;
|
||||||
const folderB = (await api('/drive/folders/create', {
|
const folderB = (await api('drive/folders/create', {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
}, alice)).body;
|
}, alice)).body;
|
||||||
const folderC = (await api('/drive/folders/create', {
|
const folderC = (await api('drive/folders/create', {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
}, alice)).body;
|
}, alice)).body;
|
||||||
await api('/drive/folders/update', {
|
await api('drive/folders/update', {
|
||||||
folderId: folderB.id,
|
folderId: folderB.id,
|
||||||
parentId: folderA.id,
|
parentId: folderA.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
await api('/drive/folders/update', {
|
await api('drive/folders/update', {
|
||||||
folderId: folderC.id,
|
folderId: folderC.id,
|
||||||
parentId: folderB.id,
|
parentId: folderB.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const res = await api('/drive/folders/update', {
|
const res = await api('drive/folders/update', {
|
||||||
folderId: folderA.id,
|
folderId: folderA.id,
|
||||||
parentId: folderC.id,
|
parentId: folderC.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -869,11 +874,11 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('フォルダが循環するような構造にできない(自身)', async () => {
|
test('フォルダが循環するような構造にできない(自身)', async () => {
|
||||||
const folderA = (await api('/drive/folders/create', {
|
const folderA = (await api('drive/folders/create', {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
}, alice)).body;
|
}, alice)).body;
|
||||||
|
|
||||||
const res = await api('/drive/folders/update', {
|
const res = await api('drive/folders/update', {
|
||||||
folderId: folderA.id,
|
folderId: folderA.id,
|
||||||
parentId: folderA.id,
|
parentId: folderA.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -882,11 +887,11 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('存在しない親フォルダを設定できない', async () => {
|
test('存在しない親フォルダを設定できない', async () => {
|
||||||
const folder = (await api('/drive/folders/create', {
|
const folder = (await api('drive/folders/create', {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
}, alice)).body;
|
}, alice)).body;
|
||||||
|
|
||||||
const res = await api('/drive/folders/update', {
|
const res = await api('drive/folders/update', {
|
||||||
folderId: folder.id,
|
folderId: folder.id,
|
||||||
parentId: '000000000000000000000000',
|
parentId: '000000000000000000000000',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -895,11 +900,11 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('不正な親フォルダIDで怒られる', async () => {
|
test('不正な親フォルダIDで怒られる', async () => {
|
||||||
const folder = (await api('/drive/folders/create', {
|
const folder = (await api('drive/folders/create', {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
}, alice)).body;
|
}, alice)).body;
|
||||||
|
|
||||||
const res = await api('/drive/folders/update', {
|
const res = await api('drive/folders/update', {
|
||||||
folderId: folder.id,
|
folderId: folder.id,
|
||||||
parentId: 'foo',
|
parentId: 'foo',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -908,7 +913,7 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('存在しないフォルダを更新できない', async () => {
|
test('存在しないフォルダを更新できない', async () => {
|
||||||
const res = await api('/drive/folders/update', {
|
const res = await api('drive/folders/update', {
|
||||||
folderId: '000000000000000000000000',
|
folderId: '000000000000000000000000',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -916,7 +921,7 @@ describe('Endpoints', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('不正なフォルダIDで怒られる', async () => {
|
test('不正なフォルダIDで怒られる', async () => {
|
||||||
const res = await api('/drive/folders/update', {
|
const res = await api('drive/folders/update', {
|
||||||
folderId: 'foo',
|
folderId: 'foo',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -937,7 +942,7 @@ describe('Endpoints', () => {
|
||||||
visibleUserIds: [alice.id],
|
visibleUserIds: [alice.id],
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = await api('/notes/replies', {
|
const res = await api('notes/replies', {
|
||||||
noteId: alicePost.id,
|
noteId: alicePost.id,
|
||||||
}, carol);
|
}, carol);
|
||||||
|
|
||||||
|
@ -949,7 +954,7 @@ describe('Endpoints', () => {
|
||||||
|
|
||||||
describe('notes/timeline', () => {
|
describe('notes/timeline', () => {
|
||||||
test('フォロワー限定投稿が含まれる', async () => {
|
test('フォロワー限定投稿が含まれる', async () => {
|
||||||
await api('/following/create', {
|
await api('following/create', {
|
||||||
userId: carol.id,
|
userId: carol.id,
|
||||||
}, dave);
|
}, dave);
|
||||||
|
|
||||||
|
@ -958,7 +963,7 @@ describe('Endpoints', () => {
|
||||||
visibility: 'followers',
|
visibility: 'followers',
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = await api('/notes/timeline', {}, dave);
|
const res = await api('notes/timeline', {}, dave);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
@ -979,12 +984,12 @@ describe('Endpoints', () => {
|
||||||
test('他者に関するメモを更新できる', async () => {
|
test('他者に関するメモを更新できる', async () => {
|
||||||
const memo = '10月まで低浮上とのこと。';
|
const memo = '10月まで低浮上とのこと。';
|
||||||
|
|
||||||
const res1 = await api('/users/update-memo', {
|
const res1 = await api('users/update-memo', {
|
||||||
memo,
|
memo,
|
||||||
userId: bob.id,
|
userId: bob.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const res2 = await api('/users/show', {
|
const res2 = await api('users/show', {
|
||||||
userId: bob.id,
|
userId: bob.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(res1.status, 204);
|
assert.strictEqual(res1.status, 204);
|
||||||
|
@ -994,12 +999,12 @@ describe('Endpoints', () => {
|
||||||
test('自分に関するメモを更新できる', async () => {
|
test('自分に関するメモを更新できる', async () => {
|
||||||
const memo = 'チケットを月末までに買う。';
|
const memo = 'チケットを月末までに買う。';
|
||||||
|
|
||||||
const res1 = await api('/users/update-memo', {
|
const res1 = await api('users/update-memo', {
|
||||||
memo,
|
memo,
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const res2 = await api('/users/show', {
|
const res2 = await api('users/show', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(res1.status, 204);
|
assert.strictEqual(res1.status, 204);
|
||||||
|
@ -1009,17 +1014,17 @@ describe('Endpoints', () => {
|
||||||
test('メモを削除できる', async () => {
|
test('メモを削除できる', async () => {
|
||||||
const memo = '10月まで低浮上とのこと。';
|
const memo = '10月まで低浮上とのこと。';
|
||||||
|
|
||||||
await api('/users/update-memo', {
|
await api('users/update-memo', {
|
||||||
memo,
|
memo,
|
||||||
userId: bob.id,
|
userId: bob.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
await api('/users/update-memo', {
|
await api('users/update-memo', {
|
||||||
memo: '',
|
memo: '',
|
||||||
userId: bob.id,
|
userId: bob.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const res = await api('/users/show', {
|
const res = await api('users/show', {
|
||||||
userId: bob.id,
|
userId: bob.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -1032,21 +1037,21 @@ describe('Endpoints', () => {
|
||||||
const memoCarolToBob = '例の件について今度問いただす。';
|
const memoCarolToBob = '例の件について今度問いただす。';
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
api('/users/update-memo', {
|
api('users/update-memo', {
|
||||||
memo: memoAliceToBob,
|
memo: memoAliceToBob,
|
||||||
userId: bob.id,
|
userId: bob.id,
|
||||||
}, alice),
|
}, alice),
|
||||||
api('/users/update-memo', {
|
api('users/update-memo', {
|
||||||
memo: memoCarolToBob,
|
memo: memoCarolToBob,
|
||||||
userId: bob.id,
|
userId: bob.id,
|
||||||
}, carol),
|
}, carol),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const [resAlice, resCarol] = await Promise.all([
|
const [resAlice, resCarol] = await Promise.all([
|
||||||
api('/users/show', {
|
api('users/show', {
|
||||||
userId: bob.id,
|
userId: bob.id,
|
||||||
}, alice),
|
}, alice),
|
||||||
api('/users/show', {
|
api('users/show', {
|
||||||
userId: bob.id,
|
userId: bob.id,
|
||||||
}, carol),
|
}, carol),
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -18,7 +18,7 @@ describe('export-clips', () => {
|
||||||
// XXX: Any better way to get the result?
|
// XXX: Any better way to get the result?
|
||||||
async function pollFirstDriveFile() {
|
async function pollFirstDriveFile() {
|
||||||
while (true) {
|
while (true) {
|
||||||
const files = (await api('/drive/files', {}, alice)).body;
|
const files = (await api('drive/files', {}, alice)).body;
|
||||||
if (!files.length) {
|
if (!files.length) {
|
||||||
await new Promise(r => setTimeout(r, 100));
|
await new Promise(r => setTimeout(r, 100));
|
||||||
continue;
|
continue;
|
||||||
|
@ -26,7 +26,7 @@ describe('export-clips', () => {
|
||||||
if (files.length > 1) {
|
if (files.length > 1) {
|
||||||
throw new Error('Too many files?');
|
throw new Error('Too many files?');
|
||||||
}
|
}
|
||||||
const file = (await api('/drive/files/show', { fileId: files[0].id }, alice)).body;
|
const file = (await api('drive/files/show', { fileId: files[0].id }, alice)).body;
|
||||||
const res = await fetch(new URL(new URL(file.url).pathname, `http://127.0.0.1:${port}`));
|
const res = await fetch(new URL(new URL(file.url).pathname, `http://127.0.0.1:${port}`));
|
||||||
return await res.json();
|
return await res.json();
|
||||||
}
|
}
|
||||||
|
@ -44,16 +44,16 @@ describe('export-clips', () => {
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
// Clean all clips and files of alice
|
// Clean all clips and files of alice
|
||||||
const clips = (await api('/clips/list', {}, alice)).body;
|
const clips = (await api('clips/list', {}, alice)).body;
|
||||||
for (const clip of clips) {
|
for (const clip of clips) {
|
||||||
const res = await api('/clips/delete', { clipId: clip.id }, alice);
|
const res = await api('clips/delete', { clipId: clip.id }, alice);
|
||||||
if (res.status !== 204) {
|
if (res.status !== 204) {
|
||||||
throw new Error('Failed to delete clip');
|
throw new Error('Failed to delete clip');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const files = (await api('/drive/files', {}, alice)).body;
|
const files = (await api('drive/files', {}, alice)).body;
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const res = await api('/drive/files/delete', { fileId: file.id }, alice);
|
const res = await api('drive/files/delete', { fileId: file.id }, alice);
|
||||||
if (res.status !== 204) {
|
if (res.status !== 204) {
|
||||||
throw new Error('Failed to delete file');
|
throw new Error('Failed to delete file');
|
||||||
}
|
}
|
||||||
|
@ -61,13 +61,13 @@ describe('export-clips', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('basic export', async () => {
|
test('basic export', async () => {
|
||||||
let res = await api('/clips/create', {
|
let res = await api('clips/create', {
|
||||||
name: 'foo',
|
name: 'foo',
|
||||||
description: 'bar',
|
description: 'bar',
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
|
|
||||||
res = await api('/i/export-clips', {}, alice);
|
res = await api('i/export-clips', {}, alice);
|
||||||
assert.strictEqual(res.status, 204);
|
assert.strictEqual(res.status, 204);
|
||||||
|
|
||||||
const exported = await pollFirstDriveFile();
|
const exported = await pollFirstDriveFile();
|
||||||
|
@ -77,7 +77,7 @@ describe('export-clips', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('export with notes', async () => {
|
test('export with notes', async () => {
|
||||||
let res = await api('/clips/create', {
|
let res = await api('clips/create', {
|
||||||
name: 'foo',
|
name: 'foo',
|
||||||
description: 'bar',
|
description: 'bar',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -96,14 +96,14 @@ describe('export-clips', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const note of [note1, note2]) {
|
for (const note of [note1, note2]) {
|
||||||
res = await api('/clips/add-note', {
|
res = await api('clips/add-note', {
|
||||||
clipId: clip.id,
|
clipId: clip.id,
|
||||||
noteId: note.id,
|
noteId: note.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(res.status, 204);
|
assert.strictEqual(res.status, 204);
|
||||||
}
|
}
|
||||||
|
|
||||||
res = await api('/i/export-clips', {}, alice);
|
res = await api('i/export-clips', {}, alice);
|
||||||
assert.strictEqual(res.status, 204);
|
assert.strictEqual(res.status, 204);
|
||||||
|
|
||||||
const exported = await pollFirstDriveFile();
|
const exported = await pollFirstDriveFile();
|
||||||
|
@ -116,14 +116,14 @@ describe('export-clips', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('multiple clips', async () => {
|
test('multiple clips', async () => {
|
||||||
let res = await api('/clips/create', {
|
let res = await api('clips/create', {
|
||||||
name: 'kawaii',
|
name: 'kawaii',
|
||||||
description: 'kawaii',
|
description: 'kawaii',
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
const clip1 = res.body;
|
const clip1 = res.body;
|
||||||
|
|
||||||
res = await api('/clips/create', {
|
res = await api('clips/create', {
|
||||||
name: 'yuri',
|
name: 'yuri',
|
||||||
description: 'yuri',
|
description: 'yuri',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -138,19 +138,19 @@ describe('export-clips', () => {
|
||||||
text: 'baz2',
|
text: 'baz2',
|
||||||
});
|
});
|
||||||
|
|
||||||
res = await api('/clips/add-note', {
|
res = await api('clips/add-note', {
|
||||||
clipId: clip1.id,
|
clipId: clip1.id,
|
||||||
noteId: note1.id,
|
noteId: note1.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(res.status, 204);
|
assert.strictEqual(res.status, 204);
|
||||||
|
|
||||||
res = await api('/clips/add-note', {
|
res = await api('clips/add-note', {
|
||||||
clipId: clip2.id,
|
clipId: clip2.id,
|
||||||
noteId: note2.id,
|
noteId: note2.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(res.status, 204);
|
assert.strictEqual(res.status, 204);
|
||||||
|
|
||||||
res = await api('/i/export-clips', {}, alice);
|
res = await api('i/export-clips', {}, alice);
|
||||||
assert.strictEqual(res.status, 204);
|
assert.strictEqual(res.status, 204);
|
||||||
|
|
||||||
const exported = await pollFirstDriveFile();
|
const exported = await pollFirstDriveFile();
|
||||||
|
@ -163,7 +163,7 @@ describe('export-clips', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Clipping other user\'s note', async () => {
|
test('Clipping other user\'s note', async () => {
|
||||||
let res = await api('/clips/create', {
|
let res = await api('clips/create', {
|
||||||
name: 'kawaii',
|
name: 'kawaii',
|
||||||
description: 'kawaii',
|
description: 'kawaii',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -175,13 +175,13 @@ describe('export-clips', () => {
|
||||||
visibility: 'followers',
|
visibility: 'followers',
|
||||||
});
|
});
|
||||||
|
|
||||||
res = await api('/clips/add-note', {
|
res = await api('clips/add-note', {
|
||||||
clipId: clip.id,
|
clipId: clip.id,
|
||||||
noteId: note.id,
|
noteId: note.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(res.status, 204);
|
assert.strictEqual(res.status, 204);
|
||||||
|
|
||||||
res = await api('/i/export-clips', {}, alice);
|
res = await api('i/export-clips', {}, alice);
|
||||||
assert.strictEqual(res.status, 204);
|
assert.strictEqual(res.status, 204);
|
||||||
|
|
||||||
const exported = await pollFirstDriveFile();
|
const exported = await pollFirstDriveFile();
|
||||||
|
|
|
@ -23,13 +23,13 @@ const JSON_UTF8 = 'application/json; charset=utf-8';
|
||||||
|
|
||||||
describe('Webリソース', () => {
|
describe('Webリソース', () => {
|
||||||
let alice: misskey.entities.SignupResponse;
|
let alice: misskey.entities.SignupResponse;
|
||||||
let aliceUploadedFile: any;
|
let aliceUploadedFile: misskey.entities.DriveFile | null;
|
||||||
let alicesPost: any;
|
let alicesPost: misskey.entities.Note;
|
||||||
let alicePage: any;
|
let alicePage: misskey.entities.Page;
|
||||||
let alicePlay: any;
|
let alicePlay: misskey.entities.Flash;
|
||||||
let aliceClip: any;
|
let aliceClip: misskey.entities.Clip;
|
||||||
let aliceGalleryPost: any;
|
let aliceGalleryPost: misskey.entities.GalleryPost;
|
||||||
let aliceChannel: any;
|
let aliceChannel: misskey.entities.Channel;
|
||||||
|
|
||||||
let bob: misskey.entities.SignupResponse;
|
let bob: misskey.entities.SignupResponse;
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ describe('Webリソース', () => {
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
alice = await signup({ username: 'alice' });
|
alice = await signup({ username: 'alice' });
|
||||||
aliceUploadedFile = await uploadFile(alice);
|
aliceUploadedFile = (await uploadFile(alice)).body;
|
||||||
alicesPost = await post(alice, {
|
alicesPost = await post(alice, {
|
||||||
text: 'test',
|
text: 'test',
|
||||||
});
|
});
|
||||||
|
@ -85,7 +85,7 @@ describe('Webリソース', () => {
|
||||||
alicePlay = await play(alice, {});
|
alicePlay = await play(alice, {});
|
||||||
aliceClip = await clip(alice, {});
|
aliceClip = await clip(alice, {});
|
||||||
aliceGalleryPost = await galleryPost(alice, {
|
aliceGalleryPost = await galleryPost(alice, {
|
||||||
fileIds: [aliceUploadedFile.body.id],
|
fileIds: [aliceUploadedFile!.id],
|
||||||
});
|
});
|
||||||
aliceChannel = await channel(alice, {});
|
aliceChannel = await channel(alice, {});
|
||||||
|
|
||||||
|
|
|
@ -19,15 +19,15 @@ describe('FF visibility', () => {
|
||||||
}, 1000 * 60 * 2);
|
}, 1000 * 60 * 2);
|
||||||
|
|
||||||
test('followingVisibility, followersVisibility がともに public なユーザーのフォロー/フォロワーを誰でも見れる', async () => {
|
test('followingVisibility, followersVisibility がともに public なユーザーのフォロー/フォロワーを誰でも見れる', async () => {
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'public',
|
followingVisibility: 'public',
|
||||||
followersVisibility: 'public',
|
followersVisibility: 'public',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -39,36 +39,36 @@ describe('FF visibility', () => {
|
||||||
|
|
||||||
test('followingVisibility が public であれば followersVisibility の設定に関わらずユーザーのフォローを誰でも見れる', async () => {
|
test('followingVisibility が public であれば followersVisibility の設定に関わらずユーザーのフォローを誰でも見れる', async () => {
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'public',
|
followingVisibility: 'public',
|
||||||
followersVisibility: 'public',
|
followersVisibility: 'public',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followingRes.status, 200);
|
assert.strictEqual(followingRes.status, 200);
|
||||||
assert.strictEqual(Array.isArray(followingRes.body), true);
|
assert.strictEqual(Array.isArray(followingRes.body), true);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'public',
|
followingVisibility: 'public',
|
||||||
followersVisibility: 'followers',
|
followersVisibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followingRes.status, 200);
|
assert.strictEqual(followingRes.status, 200);
|
||||||
assert.strictEqual(Array.isArray(followingRes.body), true);
|
assert.strictEqual(Array.isArray(followingRes.body), true);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'public',
|
followingVisibility: 'public',
|
||||||
followersVisibility: 'private',
|
followersVisibility: 'private',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followingRes.status, 200);
|
assert.strictEqual(followingRes.status, 200);
|
||||||
|
@ -78,36 +78,36 @@ describe('FF visibility', () => {
|
||||||
|
|
||||||
test('followersVisibility が public であれば followingVisibility の設定に関わらずユーザーのフォロワーを誰でも見れる', async () => {
|
test('followersVisibility が public であれば followingVisibility の設定に関わらずユーザーのフォロワーを誰でも見れる', async () => {
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'public',
|
followingVisibility: 'public',
|
||||||
followersVisibility: 'public',
|
followersVisibility: 'public',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followersRes.status, 200);
|
assert.strictEqual(followersRes.status, 200);
|
||||||
assert.strictEqual(Array.isArray(followersRes.body), true);
|
assert.strictEqual(Array.isArray(followersRes.body), true);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'followers',
|
followingVisibility: 'followers',
|
||||||
followersVisibility: 'public',
|
followersVisibility: 'public',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followersRes.status, 200);
|
assert.strictEqual(followersRes.status, 200);
|
||||||
assert.strictEqual(Array.isArray(followersRes.body), true);
|
assert.strictEqual(Array.isArray(followersRes.body), true);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'private',
|
followingVisibility: 'private',
|
||||||
followersVisibility: 'public',
|
followersVisibility: 'public',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followersRes.status, 200);
|
assert.strictEqual(followersRes.status, 200);
|
||||||
|
@ -116,15 +116,15 @@ describe('FF visibility', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('followingVisibility, followersVisibility がともに followers なユーザーのフォロー/フォロワーを自分で見れる', async () => {
|
test('followingVisibility, followersVisibility がともに followers なユーザーのフォロー/フォロワーを自分で見れる', async () => {
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'followers',
|
followingVisibility: 'followers',
|
||||||
followersVisibility: 'followers',
|
followersVisibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -136,36 +136,36 @@ describe('FF visibility', () => {
|
||||||
|
|
||||||
test('followingVisibility が followers なユーザーのフォローを followersVisibility の設定に関わらず自分で見れる', async () => {
|
test('followingVisibility が followers なユーザーのフォローを followersVisibility の設定に関わらず自分で見れる', async () => {
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'followers',
|
followingVisibility: 'followers',
|
||||||
followersVisibility: 'public',
|
followersVisibility: 'public',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(followingRes.status, 200);
|
assert.strictEqual(followingRes.status, 200);
|
||||||
assert.strictEqual(Array.isArray(followingRes.body), true);
|
assert.strictEqual(Array.isArray(followingRes.body), true);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'followers',
|
followingVisibility: 'followers',
|
||||||
followersVisibility: 'followers',
|
followersVisibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(followingRes.status, 200);
|
assert.strictEqual(followingRes.status, 200);
|
||||||
assert.strictEqual(Array.isArray(followingRes.body), true);
|
assert.strictEqual(Array.isArray(followingRes.body), true);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'followers',
|
followingVisibility: 'followers',
|
||||||
followersVisibility: 'private',
|
followersVisibility: 'private',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(followingRes.status, 200);
|
assert.strictEqual(followingRes.status, 200);
|
||||||
|
@ -175,36 +175,36 @@ describe('FF visibility', () => {
|
||||||
|
|
||||||
test('followersVisibility が followers なユーザーのフォロワーを followingVisibility の設定に関わらず自分で見れる', async () => {
|
test('followersVisibility が followers なユーザーのフォロワーを followingVisibility の設定に関わらず自分で見れる', async () => {
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'public',
|
followingVisibility: 'public',
|
||||||
followersVisibility: 'followers',
|
followersVisibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(followersRes.status, 200);
|
assert.strictEqual(followersRes.status, 200);
|
||||||
assert.strictEqual(Array.isArray(followersRes.body), true);
|
assert.strictEqual(Array.isArray(followersRes.body), true);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'followers',
|
followingVisibility: 'followers',
|
||||||
followersVisibility: 'followers',
|
followersVisibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(followersRes.status, 200);
|
assert.strictEqual(followersRes.status, 200);
|
||||||
assert.strictEqual(Array.isArray(followersRes.body), true);
|
assert.strictEqual(Array.isArray(followersRes.body), true);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'private',
|
followingVisibility: 'private',
|
||||||
followersVisibility: 'followers',
|
followersVisibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(followersRes.status, 200);
|
assert.strictEqual(followersRes.status, 200);
|
||||||
|
@ -213,15 +213,15 @@ describe('FF visibility', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('followingVisibility, followersVisibility がともに followers なユーザーのフォロー/フォロワーを非フォロワーが見れない', async () => {
|
test('followingVisibility, followersVisibility がともに followers なユーザーのフォロー/フォロワーを非フォロワーが見れない', async () => {
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'followers',
|
followingVisibility: 'followers',
|
||||||
followersVisibility: 'followers',
|
followersVisibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -231,34 +231,34 @@ describe('FF visibility', () => {
|
||||||
|
|
||||||
test('followingVisibility が followers なユーザーのフォローを followersVisibility の設定に関わらず非フォロワーが見れない', async () => {
|
test('followingVisibility が followers なユーザーのフォローを followersVisibility の設定に関わらず非フォロワーが見れない', async () => {
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'followers',
|
followingVisibility: 'followers',
|
||||||
followersVisibility: 'public',
|
followersVisibility: 'public',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followingRes.status, 400);
|
assert.strictEqual(followingRes.status, 400);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'followers',
|
followingVisibility: 'followers',
|
||||||
followersVisibility: 'followers',
|
followersVisibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followingRes.status, 400);
|
assert.strictEqual(followingRes.status, 400);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'followers',
|
followingVisibility: 'followers',
|
||||||
followersVisibility: 'private',
|
followersVisibility: 'private',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followingRes.status, 400);
|
assert.strictEqual(followingRes.status, 400);
|
||||||
|
@ -267,34 +267,34 @@ describe('FF visibility', () => {
|
||||||
|
|
||||||
test('followersVisibility が followers なユーザーのフォロワーを followingVisibility の設定に関わらず非フォロワーが見れない', async () => {
|
test('followersVisibility が followers なユーザーのフォロワーを followingVisibility の設定に関わらず非フォロワーが見れない', async () => {
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'public',
|
followingVisibility: 'public',
|
||||||
followersVisibility: 'followers',
|
followersVisibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followersRes.status, 400);
|
assert.strictEqual(followersRes.status, 400);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'followers',
|
followingVisibility: 'followers',
|
||||||
followersVisibility: 'followers',
|
followersVisibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followersRes.status, 400);
|
assert.strictEqual(followersRes.status, 400);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'private',
|
followingVisibility: 'private',
|
||||||
followersVisibility: 'followers',
|
followersVisibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followersRes.status, 400);
|
assert.strictEqual(followersRes.status, 400);
|
||||||
|
@ -302,19 +302,19 @@ describe('FF visibility', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('followingVisibility, followersVisibility がともに followers なユーザーのフォロー/フォロワーをフォロワーが見れる', async () => {
|
test('followingVisibility, followersVisibility がともに followers なユーザーのフォロー/フォロワーをフォロワーが見れる', async () => {
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'followers',
|
followingVisibility: 'followers',
|
||||||
followersVisibility: 'followers',
|
followersVisibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
await api('/following/create', {
|
await api('following/create', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -326,45 +326,45 @@ describe('FF visibility', () => {
|
||||||
|
|
||||||
test('followingVisibility が followers なユーザーのフォローを followersVisibility の設定に関わらずフォロワーが見れる', async () => {
|
test('followingVisibility が followers なユーザーのフォローを followersVisibility の設定に関わらずフォロワーが見れる', async () => {
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'followers',
|
followingVisibility: 'followers',
|
||||||
followersVisibility: 'public',
|
followersVisibility: 'public',
|
||||||
}, alice);
|
}, alice);
|
||||||
await api('/following/create', {
|
await api('following/create', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followingRes.status, 200);
|
assert.strictEqual(followingRes.status, 200);
|
||||||
assert.strictEqual(Array.isArray(followingRes.body), true);
|
assert.strictEqual(Array.isArray(followingRes.body), true);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'followers',
|
followingVisibility: 'followers',
|
||||||
followersVisibility: 'followers',
|
followersVisibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
await api('/following/create', {
|
await api('following/create', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followingRes.status, 200);
|
assert.strictEqual(followingRes.status, 200);
|
||||||
assert.strictEqual(Array.isArray(followingRes.body), true);
|
assert.strictEqual(Array.isArray(followingRes.body), true);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'followers',
|
followingVisibility: 'followers',
|
||||||
followersVisibility: 'private',
|
followersVisibility: 'private',
|
||||||
}, alice);
|
}, alice);
|
||||||
await api('/following/create', {
|
await api('following/create', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followingRes.status, 200);
|
assert.strictEqual(followingRes.status, 200);
|
||||||
|
@ -374,45 +374,45 @@ describe('FF visibility', () => {
|
||||||
|
|
||||||
test('followersVisibility が followers なユーザーのフォロワーを followingVisibility の設定に関わらずフォロワーが見れる', async () => {
|
test('followersVisibility が followers なユーザーのフォロワーを followingVisibility の設定に関わらずフォロワーが見れる', async () => {
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'public',
|
followingVisibility: 'public',
|
||||||
followersVisibility: 'followers',
|
followersVisibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
await api('/following/create', {
|
await api('following/create', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followersRes.status, 200);
|
assert.strictEqual(followersRes.status, 200);
|
||||||
assert.strictEqual(Array.isArray(followersRes.body), true);
|
assert.strictEqual(Array.isArray(followersRes.body), true);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'followers',
|
followingVisibility: 'followers',
|
||||||
followersVisibility: 'followers',
|
followersVisibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
await api('/following/create', {
|
await api('following/create', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followersRes.status, 200);
|
assert.strictEqual(followersRes.status, 200);
|
||||||
assert.strictEqual(Array.isArray(followersRes.body), true);
|
assert.strictEqual(Array.isArray(followersRes.body), true);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'private',
|
followingVisibility: 'private',
|
||||||
followersVisibility: 'followers',
|
followersVisibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
await api('/following/create', {
|
await api('following/create', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followersRes.status, 200);
|
assert.strictEqual(followersRes.status, 200);
|
||||||
|
@ -421,15 +421,15 @@ describe('FF visibility', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('followingVisibility, followersVisibility がともに private なユーザーのフォロー/フォロワーを自分で見れる', async () => {
|
test('followingVisibility, followersVisibility がともに private なユーザーのフォロー/フォロワーを自分で見れる', async () => {
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'private',
|
followingVisibility: 'private',
|
||||||
followersVisibility: 'private',
|
followersVisibility: 'private',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -441,36 +441,36 @@ describe('FF visibility', () => {
|
||||||
|
|
||||||
test('followingVisibility が private なユーザーのフォローを followersVisibility の設定に関わらず自分で見れる', async () => {
|
test('followingVisibility が private なユーザーのフォローを followersVisibility の設定に関わらず自分で見れる', async () => {
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'private',
|
followingVisibility: 'private',
|
||||||
followersVisibility: 'public',
|
followersVisibility: 'public',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(followingRes.status, 200);
|
assert.strictEqual(followingRes.status, 200);
|
||||||
assert.strictEqual(Array.isArray(followingRes.body), true);
|
assert.strictEqual(Array.isArray(followingRes.body), true);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'private',
|
followingVisibility: 'private',
|
||||||
followersVisibility: 'followers',
|
followersVisibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(followingRes.status, 200);
|
assert.strictEqual(followingRes.status, 200);
|
||||||
assert.strictEqual(Array.isArray(followingRes.body), true);
|
assert.strictEqual(Array.isArray(followingRes.body), true);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'private',
|
followingVisibility: 'private',
|
||||||
followersVisibility: 'private',
|
followersVisibility: 'private',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(followingRes.status, 200);
|
assert.strictEqual(followingRes.status, 200);
|
||||||
|
@ -480,36 +480,36 @@ describe('FF visibility', () => {
|
||||||
|
|
||||||
test('followersVisibility が private なユーザーのフォロワーを followingVisibility の設定に関わらず自分で見れる', async () => {
|
test('followersVisibility が private なユーザーのフォロワーを followingVisibility の設定に関わらず自分で見れる', async () => {
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'public',
|
followingVisibility: 'public',
|
||||||
followersVisibility: 'private',
|
followersVisibility: 'private',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(followersRes.status, 200);
|
assert.strictEqual(followersRes.status, 200);
|
||||||
assert.strictEqual(Array.isArray(followersRes.body), true);
|
assert.strictEqual(Array.isArray(followersRes.body), true);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'followers',
|
followingVisibility: 'followers',
|
||||||
followersVisibility: 'private',
|
followersVisibility: 'private',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(followersRes.status, 200);
|
assert.strictEqual(followersRes.status, 200);
|
||||||
assert.strictEqual(Array.isArray(followersRes.body), true);
|
assert.strictEqual(Array.isArray(followersRes.body), true);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'private',
|
followingVisibility: 'private',
|
||||||
followersVisibility: 'private',
|
followersVisibility: 'private',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(followersRes.status, 200);
|
assert.strictEqual(followersRes.status, 200);
|
||||||
|
@ -518,15 +518,15 @@ describe('FF visibility', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('followingVisibility, followersVisibility がともに private なユーザーのフォロー/フォロワーを他人が見れない', async () => {
|
test('followingVisibility, followersVisibility がともに private なユーザーのフォロー/フォロワーを他人が見れない', async () => {
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'private',
|
followingVisibility: 'private',
|
||||||
followersVisibility: 'private',
|
followersVisibility: 'private',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -536,34 +536,34 @@ describe('FF visibility', () => {
|
||||||
|
|
||||||
test('followingVisibility が private なユーザーのフォローを followersVisibility の設定に関わらず他人が見れない', async () => {
|
test('followingVisibility が private なユーザーのフォローを followersVisibility の設定に関わらず他人が見れない', async () => {
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'private',
|
followingVisibility: 'private',
|
||||||
followersVisibility: 'public',
|
followersVisibility: 'public',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followingRes.status, 400);
|
assert.strictEqual(followingRes.status, 400);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'private',
|
followingVisibility: 'private',
|
||||||
followersVisibility: 'followers',
|
followersVisibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followingRes.status, 400);
|
assert.strictEqual(followingRes.status, 400);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'private',
|
followingVisibility: 'private',
|
||||||
followersVisibility: 'private',
|
followersVisibility: 'private',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followingRes = await api('/users/following', {
|
const followingRes = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followingRes.status, 400);
|
assert.strictEqual(followingRes.status, 400);
|
||||||
|
@ -572,34 +572,34 @@ describe('FF visibility', () => {
|
||||||
|
|
||||||
test('followersVisibility が private なユーザーのフォロワーを followingVisibility の設定に関わらず他人が見れない', async () => {
|
test('followersVisibility が private なユーザーのフォロワーを followingVisibility の設定に関わらず他人が見れない', async () => {
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'public',
|
followingVisibility: 'public',
|
||||||
followersVisibility: 'private',
|
followersVisibility: 'private',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followersRes.status, 400);
|
assert.strictEqual(followersRes.status, 400);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'followers',
|
followingVisibility: 'followers',
|
||||||
followersVisibility: 'private',
|
followersVisibility: 'private',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followersRes.status, 400);
|
assert.strictEqual(followersRes.status, 400);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'private',
|
followingVisibility: 'private',
|
||||||
followersVisibility: 'private',
|
followersVisibility: 'private',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const followersRes = await api('/users/followers', {
|
const followersRes = await api('users/followers', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
assert.strictEqual(followersRes.status, 400);
|
assert.strictEqual(followersRes.status, 400);
|
||||||
|
@ -609,7 +609,7 @@ describe('FF visibility', () => {
|
||||||
describe('AP', () => {
|
describe('AP', () => {
|
||||||
test('followingVisibility が public 以外ならばAPからはフォローを取得できない', async () => {
|
test('followingVisibility が public 以外ならばAPからはフォローを取得できない', async () => {
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'public',
|
followingVisibility: 'public',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -617,7 +617,7 @@ describe('FF visibility', () => {
|
||||||
assert.strictEqual(followingRes.status, 200);
|
assert.strictEqual(followingRes.status, 200);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'followers',
|
followingVisibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -625,7 +625,7 @@ describe('FF visibility', () => {
|
||||||
assert.strictEqual(followingRes.status, 403);
|
assert.strictEqual(followingRes.status, 403);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followingVisibility: 'private',
|
followingVisibility: 'private',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -636,7 +636,7 @@ describe('FF visibility', () => {
|
||||||
|
|
||||||
test('followersVisibility が public 以外ならばAPからはフォロワーを取得できない', async () => {
|
test('followersVisibility が public 以外ならばAPからはフォロワーを取得できない', async () => {
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followersVisibility: 'public',
|
followersVisibility: 'public',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -644,7 +644,7 @@ describe('FF visibility', () => {
|
||||||
assert.strictEqual(followersRes.status, 200);
|
assert.strictEqual(followersRes.status, 200);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followersVisibility: 'followers',
|
followersVisibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -652,7 +652,7 @@ describe('FF visibility', () => {
|
||||||
assert.strictEqual(followersRes.status, 403);
|
assert.strictEqual(followersRes.status, 403);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
followersVisibility: 'private',
|
followersVisibility: 'private',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ describe('Account Move', () => {
|
||||||
}, 1000 * 10);
|
}, 1000 * 10);
|
||||||
|
|
||||||
test('Able to create an alias', async () => {
|
test('Able to create an alias', async () => {
|
||||||
const res = await api('/i/update', {
|
const res = await api('i/update', {
|
||||||
alsoKnownAs: [`@alice@${url.hostname}`],
|
alsoKnownAs: [`@alice@${url.hostname}`],
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ describe('Account Move', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Able to create a local alias without hostname', async () => {
|
test('Able to create a local alias without hostname', async () => {
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
alsoKnownAs: ['@alice'],
|
alsoKnownAs: ['@alice'],
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ describe('Account Move', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Able to create a local alias without @', async () => {
|
test('Able to create a local alias without @', async () => {
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
alsoKnownAs: ['alice'],
|
alsoKnownAs: ['alice'],
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ describe('Account Move', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Able to set remote user (but may fail)', async () => {
|
test('Able to set remote user (but may fail)', async () => {
|
||||||
const res = await api('/i/update', {
|
const res = await api('i/update', {
|
||||||
alsoKnownAs: ['@syuilo@example.com'],
|
alsoKnownAs: ['@syuilo@example.com'],
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ describe('Account Move', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Unable to add duplicated aliases to alsoKnownAs', async () => {
|
test('Unable to add duplicated aliases to alsoKnownAs', async () => {
|
||||||
const res = await api('/i/update', {
|
const res = await api('i/update', {
|
||||||
alsoKnownAs: [`@alice@${url.hostname}`, `@alice@${url.hostname}`],
|
alsoKnownAs: [`@alice@${url.hostname}`, `@alice@${url.hostname}`],
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ describe('Account Move', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Unable to add itself', async () => {
|
test('Unable to add itself', async () => {
|
||||||
const res = await api('/i/update', {
|
const res = await api('i/update', {
|
||||||
alsoKnownAs: [`@bob@${url.hostname}`],
|
alsoKnownAs: [`@bob@${url.hostname}`],
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ describe('Account Move', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Unable to add a nonexisting local account to alsoKnownAs', async () => {
|
test('Unable to add a nonexisting local account to alsoKnownAs', async () => {
|
||||||
const res1 = await api('/i/update', {
|
const res1 = await api('i/update', {
|
||||||
alsoKnownAs: [`@nonexist@${url.hostname}`],
|
alsoKnownAs: [`@nonexist@${url.hostname}`],
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ describe('Account Move', () => {
|
||||||
assert.strictEqual(res1.body.error.code, 'NO_SUCH_USER');
|
assert.strictEqual(res1.body.error.code, 'NO_SUCH_USER');
|
||||||
assert.strictEqual(res1.body.error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
|
assert.strictEqual(res1.body.error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
|
||||||
|
|
||||||
const res2 = await api('/i/update', {
|
const res2 = await api('i/update', {
|
||||||
alsoKnownAs: ['@alice', 'nonexist'],
|
alsoKnownAs: ['@alice', 'nonexist'],
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ describe('Account Move', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Able to add two existing local account to alsoKnownAs', async () => {
|
test('Able to add two existing local account to alsoKnownAs', async () => {
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
alsoKnownAs: [`@alice@${url.hostname}`, `@carol@${url.hostname}`],
|
alsoKnownAs: [`@alice@${url.hostname}`, `@carol@${url.hostname}`],
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -146,10 +146,10 @@ describe('Account Move', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Able to properly overwrite alsoKnownAs', async () => {
|
test('Able to properly overwrite alsoKnownAs', async () => {
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
alsoKnownAs: [`@alice@${url.hostname}`],
|
alsoKnownAs: [`@alice@${url.hostname}`],
|
||||||
}, bob);
|
}, bob);
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
alsoKnownAs: [`@carol@${url.hostname}`, `@dave@${url.hostname}`],
|
alsoKnownAs: [`@carol@${url.hostname}`, `@dave@${url.hostname}`],
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -164,27 +164,27 @@ describe('Account Move', () => {
|
||||||
let antennaId = '';
|
let antennaId = '';
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
alsoKnownAs: [`@alice@${url.hostname}`],
|
alsoKnownAs: [`@alice@${url.hostname}`],
|
||||||
}, root);
|
}, root);
|
||||||
const listRoot = await api('/users/lists/create', {
|
const listRoot = await api('users/lists/create', {
|
||||||
name: secureRndstr(8),
|
name: secureRndstr(8),
|
||||||
}, root);
|
}, root);
|
||||||
await api('/users/lists/push', {
|
await api('users/lists/push', {
|
||||||
listId: listRoot.body.id,
|
listId: listRoot.body.id,
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, root);
|
}, root);
|
||||||
|
|
||||||
await api('/following/create', {
|
await api('following/create', {
|
||||||
userId: root.id,
|
userId: root.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
await api('/following/create', {
|
await api('following/create', {
|
||||||
userId: eve.id,
|
userId: eve.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
const antenna = await api('/antennas/create', {
|
const antenna = await api('antennas/create', {
|
||||||
name: secureRndstr(8),
|
name: secureRndstr(8),
|
||||||
src: 'home',
|
src: 'home',
|
||||||
keywords: [secureRndstr(8)],
|
keywords: [[secureRndstr(8)]],
|
||||||
excludeKeywords: [],
|
excludeKeywords: [],
|
||||||
users: [],
|
users: [],
|
||||||
caseSensitive: false,
|
caseSensitive: false,
|
||||||
|
@ -195,48 +195,48 @@ describe('Account Move', () => {
|
||||||
}, alice);
|
}, alice);
|
||||||
antennaId = antenna.body.id;
|
antennaId = antenna.body.id;
|
||||||
|
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
alsoKnownAs: [`@alice@${url.hostname}`],
|
alsoKnownAs: [`@alice@${url.hostname}`],
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
await api('/following/create', {
|
await api('following/create', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, carol);
|
}, carol);
|
||||||
|
|
||||||
await api('/mute/create', {
|
await api('mute/create', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, dave);
|
}, dave);
|
||||||
await api('/blocking/create', {
|
await api('blocking/create', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, dave);
|
}, dave);
|
||||||
await api('/following/create', {
|
await api('following/create', {
|
||||||
userId: eve.id,
|
userId: eve.id,
|
||||||
}, dave);
|
}, dave);
|
||||||
|
|
||||||
await api('/following/create', {
|
await api('following/create', {
|
||||||
userId: dave.id,
|
userId: dave.id,
|
||||||
}, eve);
|
}, eve);
|
||||||
const listEve = await api('/users/lists/create', {
|
const listEve = await api('users/lists/create', {
|
||||||
name: secureRndstr(8),
|
name: secureRndstr(8),
|
||||||
}, eve);
|
}, eve);
|
||||||
await api('/users/lists/push', {
|
await api('users/lists/push', {
|
||||||
listId: listEve.body.id,
|
listId: listEve.body.id,
|
||||||
userId: bob.id,
|
userId: bob.id,
|
||||||
}, eve);
|
}, eve);
|
||||||
|
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
isLocked: true,
|
isLocked: true,
|
||||||
}, frank);
|
}, frank);
|
||||||
await api('/following/create', {
|
await api('following/create', {
|
||||||
userId: frank.id,
|
userId: frank.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
await api('/following/requests/accept', {
|
await api('following/requests/accept', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, frank);
|
}, frank);
|
||||||
}, 1000 * 10);
|
}, 1000 * 10);
|
||||||
|
|
||||||
test('Prohibit the root account from moving', async () => {
|
test('Prohibit the root account from moving', async () => {
|
||||||
const res = await api('/i/move', {
|
const res = await api('i/move', {
|
||||||
moveToAccount: `@bob@${url.hostname}`,
|
moveToAccount: `@bob@${url.hostname}`,
|
||||||
}, root);
|
}, root);
|
||||||
|
|
||||||
|
@ -246,7 +246,7 @@ describe('Account Move', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Unable to move to a nonexisting local account', async () => {
|
test('Unable to move to a nonexisting local account', async () => {
|
||||||
const res = await api('/i/move', {
|
const res = await api('i/move', {
|
||||||
moveToAccount: `@nonexist@${url.hostname}`,
|
moveToAccount: `@nonexist@${url.hostname}`,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -256,7 +256,7 @@ describe('Account Move', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Unable to move if alsoKnownAs is invalid', async () => {
|
test('Unable to move if alsoKnownAs is invalid', async () => {
|
||||||
const res = await api('/i/move', {
|
const res = await api('i/move', {
|
||||||
moveToAccount: `@carol@${url.hostname}`,
|
moveToAccount: `@carol@${url.hostname}`,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -266,7 +266,7 @@ describe('Account Move', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Relationships have been properly migrated', async () => {
|
test('Relationships have been properly migrated', async () => {
|
||||||
const move = await api('/i/move', {
|
const move = await api('i/move', {
|
||||||
moveToAccount: `@bob@${url.hostname}`,
|
moveToAccount: `@bob@${url.hostname}`,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -275,13 +275,13 @@ describe('Account Move', () => {
|
||||||
await sleep(1000 * 3); // wait for jobs to finish
|
await sleep(1000 * 3); // wait for jobs to finish
|
||||||
|
|
||||||
// Unfollow delayed?
|
// Unfollow delayed?
|
||||||
const aliceFollowings = await api('/users/following', {
|
const aliceFollowings = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(aliceFollowings.status, 200);
|
assert.strictEqual(aliceFollowings.status, 200);
|
||||||
assert.strictEqual(aliceFollowings.body.length, 3);
|
assert.strictEqual(aliceFollowings.body.length, 3);
|
||||||
|
|
||||||
const carolFollowings = await api('/users/following', {
|
const carolFollowings = await api('users/following', {
|
||||||
userId: carol.id,
|
userId: carol.id,
|
||||||
}, carol);
|
}, carol);
|
||||||
assert.strictEqual(carolFollowings.status, 200);
|
assert.strictEqual(carolFollowings.status, 200);
|
||||||
|
@ -289,25 +289,25 @@ describe('Account Move', () => {
|
||||||
assert.strictEqual(carolFollowings.body[0].followeeId, bob.id);
|
assert.strictEqual(carolFollowings.body[0].followeeId, bob.id);
|
||||||
assert.strictEqual(carolFollowings.body[1].followeeId, alice.id);
|
assert.strictEqual(carolFollowings.body[1].followeeId, alice.id);
|
||||||
|
|
||||||
const blockings = await api('/blocking/list', {}, dave);
|
const blockings = await api('blocking/list', {}, dave);
|
||||||
assert.strictEqual(blockings.status, 200);
|
assert.strictEqual(blockings.status, 200);
|
||||||
assert.strictEqual(blockings.body.length, 2);
|
assert.strictEqual(blockings.body.length, 2);
|
||||||
assert.strictEqual(blockings.body[0].blockeeId, bob.id);
|
assert.strictEqual(blockings.body[0].blockeeId, bob.id);
|
||||||
assert.strictEqual(blockings.body[1].blockeeId, alice.id);
|
assert.strictEqual(blockings.body[1].blockeeId, alice.id);
|
||||||
|
|
||||||
const mutings = await api('/mute/list', {}, dave);
|
const mutings = await api('mute/list', {}, dave);
|
||||||
assert.strictEqual(mutings.status, 200);
|
assert.strictEqual(mutings.status, 200);
|
||||||
assert.strictEqual(mutings.body.length, 2);
|
assert.strictEqual(mutings.body.length, 2);
|
||||||
assert.strictEqual(mutings.body[0].muteeId, bob.id);
|
assert.strictEqual(mutings.body[0].muteeId, bob.id);
|
||||||
assert.strictEqual(mutings.body[1].muteeId, alice.id);
|
assert.strictEqual(mutings.body[1].muteeId, alice.id);
|
||||||
|
|
||||||
const rootLists = await api('/users/lists/list', {}, root);
|
const rootLists = await api('users/lists/list', {}, root);
|
||||||
assert.strictEqual(rootLists.status, 200);
|
assert.strictEqual(rootLists.status, 200);
|
||||||
assert.strictEqual(rootLists.body[0].userIds.length, 2);
|
assert.strictEqual(rootLists.body[0].userIds.length, 2);
|
||||||
assert.ok(rootLists.body[0].userIds.find((id: string) => id === bob.id));
|
assert.ok(rootLists.body[0].userIds.find((id: string) => id === bob.id));
|
||||||
assert.ok(rootLists.body[0].userIds.find((id: string) => id === alice.id));
|
assert.ok(rootLists.body[0].userIds.find((id: string) => id === alice.id));
|
||||||
|
|
||||||
const eveLists = await api('/users/lists/list', {}, eve);
|
const eveLists = await api('users/lists/list', {}, eve);
|
||||||
assert.strictEqual(eveLists.status, 200);
|
assert.strictEqual(eveLists.status, 200);
|
||||||
assert.strictEqual(eveLists.body[0].userIds.length, 1);
|
assert.strictEqual(eveLists.body[0].userIds.length, 1);
|
||||||
assert.ok(eveLists.body[0].userIds.find((id: string) => id === bob.id));
|
assert.ok(eveLists.body[0].userIds.find((id: string) => id === bob.id));
|
||||||
|
@ -315,13 +315,13 @@ describe('Account Move', () => {
|
||||||
|
|
||||||
test('A locked account automatically accept the follow request if it had already accepted the old account.', async () => {
|
test('A locked account automatically accept the follow request if it had already accepted the old account.', async () => {
|
||||||
await successfulApiCall({
|
await successfulApiCall({
|
||||||
endpoint: '/following/create',
|
endpoint: 'following/create',
|
||||||
parameters: {
|
parameters: {
|
||||||
userId: frank.id,
|
userId: frank.id,
|
||||||
},
|
},
|
||||||
user: bob,
|
user: bob,
|
||||||
});
|
});
|
||||||
const followers = await api('/users/followers', {
|
const followers = await api('users/followers', {
|
||||||
userId: frank.id,
|
userId: frank.id,
|
||||||
}, frank);
|
}, frank);
|
||||||
|
|
||||||
|
@ -333,7 +333,7 @@ describe('Account Move', () => {
|
||||||
test('Unfollowed after 10 sec (24 hours in production).', async () => {
|
test('Unfollowed after 10 sec (24 hours in production).', async () => {
|
||||||
await sleep(1000 * 8);
|
await sleep(1000 * 8);
|
||||||
|
|
||||||
const following = await api('/users/following', {
|
const following = await api('users/following', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -342,7 +342,7 @@ describe('Account Move', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Unable to move if the destination account has already moved.', async () => {
|
test('Unable to move if the destination account has already moved.', async () => {
|
||||||
const res = await api('/i/move', {
|
const res = await api('i/move', {
|
||||||
moveToAccount: `@alice@${url.hostname}`,
|
moveToAccount: `@alice@${url.hostname}`,
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
|
@ -352,7 +352,7 @@ describe('Account Move', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Follow and follower counts are properly adjusted', async () => {
|
test('Follow and follower counts are properly adjusted', async () => {
|
||||||
await api('/following/create', {
|
await api('following/create', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, eve);
|
}, eve);
|
||||||
const newAlice = await Users.findOneByOrFail({ id: alice.id });
|
const newAlice = await Users.findOneByOrFail({ id: alice.id });
|
||||||
|
@ -365,7 +365,7 @@ describe('Account Move', () => {
|
||||||
assert.strictEqual(newEve.followingCount, 1);
|
assert.strictEqual(newEve.followingCount, 1);
|
||||||
assert.strictEqual(newEve.followersCount, 1);
|
assert.strictEqual(newEve.followersCount, 1);
|
||||||
|
|
||||||
await api('/following/delete', {
|
await api('following/delete', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, eve);
|
}, eve);
|
||||||
newEve = await Users.findOneByOrFail({ id: eve.id });
|
newEve = await Users.findOneByOrFail({ id: eve.id });
|
||||||
|
@ -374,49 +374,49 @@ describe('Account Move', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test.each([
|
test.each([
|
||||||
'/antennas/create',
|
'antennas/create',
|
||||||
'/channels/create',
|
'channels/create',
|
||||||
'/channels/favorite',
|
'channels/favorite',
|
||||||
'/channels/follow',
|
'channels/follow',
|
||||||
'/channels/unfavorite',
|
'channels/unfavorite',
|
||||||
'/channels/unfollow',
|
'channels/unfollow',
|
||||||
'/clips/add-note',
|
'clips/add-note',
|
||||||
'/clips/create',
|
'clips/create',
|
||||||
'/clips/favorite',
|
'clips/favorite',
|
||||||
'/clips/remove-note',
|
'clips/remove-note',
|
||||||
'/clips/unfavorite',
|
'clips/unfavorite',
|
||||||
'/clips/update',
|
'clips/update',
|
||||||
'/drive/files/upload-from-url',
|
'drive/files/upload-from-url',
|
||||||
'/flash/create',
|
'flash/create',
|
||||||
'/flash/like',
|
'flash/like',
|
||||||
'/flash/unlike',
|
'flash/unlike',
|
||||||
'/flash/update',
|
'flash/update',
|
||||||
'/following/create',
|
'following/create',
|
||||||
'/gallery/posts/create',
|
'gallery/posts/create',
|
||||||
'/gallery/posts/like',
|
'gallery/posts/like',
|
||||||
'/gallery/posts/unlike',
|
'gallery/posts/unlike',
|
||||||
'/gallery/posts/update',
|
'gallery/posts/update',
|
||||||
'/i/claim-achievement',
|
'i/claim-achievement',
|
||||||
'/i/move',
|
'i/move',
|
||||||
'/i/import-blocking',
|
'i/import-blocking',
|
||||||
'/i/import-following',
|
'i/import-following',
|
||||||
'/i/import-muting',
|
'i/import-muting',
|
||||||
'/i/import-user-lists',
|
'i/import-user-lists',
|
||||||
'/i/pin',
|
'i/pin',
|
||||||
'/mute/create',
|
'mute/create',
|
||||||
'/notes/create',
|
'notes/create',
|
||||||
'/notes/favorites/create',
|
'notes/favorites/create',
|
||||||
'/notes/polls/vote',
|
'notes/polls/vote',
|
||||||
'/notes/reactions/create',
|
'notes/reactions/create',
|
||||||
'/pages/create',
|
'pages/create',
|
||||||
'/pages/like',
|
'pages/like',
|
||||||
'/pages/unlike',
|
'pages/unlike',
|
||||||
'/pages/update',
|
'pages/update',
|
||||||
'/renote-mute/create',
|
'renote-mute/create',
|
||||||
'/users/lists/create',
|
'users/lists/create',
|
||||||
'/users/lists/pull',
|
'users/lists/pull',
|
||||||
'/users/lists/push',
|
'users/lists/push',
|
||||||
])('Prohibit access after moving: %s', async (endpoint) => {
|
] as const)('Prohibit access after moving: %s', async (endpoint) => {
|
||||||
const res = await api(endpoint, {}, alice);
|
const res = await api(endpoint, {}, alice);
|
||||||
assert.strictEqual(res.status, 403);
|
assert.strictEqual(res.status, 403);
|
||||||
assert.strictEqual(res.body.error.code, 'YOUR_ACCOUNT_MOVED');
|
assert.strictEqual(res.body.error.code, 'YOUR_ACCOUNT_MOVED');
|
||||||
|
@ -424,11 +424,11 @@ describe('Account Move', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Prohibit access after moving: /antennas/update', async () => {
|
test('Prohibit access after moving: /antennas/update', async () => {
|
||||||
const res = await api('/antennas/update', {
|
const res = await api('antennas/update', {
|
||||||
antennaId,
|
antennaId,
|
||||||
name: secureRndstr(8),
|
name: secureRndstr(8),
|
||||||
src: 'users',
|
src: 'users',
|
||||||
keywords: [secureRndstr(8)],
|
keywords: [[secureRndstr(8)]],
|
||||||
excludeKeywords: [],
|
excludeKeywords: [],
|
||||||
users: [eve.id],
|
users: [eve.id],
|
||||||
caseSensitive: false,
|
caseSensitive: false,
|
||||||
|
@ -447,12 +447,12 @@ describe('Account Move', () => {
|
||||||
const res = await uploadFile(alice);
|
const res = await uploadFile(alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 403);
|
assert.strictEqual(res.status, 403);
|
||||||
assert.strictEqual(res.body.error.code, 'YOUR_ACCOUNT_MOVED');
|
assert.strictEqual((res.body! as any as { error: misskey.api.APIError }).error.code, 'YOUR_ACCOUNT_MOVED');
|
||||||
assert.strictEqual(res.body.error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
|
assert.strictEqual((res.body! as any as { error: misskey.api.APIError }).error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Prohibit updating alsoKnownAs after moving', async () => {
|
test('Prohibit updating alsoKnownAs after moving', async () => {
|
||||||
const res = await api('/i/update', {
|
const res = await api('i/update', {
|
||||||
alsoKnownAs: [`@eve@${url.hostname}`],
|
alsoKnownAs: [`@eve@${url.hostname}`],
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
|
|
@ -19,21 +19,31 @@ describe('Mute', () => {
|
||||||
alice = await signup({ username: 'alice' });
|
alice = await signup({ username: 'alice' });
|
||||||
bob = await signup({ username: 'bob' });
|
bob = await signup({ username: 'bob' });
|
||||||
carol = await signup({ username: 'carol' });
|
carol = await signup({ username: 'carol' });
|
||||||
|
|
||||||
|
// Mute: alice ==> carol
|
||||||
|
await api('mute/create', {
|
||||||
|
userId: carol.id,
|
||||||
|
}, alice);
|
||||||
}, 1000 * 60 * 2);
|
}, 1000 * 60 * 2);
|
||||||
|
|
||||||
test('ミュート作成', async () => {
|
test('ミュート作成', async () => {
|
||||||
const res = await api('/mute/create', {
|
const res = await api('mute/create', {
|
||||||
userId: carol.id,
|
userId: bob.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 204);
|
assert.strictEqual(res.status, 204);
|
||||||
|
|
||||||
|
// 単体でも走らせられるように副作用消す
|
||||||
|
await api('mute/delete', {
|
||||||
|
userId: bob.id,
|
||||||
|
}, alice);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('「自分宛ての投稿」にミュートしているユーザーの投稿が含まれない', async () => {
|
test('「自分宛ての投稿」にミュートしているユーザーの投稿が含まれない', async () => {
|
||||||
const bobNote = await post(bob, { text: '@alice hi' });
|
const bobNote = await post(bob, { text: '@alice hi' });
|
||||||
const carolNote = await post(carol, { text: '@alice hi' });
|
const carolNote = await post(carol, { text: '@alice hi' });
|
||||||
|
|
||||||
const res = await api('/notes/mentions', {}, alice);
|
const res = await api('notes/mentions', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
@ -43,11 +53,11 @@ describe('Mute', () => {
|
||||||
|
|
||||||
test('ミュートしているユーザーからメンションされても、hasUnreadMentions が true にならない', async () => {
|
test('ミュートしているユーザーからメンションされても、hasUnreadMentions が true にならない', async () => {
|
||||||
// 状態リセット
|
// 状態リセット
|
||||||
await api('/i/read-all-unread-notes', {}, alice);
|
await api('i/read-all-unread-notes', {}, alice);
|
||||||
|
|
||||||
await post(carol, { text: '@alice hi' });
|
await post(carol, { text: '@alice hi' });
|
||||||
|
|
||||||
const res = await api('/i', {}, alice);
|
const res = await api('i', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(res.body.hasUnreadMentions, false);
|
assert.strictEqual(res.body.hasUnreadMentions, false);
|
||||||
|
@ -55,7 +65,7 @@ describe('Mute', () => {
|
||||||
|
|
||||||
test('ミュートしているユーザーからメンションされても、ストリームに unreadMention イベントが流れてこない', async () => {
|
test('ミュートしているユーザーからメンションされても、ストリームに unreadMention イベントが流れてこない', async () => {
|
||||||
// 状態リセット
|
// 状態リセット
|
||||||
await api('/i/read-all-unread-notes', {}, alice);
|
await api('i/read-all-unread-notes', {}, alice);
|
||||||
|
|
||||||
const fired = await waitFire(alice, 'main', () => post(carol, { text: '@alice hi' }), msg => msg.type === 'unreadMention');
|
const fired = await waitFire(alice, 'main', () => post(carol, { text: '@alice hi' }), msg => msg.type === 'unreadMention');
|
||||||
|
|
||||||
|
@ -64,8 +74,8 @@ describe('Mute', () => {
|
||||||
|
|
||||||
test('ミュートしているユーザーからメンションされても、ストリームに unreadNotification イベントが流れてこない', async () => {
|
test('ミュートしているユーザーからメンションされても、ストリームに unreadNotification イベントが流れてこない', async () => {
|
||||||
// 状態リセット
|
// 状態リセット
|
||||||
await api('/i/read-all-unread-notes', {}, alice);
|
await api('i/read-all-unread-notes', {}, alice);
|
||||||
await api('/notifications/mark-all-as-read', {}, alice);
|
await api('notifications/mark-all-as-read', {}, alice);
|
||||||
|
|
||||||
const fired = await waitFire(alice, 'main', () => post(carol, { text: '@alice hi' }), msg => msg.type === 'unreadNotification');
|
const fired = await waitFire(alice, 'main', () => post(carol, { text: '@alice hi' }), msg => msg.type === 'unreadNotification');
|
||||||
|
|
||||||
|
@ -78,7 +88,7 @@ describe('Mute', () => {
|
||||||
const bobNote = await post(bob, { text: 'hi' });
|
const bobNote = await post(bob, { text: 'hi' });
|
||||||
const carolNote = await post(carol, { text: 'hi' });
|
const carolNote = await post(carol, { text: 'hi' });
|
||||||
|
|
||||||
const res = await api('/notes/local-timeline', {}, alice);
|
const res = await api('notes/local-timeline', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
@ -94,7 +104,7 @@ describe('Mute', () => {
|
||||||
renoteId: carolNote.id,
|
renoteId: carolNote.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = await api('/notes/local-timeline', {}, alice);
|
const res = await api('notes/local-timeline', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
@ -110,19 +120,20 @@ describe('Mute', () => {
|
||||||
await react(bob, aliceNote, 'like');
|
await react(bob, aliceNote, 'like');
|
||||||
await react(carol, aliceNote, 'like');
|
await react(carol, aliceNote, 'like');
|
||||||
|
|
||||||
const res = await api('/i/notifications', {}, alice);
|
const res = await api('i/notifications', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
|
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
|
||||||
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
|
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('通知にミュートしているユーザーからのリプライが含まれない', async () => {
|
test('通知にミュートしているユーザーからのリプライが含まれない', async () => {
|
||||||
const aliceNote = await post(alice, { text: 'hi' });
|
const aliceNote = await post(alice, { text: 'hi' });
|
||||||
await post(bob, { text: '@alice hi', replyId: aliceNote.id });
|
await post(bob, { text: '@alice hi', replyId: aliceNote.id });
|
||||||
await post(carol, { text: '@alice hi', replyId: aliceNote.id });
|
await post(carol, { text: '@alice hi', replyId: aliceNote.id });
|
||||||
|
|
||||||
const res = await api('/i/notifications', {}, alice);
|
const res = await api('i/notifications', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
@ -136,7 +147,7 @@ describe('Mute', () => {
|
||||||
await post(bob, { text: '@alice hi' });
|
await post(bob, { text: '@alice hi' });
|
||||||
await post(carol, { text: '@alice hi' });
|
await post(carol, { text: '@alice hi' });
|
||||||
|
|
||||||
const res = await api('/i/notifications', {}, alice);
|
const res = await api('i/notifications', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
@ -150,7 +161,7 @@ describe('Mute', () => {
|
||||||
await post(bob, { text: 'hi', renoteId: aliceNote.id });
|
await post(bob, { text: 'hi', renoteId: aliceNote.id });
|
||||||
await post(carol, { text: 'hi', renoteId: aliceNote.id });
|
await post(carol, { text: 'hi', renoteId: aliceNote.id });
|
||||||
|
|
||||||
const res = await api('/i/notifications', {}, alice);
|
const res = await api('i/notifications', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
@ -164,7 +175,7 @@ describe('Mute', () => {
|
||||||
await post(bob, { renoteId: aliceNote.id });
|
await post(bob, { renoteId: aliceNote.id });
|
||||||
await post(carol, { renoteId: aliceNote.id });
|
await post(carol, { renoteId: aliceNote.id });
|
||||||
|
|
||||||
const res = await api('/i/notifications', {}, alice);
|
const res = await api('i/notifications', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
@ -174,30 +185,36 @@ describe('Mute', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('通知にミュートしているユーザーからのフォロー通知が含まれない', async () => {
|
test('通知にミュートしているユーザーからのフォロー通知が含まれない', async () => {
|
||||||
await api('/i/follow', { userId: alice.id }, bob);
|
await api('following/create', { userId: alice.id }, bob);
|
||||||
await api('/i/follow', { userId: alice.id }, carol);
|
await api('following/create', { userId: alice.id }, carol);
|
||||||
|
|
||||||
const res = await api('/i/notifications', {}, alice);
|
const res = await api('i/notifications', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
|
||||||
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
|
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
|
||||||
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
|
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
|
||||||
|
|
||||||
|
await api('following/delete', { userId: alice.id }, bob);
|
||||||
|
await api('following/delete', { userId: alice.id }, carol);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('通知にミュートしているユーザーからのフォローリクエストが含まれない', async () => {
|
test('通知にミュートしているユーザーからのフォローリクエストが含まれない', async () => {
|
||||||
await api('/i/update/', { isLocked: true }, alice);
|
await api('i/update', { isLocked: true }, alice);
|
||||||
await api('/following/create', { userId: alice.id }, bob);
|
await api('following/create', { userId: alice.id }, bob);
|
||||||
await api('/following/create', { userId: alice.id }, carol);
|
await api('following/create', { userId: alice.id }, carol);
|
||||||
|
|
||||||
const res = await api('/i/notifications', {}, alice);
|
const res = await api('i/notifications', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
|
||||||
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
|
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
|
||||||
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
|
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
|
||||||
|
|
||||||
|
await api('following/delete', { userId: alice.id }, bob);
|
||||||
|
await api('following/delete', { userId: alice.id }, carol);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -207,7 +224,7 @@ describe('Mute', () => {
|
||||||
await react(bob, aliceNote, 'like');
|
await react(bob, aliceNote, 'like');
|
||||||
await react(carol, aliceNote, 'like');
|
await react(carol, aliceNote, 'like');
|
||||||
|
|
||||||
const res = await api('/i/notifications-grouped', {}, alice);
|
const res = await api('i/notifications-grouped', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
@ -219,7 +236,7 @@ describe('Mute', () => {
|
||||||
await post(bob, { text: '@alice hi', replyId: aliceNote.id });
|
await post(bob, { text: '@alice hi', replyId: aliceNote.id });
|
||||||
await post(carol, { text: '@alice hi', replyId: aliceNote.id });
|
await post(carol, { text: '@alice hi', replyId: aliceNote.id });
|
||||||
|
|
||||||
const res = await api('/i/notifications-grouped', {}, alice);
|
const res = await api('i/notifications-grouped', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
@ -233,7 +250,7 @@ describe('Mute', () => {
|
||||||
await post(bob, { text: '@alice hi' });
|
await post(bob, { text: '@alice hi' });
|
||||||
await post(carol, { text: '@alice hi' });
|
await post(carol, { text: '@alice hi' });
|
||||||
|
|
||||||
const res = await api('/i/notifications-grouped', {}, alice);
|
const res = await api('i/notifications-grouped', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
@ -247,7 +264,7 @@ describe('Mute', () => {
|
||||||
await post(bob, { text: 'hi', renoteId: aliceNote.id });
|
await post(bob, { text: 'hi', renoteId: aliceNote.id });
|
||||||
await post(carol, { text: 'hi', renoteId: aliceNote.id });
|
await post(carol, { text: 'hi', renoteId: aliceNote.id });
|
||||||
|
|
||||||
const res = await api('/i/notifications-grouped', {}, alice);
|
const res = await api('i/notifications-grouped', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
@ -261,7 +278,7 @@ describe('Mute', () => {
|
||||||
await post(bob, { renoteId: aliceNote.id });
|
await post(bob, { renoteId: aliceNote.id });
|
||||||
await post(carol, { renoteId: aliceNote.id });
|
await post(carol, { renoteId: aliceNote.id });
|
||||||
|
|
||||||
const res = await api('/i/notifications-grouped', {}, alice);
|
const res = await api('i/notifications-grouped', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
@ -271,24 +288,27 @@ describe('Mute', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('通知にミュートしているユーザーからのフォロー通知が含まれない', async () => {
|
test('通知にミュートしているユーザーからのフォロー通知が含まれない', async () => {
|
||||||
await api('/i/follow', { userId: alice.id }, bob);
|
await api('following/create', { userId: alice.id }, bob);
|
||||||
await api('/i/follow', { userId: alice.id }, carol);
|
await api('following/create', { userId: alice.id }, carol);
|
||||||
|
|
||||||
const res = await api('/i/notifications-grouped', {}, alice);
|
const res = await api('i/notifications-grouped', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
|
||||||
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
|
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
|
||||||
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
|
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
|
||||||
|
|
||||||
|
await api('following/delete', { userId: alice.id }, bob);
|
||||||
|
await api('following/delete', { userId: alice.id }, carol);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('通知にミュートしているユーザーからのフォローリクエストが含まれない', async () => {
|
test('通知にミュートしているユーザーからのフォローリクエストが含まれない', async () => {
|
||||||
await api('/i/update/', { isLocked: true }, alice);
|
await api('i/update', { isLocked: true }, alice);
|
||||||
await api('/following/create', { userId: alice.id }, bob);
|
await api('following/create', { userId: alice.id }, bob);
|
||||||
await api('/following/create', { userId: alice.id }, carol);
|
await api('following/create', { userId: alice.id }, carol);
|
||||||
|
|
||||||
const res = await api('/i/notifications-grouped', {}, alice);
|
const res = await api('i/notifications-grouped', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
|
|
@ -31,7 +31,7 @@ describe('Note', () => {
|
||||||
text: 'test',
|
text: 'test',
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await api('/notes/create', post, alice);
|
const res = await api('notes/create', post, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||||
|
@ -41,7 +41,7 @@ describe('Note', () => {
|
||||||
test('ファイルを添付できる', async () => {
|
test('ファイルを添付できる', async () => {
|
||||||
const file = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg');
|
const file = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg');
|
||||||
|
|
||||||
const res = await api('/notes/create', {
|
const res = await api('notes/create', {
|
||||||
fileIds: [file.id],
|
fileIds: [file.id],
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ describe('Note', () => {
|
||||||
test('他人のファイルで怒られる', async () => {
|
test('他人のファイルで怒られる', async () => {
|
||||||
const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg');
|
const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg');
|
||||||
|
|
||||||
const res = await api('/notes/create', {
|
const res = await api('notes/create', {
|
||||||
text: 'test',
|
text: 'test',
|
||||||
fileIds: [file.id],
|
fileIds: [file.id],
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -64,7 +64,7 @@ describe('Note', () => {
|
||||||
}, 1000 * 10);
|
}, 1000 * 10);
|
||||||
|
|
||||||
test('存在しないファイルで怒られる', async () => {
|
test('存在しないファイルで怒られる', async () => {
|
||||||
const res = await api('/notes/create', {
|
const res = await api('notes/create', {
|
||||||
text: 'test',
|
text: 'test',
|
||||||
fileIds: ['000000000000000000000000'],
|
fileIds: ['000000000000000000000000'],
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -75,7 +75,7 @@ describe('Note', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('不正なファイルIDで怒られる', async () => {
|
test('不正なファイルIDで怒られる', async () => {
|
||||||
const res = await api('/notes/create', {
|
const res = await api('notes/create', {
|
||||||
fileIds: ['kyoppie'],
|
fileIds: ['kyoppie'],
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
|
@ -93,7 +93,7 @@ describe('Note', () => {
|
||||||
replyId: bobPost.id,
|
replyId: bobPost.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await api('/notes/create', alicePost, alice);
|
const res = await api('notes/create', alicePost, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||||
|
@ -111,7 +111,7 @@ describe('Note', () => {
|
||||||
renoteId: bobPost.id,
|
renoteId: bobPost.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await api('/notes/create', alicePost, alice);
|
const res = await api('notes/create', alicePost, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||||
|
@ -129,7 +129,7 @@ describe('Note', () => {
|
||||||
renoteId: bobPost.id,
|
renoteId: bobPost.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await api('/notes/create', alicePost, alice);
|
const res = await api('notes/create', alicePost, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||||
|
@ -142,7 +142,7 @@ describe('Note', () => {
|
||||||
const bobPost = await post(bob, {
|
const bobPost = await post(bob, {
|
||||||
text: 'test',
|
text: 'test',
|
||||||
});
|
});
|
||||||
const res = await api('/notes/create', {
|
const res = await api('notes/create', {
|
||||||
text: ' ',
|
text: ' ',
|
||||||
renoteId: bobPost.id,
|
renoteId: bobPost.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -152,7 +152,7 @@ describe('Note', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('visibility: followersでrenoteできる', async () => {
|
test('visibility: followersでrenoteできる', async () => {
|
||||||
const createRes = await api('/notes/create', {
|
const createRes = await api('notes/create', {
|
||||||
text: 'test',
|
text: 'test',
|
||||||
visibility: 'followers',
|
visibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -160,7 +160,7 @@ describe('Note', () => {
|
||||||
assert.strictEqual(createRes.status, 200);
|
assert.strictEqual(createRes.status, 200);
|
||||||
|
|
||||||
const renoteId = createRes.body.createdNote.id;
|
const renoteId = createRes.body.createdNote.id;
|
||||||
const renoteRes = await api('/notes/create', {
|
const renoteRes = await api('notes/create', {
|
||||||
visibility: 'followers',
|
visibility: 'followers',
|
||||||
renoteId,
|
renoteId,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -169,7 +169,7 @@ describe('Note', () => {
|
||||||
assert.strictEqual(renoteRes.body.createdNote.renoteId, renoteId);
|
assert.strictEqual(renoteRes.body.createdNote.renoteId, renoteId);
|
||||||
assert.strictEqual(renoteRes.body.createdNote.visibility, 'followers');
|
assert.strictEqual(renoteRes.body.createdNote.visibility, 'followers');
|
||||||
|
|
||||||
const deleteRes = await api('/notes/delete', {
|
const deleteRes = await api('notes/delete', {
|
||||||
noteId: renoteRes.body.createdNote.id,
|
noteId: renoteRes.body.createdNote.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -177,11 +177,11 @@ describe('Note', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('visibility: followersなノートに対してフォロワーはリプライできる', async () => {
|
test('visibility: followersなノートに対してフォロワーはリプライできる', async () => {
|
||||||
await api('/following/create', {
|
await api('following/create', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
|
|
||||||
const aliceNote = await api('/notes/create', {
|
const aliceNote = await api('notes/create', {
|
||||||
text: 'direct note to bob',
|
text: 'direct note to bob',
|
||||||
visibility: 'followers',
|
visibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -189,7 +189,7 @@ describe('Note', () => {
|
||||||
assert.strictEqual(aliceNote.status, 200);
|
assert.strictEqual(aliceNote.status, 200);
|
||||||
|
|
||||||
const replyId = aliceNote.body.createdNote.id;
|
const replyId = aliceNote.body.createdNote.id;
|
||||||
const bobReply = await api('/notes/create', {
|
const bobReply = await api('notes/create', {
|
||||||
text: 'reply to alice note',
|
text: 'reply to alice note',
|
||||||
replyId,
|
replyId,
|
||||||
}, bob);
|
}, bob);
|
||||||
|
@ -197,20 +197,20 @@ describe('Note', () => {
|
||||||
assert.strictEqual(bobReply.status, 200);
|
assert.strictEqual(bobReply.status, 200);
|
||||||
assert.strictEqual(bobReply.body.createdNote.replyId, replyId);
|
assert.strictEqual(bobReply.body.createdNote.replyId, replyId);
|
||||||
|
|
||||||
await api('/following/delete', {
|
await api('following/delete', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('visibility: followersなノートに対してフォロワーでないユーザーがリプライしようとすると怒られる', async () => {
|
test('visibility: followersなノートに対してフォロワーでないユーザーがリプライしようとすると怒られる', async () => {
|
||||||
const aliceNote = await api('/notes/create', {
|
const aliceNote = await api('notes/create', {
|
||||||
text: 'direct note to bob',
|
text: 'direct note to bob',
|
||||||
visibility: 'followers',
|
visibility: 'followers',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
assert.strictEqual(aliceNote.status, 200);
|
assert.strictEqual(aliceNote.status, 200);
|
||||||
|
|
||||||
const bobReply = await api('/notes/create', {
|
const bobReply = await api('notes/create', {
|
||||||
text: 'reply to alice note',
|
text: 'reply to alice note',
|
||||||
replyId: aliceNote.body.createdNote.id,
|
replyId: aliceNote.body.createdNote.id,
|
||||||
}, bob);
|
}, bob);
|
||||||
|
@ -220,7 +220,7 @@ describe('Note', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('visibility: specifiedなノートに対してvisibility: specifiedで返信できる', async () => {
|
test('visibility: specifiedなノートに対してvisibility: specifiedで返信できる', async () => {
|
||||||
const aliceNote = await api('/notes/create', {
|
const aliceNote = await api('notes/create', {
|
||||||
text: 'direct note to bob',
|
text: 'direct note to bob',
|
||||||
visibility: 'specified',
|
visibility: 'specified',
|
||||||
visibleUserIds: [bob.id],
|
visibleUserIds: [bob.id],
|
||||||
|
@ -228,7 +228,7 @@ describe('Note', () => {
|
||||||
|
|
||||||
assert.strictEqual(aliceNote.status, 200);
|
assert.strictEqual(aliceNote.status, 200);
|
||||||
|
|
||||||
const bobReply = await api('/notes/create', {
|
const bobReply = await api('notes/create', {
|
||||||
text: 'reply to alice note',
|
text: 'reply to alice note',
|
||||||
replyId: aliceNote.body.createdNote.id,
|
replyId: aliceNote.body.createdNote.id,
|
||||||
visibility: 'specified',
|
visibility: 'specified',
|
||||||
|
@ -239,7 +239,7 @@ describe('Note', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('visibility: specifiedなノートに対してvisibility: follwersで返信しようとすると怒られる', async () => {
|
test('visibility: specifiedなノートに対してvisibility: follwersで返信しようとすると怒られる', async () => {
|
||||||
const aliceNote = await api('/notes/create', {
|
const aliceNote = await api('notes/create', {
|
||||||
text: 'direct note to bob',
|
text: 'direct note to bob',
|
||||||
visibility: 'specified',
|
visibility: 'specified',
|
||||||
visibleUserIds: [bob.id],
|
visibleUserIds: [bob.id],
|
||||||
|
@ -247,7 +247,7 @@ describe('Note', () => {
|
||||||
|
|
||||||
assert.strictEqual(aliceNote.status, 200);
|
assert.strictEqual(aliceNote.status, 200);
|
||||||
|
|
||||||
const bobReply = await api('/notes/create', {
|
const bobReply = await api('notes/create', {
|
||||||
text: 'reply to alice note with visibility: followers',
|
text: 'reply to alice note with visibility: followers',
|
||||||
replyId: aliceNote.body.createdNote.id,
|
replyId: aliceNote.body.createdNote.id,
|
||||||
visibility: 'followers',
|
visibility: 'followers',
|
||||||
|
@ -261,7 +261,7 @@ describe('Note', () => {
|
||||||
const post = {
|
const post = {
|
||||||
text: '!'.repeat(MAX_NOTE_TEXT_LENGTH), // 3000文字
|
text: '!'.repeat(MAX_NOTE_TEXT_LENGTH), // 3000文字
|
||||||
};
|
};
|
||||||
const res = await api('/notes/create', post, alice);
|
const res = await api('notes/create', post, alice);
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -269,7 +269,7 @@ describe('Note', () => {
|
||||||
const post = {
|
const post = {
|
||||||
text: '!'.repeat(MAX_NOTE_TEXT_LENGTH + 1), // 3001文字
|
text: '!'.repeat(MAX_NOTE_TEXT_LENGTH + 1), // 3001文字
|
||||||
};
|
};
|
||||||
const res = await api('/notes/create', post, alice);
|
const res = await api('notes/create', post, alice);
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -278,7 +278,7 @@ describe('Note', () => {
|
||||||
text: 'test',
|
text: 'test',
|
||||||
replyId: '000000000000000000000000',
|
replyId: '000000000000000000000000',
|
||||||
};
|
};
|
||||||
const res = await api('/notes/create', post, alice);
|
const res = await api('notes/create', post, alice);
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -286,7 +286,7 @@ describe('Note', () => {
|
||||||
const post = {
|
const post = {
|
||||||
renoteId: '000000000000000000000000',
|
renoteId: '000000000000000000000000',
|
||||||
};
|
};
|
||||||
const res = await api('/notes/create', post, alice);
|
const res = await api('notes/create', post, alice);
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -295,7 +295,7 @@ describe('Note', () => {
|
||||||
text: 'test',
|
text: 'test',
|
||||||
replyId: 'foo',
|
replyId: 'foo',
|
||||||
};
|
};
|
||||||
const res = await api('/notes/create', post, alice);
|
const res = await api('notes/create', post, alice);
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -303,7 +303,7 @@ describe('Note', () => {
|
||||||
const post = {
|
const post = {
|
||||||
renoteId: 'foo',
|
renoteId: 'foo',
|
||||||
};
|
};
|
||||||
const res = await api('/notes/create', post, alice);
|
const res = await api('notes/create', post, alice);
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -312,7 +312,7 @@ describe('Note', () => {
|
||||||
text: '@ghost yo',
|
text: '@ghost yo',
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await api('/notes/create', post, alice);
|
const res = await api('notes/create', post, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||||
|
@ -324,7 +324,7 @@ describe('Note', () => {
|
||||||
text: '@bob @bob @bob yo',
|
text: '@bob @bob @bob yo',
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await api('/notes/create', post, alice);
|
const res = await api('notes/create', post, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||||
|
@ -337,25 +337,25 @@ describe('Note', () => {
|
||||||
describe('添付ファイル情報', () => {
|
describe('添付ファイル情報', () => {
|
||||||
test('ファイルを添付した場合、投稿成功時にファイル情報入りのレスポンスが帰ってくる', async () => {
|
test('ファイルを添付した場合、投稿成功時にファイル情報入りのレスポンスが帰ってくる', async () => {
|
||||||
const file = await uploadFile(alice);
|
const file = await uploadFile(alice);
|
||||||
const res = await api('/notes/create', {
|
const res = await api('notes/create', {
|
||||||
fileIds: [file.body.id],
|
fileIds: [file.body!.id],
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||||
assert.strictEqual(res.body.createdNote.files.length, 1);
|
assert.strictEqual(res.body.createdNote.files.length, 1);
|
||||||
assert.strictEqual(res.body.createdNote.files[0].id, file.body.id);
|
assert.strictEqual(res.body.createdNote.files[0].id, file.body!.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('ファイルを添付した場合、タイムラインでファイル情報入りのレスポンスが帰ってくる', async () => {
|
test('ファイルを添付した場合、タイムラインでファイル情報入りのレスポンスが帰ってくる', async () => {
|
||||||
const file = await uploadFile(alice);
|
const file = await uploadFile(alice);
|
||||||
const createdNote = await api('/notes/create', {
|
const createdNote = await api('notes/create', {
|
||||||
fileIds: [file.body.id],
|
fileIds: [file.body!.id],
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
assert.strictEqual(createdNote.status, 200);
|
assert.strictEqual(createdNote.status, 200);
|
||||||
|
|
||||||
const res = await api('/notes', {
|
const res = await api('notes', {
|
||||||
withFiles: true,
|
withFiles: true,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -364,23 +364,23 @@ describe('Note', () => {
|
||||||
const myNote = res.body.find((note: { id: string; files: { id: string }[] }) => note.id === createdNote.body.createdNote.id);
|
const myNote = res.body.find((note: { id: string; files: { id: string }[] }) => note.id === createdNote.body.createdNote.id);
|
||||||
assert.notEqual(myNote, null);
|
assert.notEqual(myNote, null);
|
||||||
assert.strictEqual(myNote.files.length, 1);
|
assert.strictEqual(myNote.files.length, 1);
|
||||||
assert.strictEqual(myNote.files[0].id, file.body.id);
|
assert.strictEqual(myNote.files[0].id, file.body!.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('ファイルが添付されたノートをリノートした場合、タイムラインでファイル情報入りのレスポンスが帰ってくる', async () => {
|
test('ファイルが添付されたノートをリノートした場合、タイムラインでファイル情報入りのレスポンスが帰ってくる', async () => {
|
||||||
const file = await uploadFile(alice);
|
const file = await uploadFile(alice);
|
||||||
const createdNote = await api('/notes/create', {
|
const createdNote = await api('notes/create', {
|
||||||
fileIds: [file.body.id],
|
fileIds: [file.body!.id],
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
assert.strictEqual(createdNote.status, 200);
|
assert.strictEqual(createdNote.status, 200);
|
||||||
|
|
||||||
const renoted = await api('/notes/create', {
|
const renoted = await api('notes/create', {
|
||||||
renoteId: createdNote.body.createdNote.id,
|
renoteId: createdNote.body.createdNote.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(renoted.status, 200);
|
assert.strictEqual(renoted.status, 200);
|
||||||
|
|
||||||
const res = await api('/notes', {
|
const res = await api('notes', {
|
||||||
renote: true,
|
renote: true,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -389,24 +389,24 @@ describe('Note', () => {
|
||||||
const myNote = res.body.find((note: { id: string }) => note.id === renoted.body.createdNote.id);
|
const myNote = res.body.find((note: { id: string }) => note.id === renoted.body.createdNote.id);
|
||||||
assert.notEqual(myNote, null);
|
assert.notEqual(myNote, null);
|
||||||
assert.strictEqual(myNote.renote.files.length, 1);
|
assert.strictEqual(myNote.renote.files.length, 1);
|
||||||
assert.strictEqual(myNote.renote.files[0].id, file.body.id);
|
assert.strictEqual(myNote.renote.files[0].id, file.body!.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('ファイルが添付されたノートに返信した場合、タイムラインでファイル情報入りのレスポンスが帰ってくる', async () => {
|
test('ファイルが添付されたノートに返信した場合、タイムラインでファイル情報入りのレスポンスが帰ってくる', async () => {
|
||||||
const file = await uploadFile(alice);
|
const file = await uploadFile(alice);
|
||||||
const createdNote = await api('/notes/create', {
|
const createdNote = await api('notes/create', {
|
||||||
fileIds: [file.body.id],
|
fileIds: [file.body!.id],
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
assert.strictEqual(createdNote.status, 200);
|
assert.strictEqual(createdNote.status, 200);
|
||||||
|
|
||||||
const reply = await api('/notes/create', {
|
const reply = await api('notes/create', {
|
||||||
replyId: createdNote.body.createdNote.id,
|
replyId: createdNote.body.createdNote.id,
|
||||||
text: 'this is reply',
|
text: 'this is reply',
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(reply.status, 200);
|
assert.strictEqual(reply.status, 200);
|
||||||
|
|
||||||
const res = await api('/notes', {
|
const res = await api('notes', {
|
||||||
reply: true,
|
reply: true,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -415,29 +415,29 @@ describe('Note', () => {
|
||||||
const myNote = res.body.find((note: { id: string }) => note.id === reply.body.createdNote.id);
|
const myNote = res.body.find((note: { id: string }) => note.id === reply.body.createdNote.id);
|
||||||
assert.notEqual(myNote, null);
|
assert.notEqual(myNote, null);
|
||||||
assert.strictEqual(myNote.reply.files.length, 1);
|
assert.strictEqual(myNote.reply.files.length, 1);
|
||||||
assert.strictEqual(myNote.reply.files[0].id, file.body.id);
|
assert.strictEqual(myNote.reply.files[0].id, file.body!.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('ファイルが添付されたノートへの返信をリノートした場合、タイムラインでファイル情報入りのレスポンスが帰ってくる', async () => {
|
test('ファイルが添付されたノートへの返信をリノートした場合、タイムラインでファイル情報入りのレスポンスが帰ってくる', async () => {
|
||||||
const file = await uploadFile(alice);
|
const file = await uploadFile(alice);
|
||||||
const createdNote = await api('/notes/create', {
|
const createdNote = await api('notes/create', {
|
||||||
fileIds: [file.body.id],
|
fileIds: [file.body!.id],
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
assert.strictEqual(createdNote.status, 200);
|
assert.strictEqual(createdNote.status, 200);
|
||||||
|
|
||||||
const reply = await api('/notes/create', {
|
const reply = await api('notes/create', {
|
||||||
replyId: createdNote.body.createdNote.id,
|
replyId: createdNote.body.createdNote.id,
|
||||||
text: 'this is reply',
|
text: 'this is reply',
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(reply.status, 200);
|
assert.strictEqual(reply.status, 200);
|
||||||
|
|
||||||
const renoted = await api('/notes/create', {
|
const renoted = await api('notes/create', {
|
||||||
renoteId: reply.body.createdNote.id,
|
renoteId: reply.body.createdNote.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(renoted.status, 200);
|
assert.strictEqual(renoted.status, 200);
|
||||||
|
|
||||||
const res = await api('/notes', {
|
const res = await api('notes', {
|
||||||
renote: true,
|
renote: true,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -446,7 +446,7 @@ describe('Note', () => {
|
||||||
const myNote = res.body.find((note: { id: string }) => note.id === renoted.body.createdNote.id);
|
const myNote = res.body.find((note: { id: string }) => note.id === renoted.body.createdNote.id);
|
||||||
assert.notEqual(myNote, null);
|
assert.notEqual(myNote, null);
|
||||||
assert.strictEqual(myNote.renote.reply.files.length, 1);
|
assert.strictEqual(myNote.renote.reply.files.length, 1);
|
||||||
assert.strictEqual(myNote.renote.reply.files[0].id, file.body.id);
|
assert.strictEqual(myNote.renote.reply.files[0].id, file.body!.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('NSFWが強制されている場合変更できない', async () => {
|
test('NSFWが強制されている場合変更できない', async () => {
|
||||||
|
@ -483,15 +483,15 @@ describe('Note', () => {
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
assert.strictEqual(assign.status, 204);
|
assert.strictEqual(assign.status, 204);
|
||||||
assert.strictEqual(file.body.isSensitive, false);
|
assert.strictEqual(file.body!.isSensitive, false);
|
||||||
|
|
||||||
const nsfwfile = await uploadFile(alice);
|
const nsfwfile = await uploadFile(alice);
|
||||||
|
|
||||||
assert.strictEqual(nsfwfile.status, 200);
|
assert.strictEqual(nsfwfile.status, 200);
|
||||||
assert.strictEqual(nsfwfile.body.isSensitive, true);
|
assert.strictEqual(nsfwfile.body!.isSensitive, true);
|
||||||
|
|
||||||
const liftnsfw = await api('drive/files/update', {
|
const liftnsfw = await api('drive/files/update', {
|
||||||
fileId: nsfwfile.body.id,
|
fileId: nsfwfile.body!.id,
|
||||||
isSensitive: false,
|
isSensitive: false,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -499,7 +499,7 @@ describe('Note', () => {
|
||||||
assert.strictEqual(liftnsfw.body.error.code, 'RESTRICTED_BY_ROLE');
|
assert.strictEqual(liftnsfw.body.error.code, 'RESTRICTED_BY_ROLE');
|
||||||
|
|
||||||
const oldaddnsfw = await api('drive/files/update', {
|
const oldaddnsfw = await api('drive/files/update', {
|
||||||
fileId: file.body.id,
|
fileId: file.body!.id,
|
||||||
isSensitive: true,
|
isSensitive: true,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -518,7 +518,7 @@ describe('Note', () => {
|
||||||
|
|
||||||
describe('notes/create', () => {
|
describe('notes/create', () => {
|
||||||
test('投票を添付できる', async () => {
|
test('投票を添付できる', async () => {
|
||||||
const res = await api('/notes/create', {
|
const res = await api('notes/create', {
|
||||||
text: 'test',
|
text: 'test',
|
||||||
poll: {
|
poll: {
|
||||||
choices: ['foo', 'bar'],
|
choices: ['foo', 'bar'],
|
||||||
|
@ -531,14 +531,15 @@ describe('Note', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('投票の選択肢が無くて怒られる', async () => {
|
test('投票の選択肢が無くて怒られる', async () => {
|
||||||
const res = await api('/notes/create', {
|
const res = await api('notes/create', {
|
||||||
|
// @ts-expect-error poll must not be empty
|
||||||
poll: {},
|
poll: {},
|
||||||
}, alice);
|
}, alice);
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('投票の選択肢が無くて怒られる (空の配列)', async () => {
|
test('投票の選択肢が無くて怒られる (空の配列)', async () => {
|
||||||
const res = await api('/notes/create', {
|
const res = await api('notes/create', {
|
||||||
poll: {
|
poll: {
|
||||||
choices: [],
|
choices: [],
|
||||||
},
|
},
|
||||||
|
@ -547,7 +548,7 @@ describe('Note', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('投票の選択肢が1つで怒られる', async () => {
|
test('投票の選択肢が1つで怒られる', async () => {
|
||||||
const res = await api('/notes/create', {
|
const res = await api('notes/create', {
|
||||||
poll: {
|
poll: {
|
||||||
choices: ['Strawberry Pasta'],
|
choices: ['Strawberry Pasta'],
|
||||||
},
|
},
|
||||||
|
@ -556,14 +557,14 @@ describe('Note', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('投票できる', async () => {
|
test('投票できる', async () => {
|
||||||
const { body } = await api('/notes/create', {
|
const { body } = await api('notes/create', {
|
||||||
text: 'test',
|
text: 'test',
|
||||||
poll: {
|
poll: {
|
||||||
choices: ['sakura', 'izumi', 'ako'],
|
choices: ['sakura', 'izumi', 'ako'],
|
||||||
},
|
},
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const res = await api('/notes/polls/vote', {
|
const res = await api('notes/polls/vote', {
|
||||||
noteId: body.createdNote.id,
|
noteId: body.createdNote.id,
|
||||||
choice: 1,
|
choice: 1,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -572,19 +573,19 @@ describe('Note', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('複数投票できない', async () => {
|
test('複数投票できない', async () => {
|
||||||
const { body } = await api('/notes/create', {
|
const { body } = await api('notes/create', {
|
||||||
text: 'test',
|
text: 'test',
|
||||||
poll: {
|
poll: {
|
||||||
choices: ['sakura', 'izumi', 'ako'],
|
choices: ['sakura', 'izumi', 'ako'],
|
||||||
},
|
},
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
await api('/notes/polls/vote', {
|
await api('notes/polls/vote', {
|
||||||
noteId: body.createdNote.id,
|
noteId: body.createdNote.id,
|
||||||
choice: 0,
|
choice: 0,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const res = await api('/notes/polls/vote', {
|
const res = await api('notes/polls/vote', {
|
||||||
noteId: body.createdNote.id,
|
noteId: body.createdNote.id,
|
||||||
choice: 2,
|
choice: 2,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -593,7 +594,7 @@ describe('Note', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('許可されている場合は複数投票できる', async () => {
|
test('許可されている場合は複数投票できる', async () => {
|
||||||
const { body } = await api('/notes/create', {
|
const { body } = await api('notes/create', {
|
||||||
text: 'test',
|
text: 'test',
|
||||||
poll: {
|
poll: {
|
||||||
choices: ['sakura', 'izumi', 'ako'],
|
choices: ['sakura', 'izumi', 'ako'],
|
||||||
|
@ -601,17 +602,17 @@ describe('Note', () => {
|
||||||
},
|
},
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
await api('/notes/polls/vote', {
|
await api('notes/polls/vote', {
|
||||||
noteId: body.createdNote.id,
|
noteId: body.createdNote.id,
|
||||||
choice: 0,
|
choice: 0,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
await api('/notes/polls/vote', {
|
await api('notes/polls/vote', {
|
||||||
noteId: body.createdNote.id,
|
noteId: body.createdNote.id,
|
||||||
choice: 1,
|
choice: 1,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
const res = await api('/notes/polls/vote', {
|
const res = await api('notes/polls/vote', {
|
||||||
noteId: body.createdNote.id,
|
noteId: body.createdNote.id,
|
||||||
choice: 2,
|
choice: 2,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -620,7 +621,7 @@ describe('Note', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('締め切られている場合は投票できない', async () => {
|
test('締め切られている場合は投票できない', async () => {
|
||||||
const { body } = await api('/notes/create', {
|
const { body } = await api('notes/create', {
|
||||||
text: 'test',
|
text: 'test',
|
||||||
poll: {
|
poll: {
|
||||||
choices: ['sakura', 'izumi', 'ako'],
|
choices: ['sakura', 'izumi', 'ako'],
|
||||||
|
@ -630,7 +631,7 @@ describe('Note', () => {
|
||||||
|
|
||||||
await new Promise(x => setTimeout(x, 2));
|
await new Promise(x => setTimeout(x, 2));
|
||||||
|
|
||||||
const res = await api('/notes/polls/vote', {
|
const res = await api('notes/polls/vote', {
|
||||||
noteId: body.createdNote.id,
|
noteId: body.createdNote.id,
|
||||||
choice: 1,
|
choice: 1,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
@ -649,7 +650,7 @@ describe('Note', () => {
|
||||||
|
|
||||||
await new Promise(x => setTimeout(x, 2));
|
await new Promise(x => setTimeout(x, 2));
|
||||||
|
|
||||||
const note1 = await api('/notes/create', {
|
const note1 = await api('notes/create', {
|
||||||
text: 'hogetesthuge',
|
text: 'hogetesthuge',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -666,7 +667,7 @@ describe('Note', () => {
|
||||||
|
|
||||||
assert.strictEqual(sensitive.status, 204);
|
assert.strictEqual(sensitive.status, 204);
|
||||||
|
|
||||||
const note2 = await api('/notes/create', {
|
const note2 = await api('notes/create', {
|
||||||
text: 'hogetesthuge',
|
text: 'hogetesthuge',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -683,7 +684,7 @@ describe('Note', () => {
|
||||||
|
|
||||||
assert.strictEqual(sensitive.status, 204);
|
assert.strictEqual(sensitive.status, 204);
|
||||||
|
|
||||||
const note2 = await api('/notes/create', {
|
const note2 = await api('notes/create', {
|
||||||
text: 'hogeTesthuge',
|
text: 'hogeTesthuge',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -702,7 +703,7 @@ describe('Note', () => {
|
||||||
|
|
||||||
await new Promise(x => setTimeout(x, 2));
|
await new Promise(x => setTimeout(x, 2));
|
||||||
|
|
||||||
const note1 = await api('/notes/create', {
|
const note1 = await api('notes/create', {
|
||||||
text: 'hogetesthuge',
|
text: 'hogetesthuge',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -719,7 +720,7 @@ describe('Note', () => {
|
||||||
|
|
||||||
assert.strictEqual(prohibited.status, 204);
|
assert.strictEqual(prohibited.status, 204);
|
||||||
|
|
||||||
const note2 = await api('/notes/create', {
|
const note2 = await api('notes/create', {
|
||||||
text: 'hogetesthuge',
|
text: 'hogetesthuge',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -736,7 +737,7 @@ describe('Note', () => {
|
||||||
|
|
||||||
assert.strictEqual(prohibited.status, 204);
|
assert.strictEqual(prohibited.status, 204);
|
||||||
|
|
||||||
const note2 = await api('/notes/create', {
|
const note2 = await api('notes/create', {
|
||||||
text: 'hogeTesthuge',
|
text: 'hogeTesthuge',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -755,7 +756,7 @@ describe('Note', () => {
|
||||||
|
|
||||||
await new Promise(x => setTimeout(x, 2));
|
await new Promise(x => setTimeout(x, 2));
|
||||||
|
|
||||||
const note1 = await api('/notes/create', {
|
const note1 = await api('notes/create', {
|
||||||
text: 'hogetesthuge',
|
text: 'hogetesthuge',
|
||||||
}, tom);
|
}, tom);
|
||||||
|
|
||||||
|
@ -799,7 +800,7 @@ describe('Note', () => {
|
||||||
|
|
||||||
await new Promise(x => setTimeout(x, 2));
|
await new Promise(x => setTimeout(x, 2));
|
||||||
|
|
||||||
const note = await api('/notes/create', {
|
const note = await api('notes/create', {
|
||||||
text: '@bob potentially annoying text',
|
text: '@bob potentially annoying text',
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -853,10 +854,10 @@ describe('Note', () => {
|
||||||
|
|
||||||
await new Promise(x => setTimeout(x, 2));
|
await new Promise(x => setTimeout(x, 2));
|
||||||
|
|
||||||
const note = await api('/notes/create', {
|
const note = await api('notes/create', {
|
||||||
text: 'potentially annoying text',
|
text: 'potentially annoying text',
|
||||||
visibility: 'specified',
|
visibility: 'specified',
|
||||||
visibleUserIds: [ bob.id ],
|
visibleUserIds: [bob.id],
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
assert.strictEqual(note.status, 400);
|
assert.strictEqual(note.status, 400);
|
||||||
|
@ -909,10 +910,10 @@ describe('Note', () => {
|
||||||
|
|
||||||
await new Promise(x => setTimeout(x, 2));
|
await new Promise(x => setTimeout(x, 2));
|
||||||
|
|
||||||
const note = await api('/notes/create', {
|
const note = await api('notes/create', {
|
||||||
text: '@bob potentially annoying text',
|
text: '@bob potentially annoying text',
|
||||||
visibility: 'specified',
|
visibility: 'specified',
|
||||||
visibleUserIds: [ bob.id ],
|
visibleUserIds: [bob.id],
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
assert.strictEqual(note.status, 200);
|
assert.strictEqual(note.status, 200);
|
||||||
|
|
|
@ -22,7 +22,7 @@ describe('Renote Mute', () => {
|
||||||
}, 1000 * 60 * 2);
|
}, 1000 * 60 * 2);
|
||||||
|
|
||||||
test('ミュート作成', async () => {
|
test('ミュート作成', async () => {
|
||||||
const res = await api('/renote-mute/create', {
|
const res = await api('renote-mute/create', {
|
||||||
userId: carol.id,
|
userId: carol.id,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ describe('Renote Mute', () => {
|
||||||
// redisに追加されるのを待つ
|
// redisに追加されるのを待つ
|
||||||
await sleep(100);
|
await sleep(100);
|
||||||
|
|
||||||
const res = await api('/notes/local-timeline', {}, alice);
|
const res = await api('notes/local-timeline', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
@ -54,7 +54,7 @@ describe('Renote Mute', () => {
|
||||||
// redisに追加されるのを待つ
|
// redisに追加されるのを待つ
|
||||||
await sleep(100);
|
await sleep(100);
|
||||||
|
|
||||||
const res = await api('/notes/local-timeline', {}, alice);
|
const res = await api('notes/local-timeline', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
|
|
@ -601,7 +601,7 @@ describe('Streaming', () => {
|
||||||
|
|
||||||
// #10443
|
// #10443
|
||||||
test('ミュートしているサーバのノートがリストTLに流れない', async () => {
|
test('ミュートしているサーバのノートがリストTLに流れない', async () => {
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
mutedInstances: ['example.com'],
|
mutedInstances: ['example.com'],
|
||||||
}, chitose);
|
}, chitose);
|
||||||
|
|
||||||
|
@ -618,7 +618,7 @@ describe('Streaming', () => {
|
||||||
|
|
||||||
// #10443
|
// #10443
|
||||||
test('ミュートしているサーバのノートに対するリプライがリストTLに流れない', async () => {
|
test('ミュートしているサーバのノートに対するリプライがリストTLに流れない', async () => {
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
mutedInstances: ['example.com'],
|
mutedInstances: ['example.com'],
|
||||||
}, chitose);
|
}, chitose);
|
||||||
|
|
||||||
|
@ -635,7 +635,7 @@ describe('Streaming', () => {
|
||||||
|
|
||||||
// #10443
|
// #10443
|
||||||
test('ミュートしているサーバのノートに対するリノートがリストTLに流れない', async () => {
|
test('ミュートしているサーバのノートに対するリノートがリストTLに流れない', async () => {
|
||||||
await api('/i/update', {
|
await api('i/update', {
|
||||||
mutedInstances: ['example.com'],
|
mutedInstances: ['example.com'],
|
||||||
}, chitose);
|
}, chitose);
|
||||||
|
|
||||||
|
|
|
@ -24,12 +24,12 @@ describe('Note thread mute', () => {
|
||||||
const bobNote = await post(bob, { text: '@alice @carol root note' });
|
const bobNote = await post(bob, { text: '@alice @carol root note' });
|
||||||
const aliceReply = await post(alice, { replyId: bobNote.id, text: '@bob @carol child note' });
|
const aliceReply = await post(alice, { replyId: bobNote.id, text: '@bob @carol child note' });
|
||||||
|
|
||||||
await api('/notes/thread-muting/create', { noteId: bobNote.id }, alice);
|
await api('notes/thread-muting/create', { noteId: bobNote.id }, alice);
|
||||||
|
|
||||||
const carolReply = await post(carol, { replyId: bobNote.id, text: '@bob @alice child note' });
|
const carolReply = await post(carol, { replyId: bobNote.id, text: '@bob @alice child note' });
|
||||||
const carolReplyWithoutMention = await post(carol, { replyId: aliceReply.id, text: 'child note' });
|
const carolReplyWithoutMention = await post(carol, { replyId: aliceReply.id, text: 'child note' });
|
||||||
|
|
||||||
const res = await api('/notes/mentions', {}, alice);
|
const res = await api('notes/mentions', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
@ -40,15 +40,15 @@ describe('Note thread mute', () => {
|
||||||
|
|
||||||
test('ミュートしているスレッドからメンションされても、hasUnreadMentions が true にならない', async () => {
|
test('ミュートしているスレッドからメンションされても、hasUnreadMentions が true にならない', async () => {
|
||||||
// 状態リセット
|
// 状態リセット
|
||||||
await api('/i/read-all-unread-notes', {}, alice);
|
await api('i/read-all-unread-notes', {}, alice);
|
||||||
|
|
||||||
const bobNote = await post(bob, { text: '@alice @carol root note' });
|
const bobNote = await post(bob, { text: '@alice @carol root note' });
|
||||||
|
|
||||||
await api('/notes/thread-muting/create', { noteId: bobNote.id }, alice);
|
await api('notes/thread-muting/create', { noteId: bobNote.id }, alice);
|
||||||
|
|
||||||
const carolReply = await post(carol, { replyId: bobNote.id, text: '@bob @alice child note' });
|
const carolReply = await post(carol, { replyId: bobNote.id, text: '@bob @alice child note' });
|
||||||
|
|
||||||
const res = await api('/i', {}, alice);
|
const res = await api('i', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(res.body.hasUnreadMentions, false);
|
assert.strictEqual(res.body.hasUnreadMentions, false);
|
||||||
|
@ -56,11 +56,11 @@ describe('Note thread mute', () => {
|
||||||
|
|
||||||
test('ミュートしているスレッドからメンションされても、ストリームに unreadMention イベントが流れてこない', () => new Promise<void>(async done => {
|
test('ミュートしているスレッドからメンションされても、ストリームに unreadMention イベントが流れてこない', () => new Promise<void>(async done => {
|
||||||
// 状態リセット
|
// 状態リセット
|
||||||
await api('/i/read-all-unread-notes', {}, alice);
|
await api('i/read-all-unread-notes', {}, alice);
|
||||||
|
|
||||||
const bobNote = await post(bob, { text: '@alice @carol root note' });
|
const bobNote = await post(bob, { text: '@alice @carol root note' });
|
||||||
|
|
||||||
await api('/notes/thread-muting/create', { noteId: bobNote.id }, alice);
|
await api('notes/thread-muting/create', { noteId: bobNote.id }, alice);
|
||||||
|
|
||||||
let fired = false;
|
let fired = false;
|
||||||
|
|
||||||
|
@ -84,12 +84,12 @@ describe('Note thread mute', () => {
|
||||||
const bobNote = await post(bob, { text: '@alice @carol root note' });
|
const bobNote = await post(bob, { text: '@alice @carol root note' });
|
||||||
const aliceReply = await post(alice, { replyId: bobNote.id, text: '@bob @carol child note' });
|
const aliceReply = await post(alice, { replyId: bobNote.id, text: '@bob @carol child note' });
|
||||||
|
|
||||||
await api('/notes/thread-muting/create', { noteId: bobNote.id }, alice);
|
await api('notes/thread-muting/create', { noteId: bobNote.id }, alice);
|
||||||
|
|
||||||
const carolReply = await post(carol, { replyId: bobNote.id, text: '@bob @alice child note' });
|
const carolReply = await post(carol, { replyId: bobNote.id, text: '@bob @alice child note' });
|
||||||
const carolReplyWithoutMention = await post(carol, { replyId: aliceReply.id, text: 'child note' });
|
const carolReplyWithoutMention = await post(carol, { replyId: aliceReply.id, text: 'child note' });
|
||||||
|
|
||||||
const res = await api('/i/notifications', {}, alice);
|
const res = await api('i/notifications', {}, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.status, 200);
|
assert.strictEqual(res.status, 200);
|
||||||
assert.strictEqual(Array.isArray(res.body), true);
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -11,9 +11,9 @@ import type * as misskey from 'misskey-js';
|
||||||
|
|
||||||
describe('users/notes', () => {
|
describe('users/notes', () => {
|
||||||
let alice: misskey.entities.SignupResponse;
|
let alice: misskey.entities.SignupResponse;
|
||||||
let jpgNote: any;
|
let jpgNote: misskey.entities.Note;
|
||||||
let pngNote: any;
|
let pngNote: misskey.entities.Note;
|
||||||
let jpgPngNote: any;
|
let jpgPngNote: misskey.entities.Note;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
alice = await signup({ username: 'alice' });
|
alice = await signup({ username: 'alice' });
|
||||||
|
@ -31,7 +31,7 @@ describe('users/notes', () => {
|
||||||
}, 1000 * 60 * 2);
|
}, 1000 * 60 * 2);
|
||||||
|
|
||||||
test('withFiles', async () => {
|
test('withFiles', async () => {
|
||||||
const res = await api('/users/notes', {
|
const res = await api('users/notes', {
|
||||||
userId: alice.id,
|
userId: alice.id,
|
||||||
withFiles: true,
|
withFiles: true,
|
||||||
}, alice);
|
}, alice);
|
||||||
|
|
|
@ -8,7 +8,7 @@ process.env.NODE_ENV = 'test';
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import { inspect } from 'node:util';
|
import { inspect } from 'node:util';
|
||||||
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
||||||
import { api, page, post, role, signup, successfulApiCall, uploadFile } from '../utils.js';
|
import { api, post, role, signup, successfulApiCall, uploadFile } from '../utils.js';
|
||||||
import type * as misskey from 'misskey-js';
|
import type * as misskey from 'misskey-js';
|
||||||
|
|
||||||
describe('ユーザー', () => {
|
describe('ユーザー', () => {
|
||||||
|
@ -24,31 +24,12 @@ describe('ユーザー', () => {
|
||||||
}, {});
|
}, {});
|
||||||
};
|
};
|
||||||
|
|
||||||
// BUG misskey-jsとjson-schemaと実際に返ってくるデータが全部違う
|
const show = async (id: string, me = root): Promise<misskey.entities.UserDetailed> => {
|
||||||
type UserLite = misskey.entities.UserLite & {
|
return successfulApiCall({ endpoint: 'users/show', parameters: { userId: id }, user: me });
|
||||||
badgeRoles: any[],
|
|
||||||
};
|
|
||||||
|
|
||||||
type UserDetailedNotMe = UserLite &
|
|
||||||
misskey.entities.UserDetailed & {
|
|
||||||
roles: any[],
|
|
||||||
};
|
|
||||||
|
|
||||||
type MeDetailed = UserDetailedNotMe &
|
|
||||||
misskey.entities.MeDetailed & {
|
|
||||||
achievements: object[],
|
|
||||||
loggedInDays: number,
|
|
||||||
policies: object,
|
|
||||||
};
|
|
||||||
|
|
||||||
type User = MeDetailed & { token: string };
|
|
||||||
|
|
||||||
const show = async (id: string, me = root): Promise<MeDetailed | UserDetailedNotMe> => {
|
|
||||||
return successfulApiCall({ endpoint: 'users/show', parameters: { userId: id }, user: me }) as any;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// UserLiteのキーが過不足なく入っている?
|
// UserLiteのキーが過不足なく入っている?
|
||||||
const userLite = (user: User): Partial<UserLite> => {
|
const userLite = (user: misskey.entities.UserLite): Partial<misskey.entities.UserLite> => {
|
||||||
return stripUndefined({
|
return stripUndefined({
|
||||||
id: user.id,
|
id: user.id,
|
||||||
name: user.name,
|
name: user.name,
|
||||||
|
@ -71,7 +52,7 @@ describe('ユーザー', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// UserDetailedNotMeのキーが過不足なく入っている?
|
// UserDetailedNotMeのキーが過不足なく入っている?
|
||||||
const userDetailedNotMe = (user: User): Partial<UserDetailedNotMe> => {
|
const userDetailedNotMe = (user: misskey.entities.SignupResponse): Partial<misskey.entities.UserDetailedNotMe> => {
|
||||||
return stripUndefined({
|
return stripUndefined({
|
||||||
...userLite(user),
|
...userLite(user),
|
||||||
url: user.url,
|
url: user.url,
|
||||||
|
@ -111,7 +92,7 @@ describe('ユーザー', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Relations関連のキーが過不足なく入っている?
|
// Relations関連のキーが過不足なく入っている?
|
||||||
const userDetailedNotMeWithRelations = (user: User): Partial<UserDetailedNotMe> => {
|
const userDetailedNotMeWithRelations = (user: misskey.entities.SignupResponse): Partial<misskey.entities.UserDetailedNotMe> => {
|
||||||
return stripUndefined({
|
return stripUndefined({
|
||||||
...userDetailedNotMe(user),
|
...userDetailedNotMe(user),
|
||||||
isFollowing: user.isFollowing ?? false,
|
isFollowing: user.isFollowing ?? false,
|
||||||
|
@ -128,7 +109,7 @@ describe('ユーザー', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// MeDetailedのキーが過不足なく入っている?
|
// MeDetailedのキーが過不足なく入っている?
|
||||||
const meDetailed = (user: User, security = false): Partial<MeDetailed> => {
|
const meDetailed = (user: misskey.entities.SignupResponse, security = false): Partial<misskey.entities.MeDetailed> => {
|
||||||
return stripUndefined({
|
return stripUndefined({
|
||||||
...userDetailedNotMe(user),
|
...userDetailedNotMe(user),
|
||||||
avatarId: user.avatarId,
|
avatarId: user.avatarId,
|
||||||
|
@ -159,6 +140,7 @@ describe('ユーザー', () => {
|
||||||
mutedWords: user.mutedWords,
|
mutedWords: user.mutedWords,
|
||||||
hardMutedWords: user.hardMutedWords,
|
hardMutedWords: user.hardMutedWords,
|
||||||
mutedInstances: user.mutedInstances,
|
mutedInstances: user.mutedInstances,
|
||||||
|
// @ts-expect-error 後方互換性
|
||||||
mutingNotificationTypes: user.mutingNotificationTypes,
|
mutingNotificationTypes: user.mutingNotificationTypes,
|
||||||
notificationRecieveConfig: user.notificationRecieveConfig,
|
notificationRecieveConfig: user.notificationRecieveConfig,
|
||||||
emailNotificationTypes: user.emailNotificationTypes,
|
emailNotificationTypes: user.emailNotificationTypes,
|
||||||
|
@ -173,61 +155,53 @@ describe('ユーザー', () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let root: User;
|
let root: misskey.entities.SignupResponse;
|
||||||
let alice: User;
|
let alice: misskey.entities.SignupResponse;
|
||||||
let aliceNote: misskey.entities.Note;
|
let aliceNote: misskey.entities.Note;
|
||||||
let alicePage: misskey.entities.Page;
|
|
||||||
let aliceList: misskey.entities.UserList;
|
|
||||||
|
|
||||||
let bob: User;
|
let bob: misskey.entities.SignupResponse;
|
||||||
let bobNote: misskey.entities.Note;
|
|
||||||
|
|
||||||
let carol: User;
|
// NOTE: これがないと落ちる(bob の updatedAt が null になってしまうため?)
|
||||||
let dave: User;
|
let bobNote: misskey.entities.Note; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||||
let ellen: User;
|
|
||||||
let frank: User;
|
|
||||||
|
|
||||||
let usersReplying: User[];
|
let carol: misskey.entities.SignupResponse;
|
||||||
|
|
||||||
let userNoNote: User;
|
let usersReplying: misskey.entities.SignupResponse[];
|
||||||
let userNotExplorable: User;
|
|
||||||
let userLocking: User;
|
let userNoNote: misskey.entities.SignupResponse;
|
||||||
let userAdmin: User;
|
let userNotExplorable: misskey.entities.SignupResponse;
|
||||||
let roleAdmin: any;
|
let userLocking: misskey.entities.SignupResponse;
|
||||||
let userModerator: User;
|
let userAdmin: misskey.entities.SignupResponse;
|
||||||
let roleModerator: any;
|
let roleAdmin: misskey.entities.Role;
|
||||||
let userRolePublic: User;
|
let userModerator: misskey.entities.SignupResponse;
|
||||||
let rolePublic: any;
|
let roleModerator: misskey.entities.Role;
|
||||||
let userRoleBadge: User;
|
let userRolePublic: misskey.entities.SignupResponse;
|
||||||
let roleBadge: any;
|
let rolePublic: misskey.entities.Role;
|
||||||
let userSilenced: User;
|
let userRoleBadge: misskey.entities.SignupResponse;
|
||||||
let roleSilenced: any;
|
let roleBadge: misskey.entities.Role;
|
||||||
let userSuspended: User;
|
let userSilenced: misskey.entities.SignupResponse;
|
||||||
let userDeletedBySelf: User;
|
let roleSilenced: misskey.entities.Role;
|
||||||
let userDeletedByAdmin: User;
|
let userSuspended: misskey.entities.SignupResponse;
|
||||||
let userFollowingAlice: User;
|
let userDeletedBySelf: misskey.entities.SignupResponse;
|
||||||
let userFollowedByAlice: User;
|
let userDeletedByAdmin: misskey.entities.SignupResponse;
|
||||||
let userBlockingAlice: User;
|
let userFollowingAlice: misskey.entities.SignupResponse;
|
||||||
let userBlockedByAlice: User;
|
let userFollowedByAlice: misskey.entities.SignupResponse;
|
||||||
let userMutingAlice: User;
|
let userBlockingAlice: misskey.entities.SignupResponse;
|
||||||
let userMutedByAlice: User;
|
let userBlockedByAlice: misskey.entities.SignupResponse;
|
||||||
let userRnMutingAlice: User;
|
let userMutingAlice: misskey.entities.SignupResponse;
|
||||||
let userRnMutedByAlice: User;
|
let userMutedByAlice: misskey.entities.SignupResponse;
|
||||||
let userFollowRequesting: User;
|
let userRnMutingAlice: misskey.entities.SignupResponse;
|
||||||
let userFollowRequested: User;
|
let userRnMutedByAlice: misskey.entities.SignupResponse;
|
||||||
|
let userFollowRequesting: misskey.entities.SignupResponse;
|
||||||
|
let userFollowRequested: misskey.entities.SignupResponse;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
root = await signup({ username: 'root' });
|
root = await signup({ username: 'root' });
|
||||||
alice = await signup({ username: 'alice' });
|
alice = await signup({ username: 'alice' });
|
||||||
aliceNote = await post(alice, { text: 'test' }) as any;
|
aliceNote = await post(alice, { text: 'test' });
|
||||||
alicePage = await page(alice);
|
|
||||||
aliceList = (await api('users/list/create', { name: 'aliceList' }, alice)).body;
|
|
||||||
bob = await signup({ username: 'bob' });
|
bob = await signup({ username: 'bob' });
|
||||||
bobNote = await post(bob, { text: 'test' }) as any;
|
bobNote = await post(bob, { text: 'test' });
|
||||||
carol = await signup({ username: 'carol' });
|
carol = await signup({ username: 'carol' });
|
||||||
dave = await signup({ username: 'dave' });
|
|
||||||
ellen = await signup({ username: 'ellen' });
|
|
||||||
frank = await signup({ username: 'frank' });
|
|
||||||
|
|
||||||
// @alice -> @replyingへのリプライ。Promise.allで一気に作るとtimeoutしてしまうのでreduceで一つ一つawaitする
|
// @alice -> @replyingへのリプライ。Promise.allで一気に作るとtimeoutしてしまうのでreduceで一つ一つawaitする
|
||||||
usersReplying = await [...Array(10)].map((_, i) => i).reduce(async (acc, i) => {
|
usersReplying = await [...Array(10)].map((_, i) => i).reduce(async (acc, i) => {
|
||||||
|
@ -238,7 +212,7 @@ describe('ユーザー', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (await acc).concat(u);
|
return (await acc).concat(u);
|
||||||
}, Promise.resolve([] as User[]));
|
}, Promise.resolve([] as misskey.entities.SignupResponse[]));
|
||||||
|
|
||||||
userNoNote = await signup({ username: 'userNoNote' });
|
userNoNote = await signup({ username: 'userNoNote' });
|
||||||
userNotExplorable = await signup({ username: 'userNotExplorable' });
|
userNotExplorable = await signup({ username: 'userNotExplorable' });
|
||||||
|
@ -306,7 +280,7 @@ describe('ユーザー', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
alice = {
|
alice = {
|
||||||
...alice,
|
...alice,
|
||||||
...await successfulApiCall({ endpoint: 'i', parameters: {}, user: alice }) as any,
|
...await successfulApiCall({ endpoint: 'i', parameters: {}, user: alice }),
|
||||||
};
|
};
|
||||||
aliceNote = await successfulApiCall({ endpoint: 'notes/show', parameters: { noteId: aliceNote.id }, user: alice });
|
aliceNote = await successfulApiCall({ endpoint: 'notes/show', parameters: { noteId: aliceNote.id }, user: alice });
|
||||||
});
|
});
|
||||||
|
@ -319,7 +293,7 @@ describe('ユーザー', () => {
|
||||||
endpoint: 'signup',
|
endpoint: 'signup',
|
||||||
parameters: { username: 'zoe', password: 'password' },
|
parameters: { username: 'zoe', password: 'password' },
|
||||||
user: undefined,
|
user: undefined,
|
||||||
}) as unknown as User; // BUG MeDetailedに足りないキーがある
|
}) as unknown as misskey.entities.SignupResponse; // BUG MeDetailedに足りないキーがある
|
||||||
|
|
||||||
// signupの時はtokenが含まれる特別なMeDetailedが返ってくる
|
// signupの時はtokenが含まれる特別なMeDetailedが返ってくる
|
||||||
assert.match(response.token, /[a-zA-Z0-9]{16}/);
|
assert.match(response.token, /[a-zA-Z0-9]{16}/);
|
||||||
|
@ -329,7 +303,7 @@ describe('ユーザー', () => {
|
||||||
assert.strictEqual(response.name, null);
|
assert.strictEqual(response.name, null);
|
||||||
assert.strictEqual(response.username, 'zoe');
|
assert.strictEqual(response.username, 'zoe');
|
||||||
assert.strictEqual(response.host, null);
|
assert.strictEqual(response.host, null);
|
||||||
assert.match(response.avatarUrl, /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/);
|
response.avatarUrl && assert.match(response.avatarUrl, /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/);
|
||||||
assert.strictEqual(response.avatarBlurhash, null);
|
assert.strictEqual(response.avatarBlurhash, null);
|
||||||
assert.deepStrictEqual(response.avatarDecorations, []);
|
assert.deepStrictEqual(response.avatarDecorations, []);
|
||||||
assert.strictEqual(response.isBot, false);
|
assert.strictEqual(response.isBot, false);
|
||||||
|
@ -401,6 +375,7 @@ describe('ユーザー', () => {
|
||||||
assert.deepStrictEqual(response.unreadAnnouncements, []);
|
assert.deepStrictEqual(response.unreadAnnouncements, []);
|
||||||
assert.deepStrictEqual(response.mutedWords, []);
|
assert.deepStrictEqual(response.mutedWords, []);
|
||||||
assert.deepStrictEqual(response.mutedInstances, []);
|
assert.deepStrictEqual(response.mutedInstances, []);
|
||||||
|
// @ts-expect-error 後方互換のため
|
||||||
assert.deepStrictEqual(response.mutingNotificationTypes, []);
|
assert.deepStrictEqual(response.mutingNotificationTypes, []);
|
||||||
assert.deepStrictEqual(response.notificationRecieveConfig, {});
|
assert.deepStrictEqual(response.notificationRecieveConfig, {});
|
||||||
assert.deepStrictEqual(response.emailNotificationTypes, ['follow', 'receiveFollowRequest']);
|
assert.deepStrictEqual(response.emailNotificationTypes, ['follow', 'receiveFollowRequest']);
|
||||||
|
@ -430,66 +405,66 @@ describe('ユーザー', () => {
|
||||||
//#region 自分の情報の更新(i/update)
|
//#region 自分の情報の更新(i/update)
|
||||||
|
|
||||||
test.each([
|
test.each([
|
||||||
{ parameters: (): object => ({ name: null }) },
|
{ parameters: () => ({ name: null }) },
|
||||||
{ parameters: (): object => ({ name: 'x'.repeat(50) }) },
|
{ parameters: () => ({ name: 'x'.repeat(50) }) },
|
||||||
{ parameters: (): object => ({ name: 'x' }) },
|
{ parameters: () => ({ name: 'x' }) },
|
||||||
{ parameters: (): object => ({ name: 'My name' }) },
|
{ parameters: () => ({ name: 'My name' }) },
|
||||||
{ parameters: (): object => ({ description: null }) },
|
{ parameters: () => ({ description: null }) },
|
||||||
{ parameters: (): object => ({ description: 'x'.repeat(1500) }) },
|
{ parameters: () => ({ description: 'x'.repeat(1500) }) },
|
||||||
{ parameters: (): object => ({ description: 'x' }) },
|
{ parameters: () => ({ description: 'x' }) },
|
||||||
{ parameters: (): object => ({ description: 'My description' }) },
|
{ parameters: () => ({ description: 'My description' }) },
|
||||||
{ parameters: (): object => ({ location: null }) },
|
{ parameters: () => ({ location: null }) },
|
||||||
{ parameters: (): object => ({ location: 'x'.repeat(50) }) },
|
{ parameters: () => ({ location: 'x'.repeat(50) }) },
|
||||||
{ parameters: (): object => ({ location: 'x' }) },
|
{ parameters: () => ({ location: 'x' }) },
|
||||||
{ parameters: (): object => ({ location: 'My location' }) },
|
{ parameters: () => ({ location: 'My location' }) },
|
||||||
{ parameters: (): object => ({ birthday: '0000-00-00' }) },
|
{ parameters: () => ({ birthday: '0000-00-00' }) },
|
||||||
{ parameters: (): object => ({ birthday: '9999-99-99' }) },
|
{ parameters: () => ({ birthday: '9999-99-99' }) },
|
||||||
{ parameters: (): object => ({ lang: 'en-US' }) },
|
{ parameters: () => ({ lang: 'en-US' as const }) },
|
||||||
{ parameters: (): object => ({ fields: [] }) },
|
{ parameters: () => ({ fields: [] }) },
|
||||||
{ parameters: (): object => ({ fields: [{ name: 'x', value: 'x' }] }) },
|
{ parameters: () => ({ fields: [{ name: 'x', value: 'x' }] }) },
|
||||||
{ parameters: (): object => ({ fields: [{ name: 'x'.repeat(3000), value: 'x'.repeat(3000) }] }) }, // BUG? fieldには制限がない
|
{ parameters: () => ({ fields: [{ name: 'x'.repeat(3000), value: 'x'.repeat(3000) }] }) }, // BUG? fieldには制限がない
|
||||||
{ parameters: (): object => ({ fields: Array(16).fill({ name: 'x', value: 'y' }) }) },
|
{ parameters: () => ({ fields: Array(16).fill({ name: 'x', value: 'y' }) }) },
|
||||||
{ parameters: (): object => ({ isLocked: true }) },
|
{ parameters: () => ({ isLocked: true }) },
|
||||||
{ parameters: (): object => ({ isLocked: false }) },
|
{ parameters: () => ({ isLocked: false }) },
|
||||||
{ parameters: (): object => ({ isExplorable: false }) },
|
{ parameters: () => ({ isExplorable: false }) },
|
||||||
{ parameters: (): object => ({ isExplorable: true }) },
|
{ parameters: () => ({ isExplorable: true }) },
|
||||||
{ parameters: (): object => ({ hideOnlineStatus: true }) },
|
{ parameters: () => ({ hideOnlineStatus: true }) },
|
||||||
{ parameters: (): object => ({ hideOnlineStatus: false }) },
|
{ parameters: () => ({ hideOnlineStatus: false }) },
|
||||||
{ parameters: (): object => ({ publicReactions: false }) },
|
{ parameters: () => ({ publicReactions: false }) },
|
||||||
{ parameters: (): object => ({ publicReactions: true }) },
|
{ parameters: () => ({ publicReactions: true }) },
|
||||||
{ parameters: (): object => ({ autoAcceptFollowed: true }) },
|
{ parameters: () => ({ autoAcceptFollowed: true }) },
|
||||||
{ parameters: (): object => ({ autoAcceptFollowed: false }) },
|
{ parameters: () => ({ autoAcceptFollowed: false }) },
|
||||||
{ parameters: (): object => ({ noCrawle: true }) },
|
{ parameters: () => ({ noCrawle: true }) },
|
||||||
{ parameters: (): object => ({ noCrawle: false }) },
|
{ parameters: () => ({ noCrawle: false }) },
|
||||||
{ parameters: (): object => ({ preventAiLearning: false }) },
|
{ parameters: () => ({ preventAiLearning: false }) },
|
||||||
{ parameters: (): object => ({ preventAiLearning: true }) },
|
{ parameters: () => ({ preventAiLearning: true }) },
|
||||||
{ parameters: (): object => ({ isBot: true }) },
|
{ parameters: () => ({ isBot: true }) },
|
||||||
{ parameters: (): object => ({ isBot: false }) },
|
{ parameters: () => ({ isBot: false }) },
|
||||||
{ parameters: (): object => ({ isCat: true }) },
|
{ parameters: () => ({ isCat: true }) },
|
||||||
{ parameters: (): object => ({ isCat: false }) },
|
{ parameters: () => ({ isCat: false }) },
|
||||||
{ parameters: (): object => ({ injectFeaturedNote: true }) },
|
{ parameters: () => ({ injectFeaturedNote: true }) },
|
||||||
{ parameters: (): object => ({ injectFeaturedNote: false }) },
|
{ parameters: () => ({ injectFeaturedNote: false }) },
|
||||||
{ parameters: (): object => ({ receiveAnnouncementEmail: true }) },
|
{ parameters: () => ({ receiveAnnouncementEmail: true }) },
|
||||||
{ parameters: (): object => ({ receiveAnnouncementEmail: false }) },
|
{ parameters: () => ({ receiveAnnouncementEmail: false }) },
|
||||||
{ parameters: (): object => ({ alwaysMarkNsfw: true }) },
|
{ parameters: () => ({ alwaysMarkNsfw: true }) },
|
||||||
{ parameters: (): object => ({ alwaysMarkNsfw: false }) },
|
{ parameters: () => ({ alwaysMarkNsfw: false }) },
|
||||||
{ parameters: (): object => ({ autoSensitive: true }) },
|
{ parameters: () => ({ autoSensitive: true }) },
|
||||||
{ parameters: (): object => ({ autoSensitive: false }) },
|
{ parameters: () => ({ autoSensitive: false }) },
|
||||||
{ parameters: (): object => ({ followingVisibility: 'private' }) },
|
{ parameters: () => ({ followingVisibility: 'private' as const }) },
|
||||||
{ parameters: (): object => ({ followingVisibility: 'followers' }) },
|
{ parameters: () => ({ followingVisibility: 'followers' as const }) },
|
||||||
{ parameters: (): object => ({ followingVisibility: 'public' }) },
|
{ parameters: () => ({ followingVisibility: 'public' as const }) },
|
||||||
{ parameters: (): object => ({ followersVisibility: 'private' }) },
|
{ parameters: () => ({ followersVisibility: 'private' as const }) },
|
||||||
{ parameters: (): object => ({ followersVisibility: 'followers' }) },
|
{ parameters: () => ({ followersVisibility: 'followers' as const }) },
|
||||||
{ parameters: (): object => ({ followersVisibility: 'public' }) },
|
{ parameters: () => ({ followersVisibility: 'public' as const }) },
|
||||||
{ parameters: (): object => ({ mutedWords: Array(19).fill(['xxxxx']) }) },
|
{ parameters: () => ({ mutedWords: Array(19).fill(['xxxxx']) }) },
|
||||||
{ parameters: (): object => ({ mutedWords: [['x'.repeat(194)]] }) },
|
{ parameters: () => ({ mutedWords: [['x'.repeat(194)]] }) },
|
||||||
{ parameters: (): object => ({ mutedWords: [] }) },
|
{ parameters: () => ({ mutedWords: [] }) },
|
||||||
{ parameters: (): object => ({ mutedInstances: ['xxxx.xxxxx'] }) },
|
{ parameters: () => ({ mutedInstances: ['xxxx.xxxxx'] }) },
|
||||||
{ parameters: (): object => ({ mutedInstances: [] }) },
|
{ parameters: () => ({ mutedInstances: [] }) },
|
||||||
{ parameters: (): object => ({ notificationRecieveConfig: { mention: { type: 'following' } } }) },
|
{ parameters: () => ({ notificationRecieveConfig: { mention: { type: 'following' } } }) },
|
||||||
{ parameters: (): object => ({ notificationRecieveConfig: {} }) },
|
{ parameters: () => ({ notificationRecieveConfig: {} }) },
|
||||||
{ parameters: (): object => ({ emailNotificationTypes: ['mention', 'reply', 'quote', 'follow', 'receiveFollowRequest'] }) },
|
{ parameters: () => ({ emailNotificationTypes: ['mention', 'reply', 'quote', 'follow', 'receiveFollowRequest'] }) },
|
||||||
{ parameters: (): object => ({ emailNotificationTypes: [] }) },
|
{ parameters: () => ({ emailNotificationTypes: [] }) },
|
||||||
] as const)('を書き換えることができる($#)', async ({ parameters }) => {
|
] as const)('を書き換えることができる($#)', async ({ parameters }) => {
|
||||||
const response = await successfulApiCall({ endpoint: 'i/update', parameters: parameters(), user: alice });
|
const response = await successfulApiCall({ endpoint: 'i/update', parameters: parameters(), user: alice });
|
||||||
const expected = { ...meDetailed(alice, true), ...parameters() };
|
const expected = { ...meDetailed(alice, true), ...parameters() };
|
||||||
|
@ -498,13 +473,13 @@ describe('ユーザー', () => {
|
||||||
|
|
||||||
test('を書き換えることができる(Avatar)', async () => {
|
test('を書き換えることができる(Avatar)', async () => {
|
||||||
const aliceFile = (await uploadFile(alice)).body;
|
const aliceFile = (await uploadFile(alice)).body;
|
||||||
const parameters = { avatarId: aliceFile.id };
|
const parameters = { avatarId: aliceFile!.id };
|
||||||
const response = await successfulApiCall({ endpoint: 'i/update', parameters: parameters, user: alice });
|
const response = await successfulApiCall({ endpoint: 'i/update', parameters: parameters, user: alice });
|
||||||
assert.match(response.avatarUrl ?? '.', /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/);
|
assert.match(response.avatarUrl ?? '.', /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/);
|
||||||
assert.match(response.avatarBlurhash ?? '.', /[ -~]{54}/);
|
assert.match(response.avatarBlurhash ?? '.', /[ -~]{54}/);
|
||||||
const expected = {
|
const expected = {
|
||||||
...meDetailed(alice, true),
|
...meDetailed(alice, true),
|
||||||
avatarId: aliceFile.id,
|
avatarId: aliceFile!.id,
|
||||||
avatarBlurhash: response.avatarBlurhash,
|
avatarBlurhash: response.avatarBlurhash,
|
||||||
avatarUrl: response.avatarUrl,
|
avatarUrl: response.avatarUrl,
|
||||||
};
|
};
|
||||||
|
@ -523,13 +498,13 @@ describe('ユーザー', () => {
|
||||||
|
|
||||||
test('を書き換えることができる(Banner)', async () => {
|
test('を書き換えることができる(Banner)', async () => {
|
||||||
const aliceFile = (await uploadFile(alice)).body;
|
const aliceFile = (await uploadFile(alice)).body;
|
||||||
const parameters = { bannerId: aliceFile.id };
|
const parameters = { bannerId: aliceFile!.id };
|
||||||
const response = await successfulApiCall({ endpoint: 'i/update', parameters: parameters, user: alice });
|
const response = await successfulApiCall({ endpoint: 'i/update', parameters: parameters, user: alice });
|
||||||
assert.match(response.bannerUrl ?? '.', /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/);
|
assert.match(response.bannerUrl ?? '.', /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/);
|
||||||
assert.match(response.bannerBlurhash ?? '.', /[ -~]{54}/);
|
assert.match(response.bannerBlurhash ?? '.', /[ -~]{54}/);
|
||||||
const expected = {
|
const expected = {
|
||||||
...meDetailed(alice, true),
|
...meDetailed(alice, true),
|
||||||
bannerId: aliceFile.id,
|
bannerId: aliceFile!.id,
|
||||||
bannerBlurhash: response.bannerBlurhash,
|
bannerBlurhash: response.bannerBlurhash,
|
||||||
bannerUrl: response.bannerUrl,
|
bannerUrl: response.bannerUrl,
|
||||||
};
|
};
|
||||||
|
@ -579,13 +554,13 @@ describe('ユーザー', () => {
|
||||||
//#region ユーザー(users)
|
//#region ユーザー(users)
|
||||||
|
|
||||||
test.each([
|
test.each([
|
||||||
{ label: 'ID昇順', parameters: { limit: 5 }, selector: (u: UserLite): string => u.id },
|
{ label: 'ID昇順', parameters: { limit: 5 }, selector: (u: misskey.entities.UserLite): string => u.id },
|
||||||
{ label: 'フォロワー昇順', parameters: { sort: '+follower' }, selector: (u: UserDetailedNotMe): string => String(u.followersCount) },
|
{ label: 'フォロワー昇順', parameters: { sort: '+follower' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.followersCount) },
|
||||||
{ label: 'フォロワー降順', parameters: { sort: '-follower' }, selector: (u: UserDetailedNotMe): string => String(u.followersCount) },
|
{ label: 'フォロワー降順', parameters: { sort: '-follower' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.followersCount) },
|
||||||
{ label: '登録日時昇順', parameters: { sort: '+createdAt' }, selector: (u: UserDetailedNotMe): string => u.createdAt },
|
{ label: '登録日時昇順', parameters: { sort: '+createdAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => u.createdAt },
|
||||||
{ label: '登録日時降順', parameters: { sort: '-createdAt' }, selector: (u: UserDetailedNotMe): string => u.createdAt },
|
{ label: '登録日時降順', parameters: { sort: '-createdAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => u.createdAt },
|
||||||
{ label: '投稿日時昇順', parameters: { sort: '+updatedAt' }, selector: (u: UserDetailedNotMe): string => String(u.updatedAt) },
|
{ label: '投稿日時昇順', parameters: { sort: '+updatedAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.updatedAt) },
|
||||||
{ label: '投稿日時降順', parameters: { sort: '-updatedAt' }, selector: (u: UserDetailedNotMe): string => String(u.updatedAt) },
|
{ label: '投稿日時降順', parameters: { sort: '-updatedAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.updatedAt) },
|
||||||
] as const)('をリスト形式で取得することができる($label)', async ({ parameters, selector }) => {
|
] as const)('をリスト形式で取得することができる($label)', async ({ parameters, selector }) => {
|
||||||
const response = await successfulApiCall({ endpoint: 'users', parameters, user: alice });
|
const response = await successfulApiCall({ endpoint: 'users', parameters, user: alice });
|
||||||
|
|
||||||
|
@ -598,15 +573,15 @@ describe('ユーザー', () => {
|
||||||
assert.deepStrictEqual(response, expected);
|
assert.deepStrictEqual(response, expected);
|
||||||
});
|
});
|
||||||
test.each([
|
test.each([
|
||||||
{ label: '「見つけやすくする」がOFFのユーザーが含まれない', user: (): User => userNotExplorable, excluded: true },
|
{ label: '「見つけやすくする」がOFFのユーザーが含まれない', user: () => userNotExplorable, excluded: true },
|
||||||
{ label: 'ミュートユーザーが含まれない', user: (): User => userMutedByAlice, excluded: true },
|
{ label: 'ミュートユーザーが含まれない', user: () => userMutedByAlice, excluded: true },
|
||||||
{ label: 'ブロックされているユーザーが含まれない', user: (): User => userBlockedByAlice, excluded: true },
|
{ label: 'ブロックされているユーザーが含まれない', user: () => userBlockedByAlice, excluded: true },
|
||||||
{ label: 'ブロックしてきているユーザーが含まれる', user: (): User => userBlockingAlice, excluded: true },
|
{ label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice, excluded: true },
|
||||||
{ label: '承認制ユーザーが含まれる', user: (): User => userLocking },
|
{ label: '承認制ユーザーが含まれる', user: () => userLocking },
|
||||||
{ label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced },
|
{ label: 'サイレンスユーザーが含まれる', user: () => userSilenced },
|
||||||
{ label: 'サスペンドユーザーが含まれない', user: (): User => userSuspended, excluded: true },
|
{ label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true },
|
||||||
{ label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf },
|
{ label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf },
|
||||||
{ label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin },
|
{ label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin },
|
||||||
] as const)('をリスト形式で取得することができ、結果に$label', async ({ user, excluded }) => {
|
] as const)('をリスト形式で取得することができ、結果に$label', async ({ user, excluded }) => {
|
||||||
const parameters = { limit: 100 };
|
const parameters = { limit: 100 };
|
||||||
const response = await successfulApiCall({ endpoint: 'users', parameters, user: alice });
|
const response = await successfulApiCall({ endpoint: 'users', parameters, user: alice });
|
||||||
|
@ -620,39 +595,44 @@ describe('ユーザー', () => {
|
||||||
//#region ユーザー情報(users/show)
|
//#region ユーザー情報(users/show)
|
||||||
|
|
||||||
test.each([
|
test.each([
|
||||||
{ label: 'ID指定で自分自身を', parameters: (): object => ({ userId: alice.id }), user: (): User => alice, type: meDetailed },
|
{ label: 'ID指定で自分自身を', parameters: () => ({ userId: alice.id }), user: () => alice, type: meDetailed },
|
||||||
{ label: 'ID指定で他人を', parameters: (): object => ({ userId: alice.id }), user: (): User => bob, type: userDetailedNotMeWithRelations },
|
{ label: 'ID指定で他人を', parameters: () => ({ userId: alice.id }), user: () => bob, type: userDetailedNotMeWithRelations },
|
||||||
{ label: 'ID指定かつ未認証', parameters: (): object => ({ userId: alice.id }), user: undefined, type: userDetailedNotMe },
|
{ label: 'ID指定かつ未認証', parameters: () => ({ userId: alice.id }), user: undefined, type: userDetailedNotMe },
|
||||||
{ label: '@指定で自分自身を', parameters: (): object => ({ username: alice.username }), user: (): User => alice, type: meDetailed },
|
{ label: '@指定で自分自身を', parameters: () => ({ username: alice.username }), user: () => alice, type: meDetailed },
|
||||||
{ label: '@指定で他人を', parameters: (): object => ({ username: alice.username }), user: (): User => bob, type: userDetailedNotMeWithRelations },
|
{ label: '@指定で他人を', parameters: () => ({ username: alice.username }), user: () => bob, type: userDetailedNotMeWithRelations },
|
||||||
{ label: '@指定かつ未認証', parameters: (): object => ({ username: alice.username }), user: undefined, type: userDetailedNotMe },
|
{ label: '@指定かつ未認証', parameters: () => ({ username: alice.username }), user: undefined, type: userDetailedNotMe },
|
||||||
] as const)('を取得することができる($label)', async ({ parameters, user, type }) => {
|
] as const)('を取得することができる($label)', async ({ parameters, user, type }) => {
|
||||||
const response = await successfulApiCall({ endpoint: 'users/show', parameters: parameters(), user: user?.() });
|
const response = await successfulApiCall({ endpoint: 'users/show', parameters: parameters(), user: user?.() });
|
||||||
const expected = type(alice);
|
const expected = type(alice);
|
||||||
assert.deepStrictEqual(response, expected);
|
assert.deepStrictEqual(response, expected);
|
||||||
});
|
});
|
||||||
test.each([
|
test.each([
|
||||||
{ label: 'Administratorになっている', user: (): User => userAdmin, me: (): User => userAdmin, selector: (user: User): unknown => user.isAdmin },
|
{ label: 'Administratorになっている', user: () => userAdmin, me: () => userAdmin, selector: (user: misskey.entities.MeDetailed) => user.isAdmin },
|
||||||
{ label: '自分以外から見たときはAdministratorか判定できない', user: (): User => userAdmin, selector: (user: User): unknown => user.isAdmin, expected: (): undefined => undefined },
|
// @ts-expect-error UserDetailedNotMe doesn't include isAdmin
|
||||||
{ label: 'Moderatorになっている', user: (): User => userModerator, me: (): User => userModerator, selector: (user: User): unknown => user.isModerator },
|
{ label: '自分以外から見たときはAdministratorか判定できない', user: () => userAdmin, selector: (user: misskey.entities.UserDetailedNotMe) => user.isAdmin, expected: () => undefined },
|
||||||
{ label: '自分以外から見たときはModeratorか判定できない', user: (): User => userModerator, selector: (user: User): unknown => user.isModerator, expected: (): undefined => undefined },
|
{ label: 'Moderatorになっている', user: () => userModerator, me: () => userModerator, selector: (user: misskey.entities.MeDetailed) => user.isModerator },
|
||||||
{ label: 'サイレンスになっている', user: (): User => userSilenced, selector: (user: User): unknown => user.isSilenced },
|
// @ts-expect-error UserDetailedNotMe doesn't include isModerator
|
||||||
//{ label: 'サスペンドになっている', user: (): User => userSuspended, selector: (user: User): unknown => user.isSuspended },
|
{ label: '自分以外から見たときはModeratorか判定できない', user: () => userModerator, selector: (user: misskey.entities.UserDetailedNotMe) => user.isModerator, expected: () => undefined },
|
||||||
{ label: '削除済みになっている', user: (): User => userDeletedBySelf, me: (): User => userDeletedBySelf, selector: (user: User): unknown => user.isDeleted },
|
{ label: 'サイレンスになっている', user: () => userSilenced, selector: (user: misskey.entities.UserDetailed) => user.isSilenced },
|
||||||
{ label: '自分以外から見たときは削除済みか判定できない', user: (): User => userDeletedBySelf, selector: (user: User): unknown => user.isDeleted, expected: (): undefined => undefined },
|
// FIXME: 落ちる
|
||||||
{ label: '削除済み(byAdmin)になっている', user: (): User => userDeletedByAdmin, me: (): User => userDeletedByAdmin, selector: (user: User): unknown => user.isDeleted },
|
//{ label: 'サスペンドになっている', user: () => userSuspended, selector: (user: misskey.entities.UserDetailed) => user.isSuspended },
|
||||||
{ label: '自分以外から見たときは削除済み(byAdmin)か判定できない', user: (): User => userDeletedByAdmin, selector: (user: User): unknown => user.isDeleted, expected: (): undefined => undefined },
|
{ label: '削除済みになっている', user: () => userDeletedBySelf, me: () => userDeletedBySelf, selector: (user: misskey.entities.MeDetailed) => user.isDeleted },
|
||||||
{ label: 'フォロー中になっている', user: (): User => userFollowedByAlice, selector: (user: User): unknown => user.isFollowing },
|
// @ts-expect-error UserDetailedNotMe doesn't include isDeleted
|
||||||
{ label: 'フォローされている', user: (): User => userFollowingAlice, selector: (user: User): unknown => user.isFollowed },
|
{ label: '自分以外から見たときは削除済みか判定できない', user: () => userDeletedBySelf, selector: (user: misskey.entities.UserDetailedNotMe) => user.isDeleted, expected: () => undefined },
|
||||||
{ label: 'ブロック中になっている', user: (): User => userBlockedByAlice, selector: (user: User): unknown => user.isBlocking },
|
{ label: '削除済み(byAdmin)になっている', user: () => userDeletedByAdmin, me: () => userDeletedByAdmin, selector: (user: misskey.entities.MeDetailed) => user.isDeleted },
|
||||||
{ label: 'ブロックされている', user: (): User => userBlockingAlice, selector: (user: User): unknown => user.isBlocked },
|
// @ts-expect-error UserDetailedNotMe doesn't include isDeleted
|
||||||
{ label: 'ミュート中になっている', user: (): User => userMutedByAlice, selector: (user: User): unknown => user.isMuted },
|
{ label: '自分以外から見たときは削除済み(byAdmin)か判定できない', user: () => userDeletedByAdmin, selector: (user: misskey.entities.UserDetailedNotMe) => user.isDeleted, expected: () => undefined },
|
||||||
{ label: 'リノートミュート中になっている', user: (): User => userRnMutedByAlice, selector: (user: User): unknown => user.isRenoteMuted },
|
{ label: 'フォロー中になっている', user: () => userFollowedByAlice, selector: (user: misskey.entities.UserDetailed) => user.isFollowing },
|
||||||
{ label: 'フォローリクエスト中になっている', user: (): User => userFollowRequested, me: (): User => userFollowRequesting, selector: (user: User): unknown => user.hasPendingFollowRequestFromYou },
|
{ label: 'フォローされている', user: () => userFollowingAlice, selector: (user: misskey.entities.UserDetailed) => user.isFollowed },
|
||||||
{ label: 'フォローリクエストされている', user: (): User => userFollowRequesting, me: (): User => userFollowRequested, selector: (user: User): unknown => user.hasPendingFollowRequestToYou },
|
{ 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 },
|
||||||
] as const)('を取得することができ、$labelこと', async ({ user, me, selector, expected }) => {
|
] as const)('を取得することができ、$labelこと', async ({ user, me, selector, expected }) => {
|
||||||
const response = await successfulApiCall({ endpoint: 'users/show', parameters: { userId: user().id }, user: me?.() ?? alice });
|
const response = await successfulApiCall({ endpoint: 'users/show', parameters: { userId: user().id }, user: me?.() ?? alice });
|
||||||
assert.strictEqual(selector(response), (expected ?? ((): true => true))());
|
assert.strictEqual(selector(response as any), (expected ?? ((): true => true))());
|
||||||
});
|
});
|
||||||
test('を取得することができ、Publicなロールがセットされていること', async () => {
|
test('を取得することができ、Publicなロールがセットされていること', async () => {
|
||||||
const response = await successfulApiCall({ endpoint: 'users/show', parameters: { userId: userRolePublic.id }, user: alice });
|
const response = await successfulApiCall({ endpoint: 'users/show', parameters: { userId: userRolePublic.id }, user: alice });
|
||||||
|
@ -694,17 +674,18 @@ describe('ユーザー', () => {
|
||||||
assert.deepStrictEqual(response, expected);
|
assert.deepStrictEqual(response, expected);
|
||||||
});
|
});
|
||||||
test.each([
|
test.each([
|
||||||
{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: (): User => userNotExplorable },
|
{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable },
|
||||||
{ label: 'ミュートユーザーが含まれる', user: (): User => userMutedByAlice },
|
{ label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice },
|
||||||
{ label: 'ブロックされているユーザーが含まれる', user: (): User => userBlockedByAlice },
|
{ label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice },
|
||||||
{ label: 'ブロックしてきているユーザーが含まれる', user: (): User => userBlockingAlice },
|
{ label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice },
|
||||||
{ label: '承認制ユーザーが含まれる', user: (): User => userLocking },
|
{ label: '承認制ユーザーが含まれる', user: () => userLocking },
|
||||||
{ label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced },
|
{ label: 'サイレンスユーザーが含まれる', user: () => userSilenced },
|
||||||
{ label: 'サスペンドユーザーが(モデレーターが見るときは)含まれる', user: (): User => userSuspended, me: (): User => root },
|
{ label: 'サスペンドユーザーが(モデレーターが見るときは)含まれる', user: () => userSuspended, me: () => root },
|
||||||
// BUG サスペンドユーザーを一般ユーザーから見るとrootユーザーが返ってくる
|
// BUG サスペンドユーザーを一般ユーザーから見るとrootユーザーが返ってくる
|
||||||
//{ label: 'サスペンドユーザーが(一般ユーザーが見るときは)含まれない', user: (): User => userSuspended, me: (): User => bob, excluded: true },
|
//{ label: 'サスペンドユーザーが(一般ユーザーが見るときは)含まれない', user: () => userSuspended, me: () => bob, excluded: true },
|
||||||
{ label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf },
|
{ label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf },
|
||||||
{ label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin },
|
{ label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin },
|
||||||
|
// @ts-expect-error excluded は上でコメントアウトされているので
|
||||||
] as const)('をID指定のリスト形式で取得することができ、結果に$label', async ({ user, me, excluded }) => {
|
] as const)('をID指定のリスト形式で取得することができ、結果に$label', async ({ user, me, excluded }) => {
|
||||||
const parameters = { userIds: [user().id] };
|
const parameters = { userIds: [user().id] };
|
||||||
const response = await successfulApiCall({ endpoint: 'users/show', parameters, user: me?.() ?? alice });
|
const response = await successfulApiCall({ endpoint: 'users/show', parameters, user: me?.() ?? alice });
|
||||||
|
@ -729,15 +710,15 @@ describe('ユーザー', () => {
|
||||||
assert.deepStrictEqual(response, expected);
|
assert.deepStrictEqual(response, expected);
|
||||||
});
|
});
|
||||||
test.each([
|
test.each([
|
||||||
{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: (): User => userNotExplorable },
|
{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable },
|
||||||
{ label: 'ミュートユーザーが含まれる', user: (): User => userMutedByAlice },
|
{ label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice },
|
||||||
{ label: 'ブロックされているユーザーが含まれる', user: (): User => userBlockedByAlice },
|
{ label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice },
|
||||||
{ label: 'ブロックしてきているユーザーが含まれる', user: (): User => userBlockingAlice },
|
{ label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice },
|
||||||
{ label: '承認制ユーザーが含まれる', user: (): User => userLocking },
|
{ label: '承認制ユーザーが含まれる', user: () => userLocking },
|
||||||
{ label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced },
|
{ label: 'サイレンスユーザーが含まれる', user: () => userSilenced },
|
||||||
{ label: 'サスペンドユーザーが含まれない', user: (): User => userSuspended, excluded: true },
|
{ label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true },
|
||||||
{ label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf },
|
{ label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf },
|
||||||
{ label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin },
|
{ label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin },
|
||||||
] as const)('を検索することができ、結果に$labelが含まれる', async ({ user, excluded }) => {
|
] as const)('を検索することができ、結果に$labelが含まれる', async ({ user, excluded }) => {
|
||||||
const parameters = { query: user().username, limit: 1 };
|
const parameters = { query: user().username, limit: 1 };
|
||||||
const response = await successfulApiCall({ endpoint: 'users/search', parameters, user: alice });
|
const response = await successfulApiCall({ endpoint: 'users/search', parameters, user: alice });
|
||||||
|
@ -751,30 +732,30 @@ describe('ユーザー', () => {
|
||||||
//#region ID指定検索(users/search-by-username-and-host)
|
//#region ID指定検索(users/search-by-username-and-host)
|
||||||
|
|
||||||
test.each([
|
test.each([
|
||||||
{ label: '自分', parameters: { username: 'alice' }, user: (): User[] => [alice] },
|
{ label: '自分', parameters: { username: 'alice' }, user: () => [alice] },
|
||||||
{ label: '自分かつusernameが大文字', parameters: { username: 'ALICE' }, user: (): User[] => [alice] },
|
{ label: '自分かつusernameが大文字', parameters: { username: 'ALICE' }, user: () => [alice] },
|
||||||
{ label: 'ローカルのフォロイーでノートなし', parameters: { username: 'userFollowedByAlice' }, user: (): User[] => [userFollowedByAlice] },
|
{ label: 'ローカルのフォロイーでノートなし', parameters: { username: 'userFollowedByAlice' }, user: () => [userFollowedByAlice] },
|
||||||
{ label: 'ローカルでノートなしは検索に載らない', parameters: { username: 'userNoNote' }, user: (): User[] => [] },
|
{ label: 'ローカルでノートなしは検索に載らない', parameters: { username: 'userNoNote' }, user: () => [] },
|
||||||
{ label: 'ローカルの他人1', parameters: { username: 'bob' }, user: (): User[] => [bob] },
|
{ label: 'ローカルの他人1', parameters: { username: 'bob' }, user: () => [bob] },
|
||||||
{ label: 'ローカルの他人2', parameters: { username: 'bob', host: null }, user: (): User[] => [bob] },
|
{ label: 'ローカルの他人2', parameters: { username: 'bob', host: null }, user: () => [bob] },
|
||||||
{ label: 'ローカルの他人3', parameters: { username: 'bob', host: '.' }, user: (): User[] => [bob] },
|
{ label: 'ローカルの他人3', parameters: { username: 'bob', host: '.' }, user: () => [bob] },
|
||||||
{ label: 'ローカル', parameters: { host: null, limit: 1 }, user: (): User[] => [userFollowedByAlice] },
|
{ label: 'ローカル', parameters: { host: null, limit: 1 }, user: () => [userFollowedByAlice] },
|
||||||
{ label: 'ローカル', parameters: { host: '.', limit: 1 }, user: (): User[] => [userFollowedByAlice] },
|
{ label: 'ローカル', parameters: { host: '.', limit: 1 }, user: () => [userFollowedByAlice] },
|
||||||
])('をID&ホスト指定で検索できる($label)', async ({ parameters, user }) => {
|
])('をID&ホスト指定で検索できる($label)', async ({ parameters, user }) => {
|
||||||
const response = await successfulApiCall({ endpoint: 'users/search-by-username-and-host', parameters, user: alice });
|
const response = await successfulApiCall({ endpoint: 'users/search-by-username-and-host', parameters, user: alice });
|
||||||
const expected = await Promise.all(user().map(u => show(u.id, alice)));
|
const expected = await Promise.all(user().map(u => show(u.id, alice)));
|
||||||
assert.deepStrictEqual(response, expected);
|
assert.deepStrictEqual(response, expected);
|
||||||
});
|
});
|
||||||
test.each([
|
test.each([
|
||||||
{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: (): User => userNotExplorable },
|
{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable },
|
||||||
{ label: 'ミュートユーザーが含まれる', user: (): User => userMutedByAlice },
|
{ label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice },
|
||||||
{ label: 'ブロックされているユーザーが含まれる', user: (): User => userBlockedByAlice },
|
{ label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice },
|
||||||
{ label: 'ブロックしてきているユーザーが含まれる', user: (): User => userBlockingAlice },
|
{ label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice },
|
||||||
{ label: '承認制ユーザーが含まれる', user: (): User => userLocking },
|
{ label: '承認制ユーザーが含まれる', user: () => userLocking },
|
||||||
{ label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced },
|
{ label: 'サイレンスユーザーが含まれる', user: () => userSilenced },
|
||||||
{ label: 'サスペンドユーザーが含まれない', user: (): User => userSuspended, excluded: true },
|
{ label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true },
|
||||||
{ label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf },
|
{ label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf },
|
||||||
{ label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin },
|
{ label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin },
|
||||||
] as const)('をID&ホスト指定で検索でき、結果に$label', async ({ user, excluded }) => {
|
] as const)('をID&ホスト指定で検索でき、結果に$label', async ({ user, excluded }) => {
|
||||||
const parameters = { username: user().username };
|
const parameters = { username: user().username };
|
||||||
const response = await successfulApiCall({ endpoint: 'users/search-by-username-and-host', parameters, user: alice });
|
const response = await successfulApiCall({ endpoint: 'users/search-by-username-and-host', parameters, user: alice });
|
||||||
|
@ -796,15 +777,15 @@ describe('ユーザー', () => {
|
||||||
assert.deepStrictEqual(response, expected);
|
assert.deepStrictEqual(response, expected);
|
||||||
});
|
});
|
||||||
test.each([
|
test.each([
|
||||||
{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: (): User => userNotExplorable },
|
{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable },
|
||||||
{ label: 'ミュートユーザーが含まれる', user: (): User => userMutedByAlice },
|
{ label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice },
|
||||||
{ label: 'ブロックされているユーザーが含まれる', user: (): User => userBlockedByAlice },
|
{ label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice },
|
||||||
{ label: 'ブロックしてきているユーザーが含まれない', user: (): User => userBlockingAlice, excluded: true },
|
{ label: 'ブロックしてきているユーザーが含まれない', user: () => userBlockingAlice, excluded: true },
|
||||||
{ label: '承認制ユーザーが含まれる', user: (): User => userLocking },
|
{ label: '承認制ユーザーが含まれる', user: () => userLocking },
|
||||||
{ label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced },
|
{ label: 'サイレンスユーザーが含まれる', user: () => userSilenced },
|
||||||
//{ label: 'サスペンドユーザーが含まれない', user: (): User => userSuspended, excluded: true },
|
//{ label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true },
|
||||||
{ label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf },
|
{ label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf },
|
||||||
{ label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin },
|
{ label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin },
|
||||||
] as const)('がよくリプライをするユーザーのリストを取得でき、結果に$label', async ({ user, excluded }) => {
|
] as const)('がよくリプライをするユーザーのリストを取得でき、結果に$label', async ({ user, excluded }) => {
|
||||||
const replyTo = (await successfulApiCall({ endpoint: 'users/notes', parameters: { userId: user().id }, user: undefined }))[0];
|
const replyTo = (await successfulApiCall({ endpoint: 'users/notes', parameters: { userId: user().id }, user: undefined }))[0];
|
||||||
await post(alice, { text: `@${user().username} test`, replyId: replyTo.id });
|
await post(alice, { text: `@${user().username} test`, replyId: replyTo.id });
|
||||||
|
@ -818,12 +799,12 @@ describe('ユーザー', () => {
|
||||||
//#region ハッシュタグ(hashtags/users)
|
//#region ハッシュタグ(hashtags/users)
|
||||||
|
|
||||||
test.each([
|
test.each([
|
||||||
{ label: 'フォロワー昇順', sort: { sort: '+follower' }, selector: (u: UserDetailedNotMe): string => String(u.followersCount) },
|
{ label: 'フォロワー昇順', sort: { sort: '+follower' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.followersCount) },
|
||||||
{ label: 'フォロワー降順', sort: { sort: '-follower' }, selector: (u: UserDetailedNotMe): string => String(u.followersCount) },
|
{ label: 'フォロワー降順', sort: { sort: '-follower' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.followersCount) },
|
||||||
{ label: '登録日時昇順', sort: { sort: '+createdAt' }, selector: (u: UserDetailedNotMe): string => u.createdAt },
|
{ label: '登録日時昇順', sort: { sort: '+createdAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => u.createdAt },
|
||||||
{ label: '登録日時降順', sort: { sort: '-createdAt' }, selector: (u: UserDetailedNotMe): string => u.createdAt },
|
{ label: '登録日時降順', sort: { sort: '-createdAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => u.createdAt },
|
||||||
{ label: '投稿日時昇順', sort: { sort: '+updatedAt' }, selector: (u: UserDetailedNotMe): string => String(u.updatedAt) },
|
{ label: '投稿日時昇順', sort: { sort: '+updatedAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.updatedAt) },
|
||||||
{ label: '投稿日時降順', sort: { sort: '-updatedAt' }, selector: (u: UserDetailedNotMe): string => String(u.updatedAt) },
|
{ label: '投稿日時降順', sort: { sort: '-updatedAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.updatedAt) },
|
||||||
] as const)('をハッシュタグ指定で取得することができる($label)', async ({ sort, selector }) => {
|
] as const)('をハッシュタグ指定で取得することができる($label)', async ({ sort, selector }) => {
|
||||||
const hashtag = 'test_hashtag';
|
const hashtag = 'test_hashtag';
|
||||||
await successfulApiCall({ endpoint: 'i/update', parameters: { description: `#${hashtag}` }, user: alice });
|
await successfulApiCall({ endpoint: 'i/update', parameters: { description: `#${hashtag}` }, user: alice });
|
||||||
|
@ -837,15 +818,15 @@ describe('ユーザー', () => {
|
||||||
assert.deepStrictEqual(response, expected);
|
assert.deepStrictEqual(response, expected);
|
||||||
});
|
});
|
||||||
test.each([
|
test.each([
|
||||||
{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: (): User => userNotExplorable },
|
{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable },
|
||||||
{ label: 'ミュートユーザーが含まれる', user: (): User => userMutedByAlice },
|
{ label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice },
|
||||||
{ label: 'ブロックされているユーザーが含まれる', user: (): User => userBlockedByAlice },
|
{ label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice },
|
||||||
{ label: 'ブロックしてきているユーザーが含まれる', user: (): User => userBlockingAlice },
|
{ label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice },
|
||||||
{ label: '承認制ユーザーが含まれる', user: (): User => userLocking },
|
{ label: '承認制ユーザーが含まれる', user: () => userLocking },
|
||||||
{ label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced },
|
{ label: 'サイレンスユーザーが含まれる', user: () => userSilenced },
|
||||||
{ label: 'サスペンドユーザーが含まれない', user: (): User => userSuspended, excluded: true },
|
{ label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true },
|
||||||
{ label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf },
|
{ label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf },
|
||||||
{ label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin },
|
{ label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin },
|
||||||
] as const)('をハッシュタグ指定で取得することができ、結果に$label', async ({ user, excluded }) => {
|
] as const)('をハッシュタグ指定で取得することができ、結果に$label', async ({ user, excluded }) => {
|
||||||
const hashtag = `user_test${user().username}`;
|
const hashtag = `user_test${user().username}`;
|
||||||
if (user() !== userSuspended) {
|
if (user() !== userSuspended) {
|
||||||
|
|
|
@ -51,7 +51,7 @@ describe('AnnouncementService', () => {
|
||||||
|
|
||||||
function createAnnouncement(data: Partial<MiAnnouncement & { createdAt: Date }> = {}) {
|
function createAnnouncement(data: Partial<MiAnnouncement & { createdAt: Date }> = {}) {
|
||||||
return announcementsRepository.insert({
|
return announcementsRepository.insert({
|
||||||
id: genAidx(data.createdAt ?? new Date()),
|
id: genAidx(data.createdAt?.getTime() ?? Date.now()),
|
||||||
updatedAt: null,
|
updatedAt: null,
|
||||||
title: 'Title',
|
title: 'Title',
|
||||||
text: 'Text',
|
text: 'Text',
|
||||||
|
|
|
@ -19,8 +19,8 @@ import { DI } from '@/di-symbols.js';
|
||||||
import type { TestingModule } from '@nestjs/testing';
|
import type { TestingModule } from '@nestjs/testing';
|
||||||
|
|
||||||
function mockRedis() {
|
function mockRedis() {
|
||||||
const hash = {};
|
const hash = {} as any;
|
||||||
const set = jest.fn((key, value) => {
|
const set = jest.fn((key: string, value) => {
|
||||||
const ret = hash[key];
|
const ret = hash[key];
|
||||||
hash[key] = value;
|
hash[key] = value;
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -56,12 +56,13 @@ describe('FetchInstanceMetadataService', () => {
|
||||||
} else if (token === DI.redis) {
|
} else if (token === DI.redis) {
|
||||||
return mockRedis;
|
return mockRedis;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
})
|
})
|
||||||
.compile();
|
.compile();
|
||||||
|
|
||||||
app.enableShutdownHooks();
|
app.enableShutdownHooks();
|
||||||
|
|
||||||
fetchInstanceMetadataService = app.get<FetchInstanceMetadataService>(FetchInstanceMetadataService);
|
fetchInstanceMetadataService = app.get<FetchInstanceMetadataService>(FetchInstanceMetadataService) as jest.Mocked<FetchInstanceMetadataService>;
|
||||||
federatedInstanceService = app.get<FederatedInstanceService>(FederatedInstanceService) as jest.Mocked<FederatedInstanceService>;
|
federatedInstanceService = app.get<FederatedInstanceService>(FederatedInstanceService) as jest.Mocked<FederatedInstanceService>;
|
||||||
redisClient = app.get<Redis>(DI.redis) as jest.Mocked<Redis>;
|
redisClient = app.get<Redis>(DI.redis) as jest.Mocked<Redis>;
|
||||||
httpRequestService = app.get<HttpRequestService>(HttpRequestService) as jest.Mocked<HttpRequestService>;
|
httpRequestService = app.get<HttpRequestService>(HttpRequestService) as jest.Mocked<HttpRequestService>;
|
||||||
|
@ -74,11 +75,12 @@ describe('FetchInstanceMetadataService', () => {
|
||||||
test('Lock and update', async () => {
|
test('Lock and update', async () => {
|
||||||
redisClient.set = mockRedis();
|
redisClient.set = mockRedis();
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
federatedInstanceService.fetch.mockReturnValue({ infoUpdatedAt: { getTime: () => { return now - 10 * 1000 * 60 * 60 * 24; } } });
|
federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => { return now - 10 * 1000 * 60 * 60 * 24; } } } as any);
|
||||||
httpRequestService.getJson.mockImplementation(() => { throw Error(); });
|
httpRequestService.getJson.mockImplementation(() => { throw Error(); });
|
||||||
const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
|
const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
|
||||||
const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
|
const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
|
||||||
await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' });
|
|
||||||
|
await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any);
|
||||||
expect(tryLockSpy).toHaveBeenCalledTimes(1);
|
expect(tryLockSpy).toHaveBeenCalledTimes(1);
|
||||||
expect(unlockSpy).toHaveBeenCalledTimes(1);
|
expect(unlockSpy).toHaveBeenCalledTimes(1);
|
||||||
expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(1);
|
expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(1);
|
||||||
|
@ -88,11 +90,12 @@ describe('FetchInstanceMetadataService', () => {
|
||||||
test('Lock and don\'t update', async () => {
|
test('Lock and don\'t update', async () => {
|
||||||
redisClient.set = mockRedis();
|
redisClient.set = mockRedis();
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
federatedInstanceService.fetch.mockReturnValue({ infoUpdatedAt: { getTime: () => now } });
|
federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => now } } as any);
|
||||||
httpRequestService.getJson.mockImplementation(() => { throw Error(); });
|
httpRequestService.getJson.mockImplementation(() => { throw Error(); });
|
||||||
const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
|
const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
|
||||||
const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
|
const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
|
||||||
await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' });
|
|
||||||
|
await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any);
|
||||||
expect(tryLockSpy).toHaveBeenCalledTimes(1);
|
expect(tryLockSpy).toHaveBeenCalledTimes(1);
|
||||||
expect(unlockSpy).toHaveBeenCalledTimes(1);
|
expect(unlockSpy).toHaveBeenCalledTimes(1);
|
||||||
expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(1);
|
expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(1);
|
||||||
|
@ -101,15 +104,33 @@ describe('FetchInstanceMetadataService', () => {
|
||||||
|
|
||||||
test('Do nothing when lock not acquired', async () => {
|
test('Do nothing when lock not acquired', async () => {
|
||||||
redisClient.set = mockRedis();
|
redisClient.set = mockRedis();
|
||||||
federatedInstanceService.fetch.mockReturnValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } });
|
const now = Date.now();
|
||||||
|
federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } } as any);
|
||||||
httpRequestService.getJson.mockImplementation(() => { throw Error(); });
|
httpRequestService.getJson.mockImplementation(() => { throw Error(); });
|
||||||
|
await fetchInstanceMetadataService.tryLock('example.com');
|
||||||
const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
|
const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
|
||||||
const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
|
const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
|
||||||
await fetchInstanceMetadataService.tryLock('example.com');
|
|
||||||
await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' });
|
await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any);
|
||||||
expect(tryLockSpy).toHaveBeenCalledTimes(2);
|
expect(tryLockSpy).toHaveBeenCalledTimes(1);
|
||||||
expect(unlockSpy).toHaveBeenCalledTimes(0);
|
expect(unlockSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(0);
|
expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(0);
|
||||||
expect(httpRequestService.getJson).toHaveBeenCalledTimes(0);
|
expect(httpRequestService.getJson).toHaveBeenCalledTimes(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Do when lock not acquired but forced', async () => {
|
||||||
|
redisClient.set = mockRedis();
|
||||||
|
const now = Date.now();
|
||||||
|
federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } } as any);
|
||||||
|
httpRequestService.getJson.mockImplementation(() => { throw Error(); });
|
||||||
|
await fetchInstanceMetadataService.tryLock('example.com');
|
||||||
|
const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
|
||||||
|
const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
|
||||||
|
|
||||||
|
await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any, true);
|
||||||
|
expect(tryLockSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(unlockSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(0);
|
||||||
|
expect(httpRequestService.getJson).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -228,11 +228,14 @@ describe('RoleService', () => {
|
||||||
},
|
},
|
||||||
target: 'conditional',
|
target: 'conditional',
|
||||||
condFormula: {
|
condFormula: {
|
||||||
|
id: '232a4221-9816-49a6-a967-ae0fac52ec5e',
|
||||||
type: 'and',
|
type: 'and',
|
||||||
values: [{
|
values: [{
|
||||||
|
id: '2a37ef43-2d93-4c4d-87f6-f2fdb7d9b530',
|
||||||
type: 'followersMoreThanOrEq',
|
type: 'followersMoreThanOrEq',
|
||||||
value: 10,
|
value: 10,
|
||||||
}, {
|
}, {
|
||||||
|
id: '1bd67839-b126-4f92-bad0-4e285dab453b',
|
||||||
type: 'createdMoreThan',
|
type: 'createdMoreThan',
|
||||||
sec: 60 * 60 * 24 * 7,
|
sec: 60 * 60 * 24 * 7,
|
||||||
}],
|
}],
|
||||||
|
|
|
@ -9,11 +9,10 @@ import { basename, isAbsolute } from 'node:path';
|
||||||
import { randomUUID } from 'node:crypto';
|
import { randomUUID } from 'node:crypto';
|
||||||
import { inspect } from 'node:util';
|
import { inspect } from 'node:util';
|
||||||
import WebSocket, { ClientOptions } from 'ws';
|
import WebSocket, { ClientOptions } from 'ws';
|
||||||
import fetch, { File, RequestInit } from 'node-fetch';
|
import fetch, { File, RequestInit, type Headers } from 'node-fetch';
|
||||||
import { DataSource } from 'typeorm';
|
import { DataSource } from 'typeorm';
|
||||||
import { JSDOM } from 'jsdom';
|
import { JSDOM } from 'jsdom';
|
||||||
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
||||||
import { Packed } from '@/misc/json-schema.js';
|
|
||||||
import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/validator.js';
|
import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/validator.js';
|
||||||
import { entities } from '../src/postgres.js';
|
import { entities } from '../src/postgres.js';
|
||||||
import { loadConfig } from '../src/config.js';
|
import { loadConfig } from '../src/config.js';
|
||||||
|
@ -21,7 +20,7 @@ import type * as misskey from 'misskey-js';
|
||||||
|
|
||||||
export { server as startServer, jobQueue as startJobQueue } from '@/boot/common.js';
|
export { server as startServer, jobQueue as startJobQueue } from '@/boot/common.js';
|
||||||
|
|
||||||
interface UserToken {
|
export interface UserToken {
|
||||||
token: string;
|
token: string;
|
||||||
bearer?: boolean;
|
bearer?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -35,20 +34,15 @@ export const cookie = (me: UserToken): string => {
|
||||||
return `token=${me.token};`;
|
return `token=${me.token};`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const api = async (endpoint: string, params: any, me?: UserToken) => {
|
export type ApiRequest<E extends keyof misskey.Endpoints, P extends misskey.Endpoints[E]['req'] = misskey.Endpoints[E]['req']> = {
|
||||||
const normalized = endpoint.replace(/^\//, '');
|
endpoint: E,
|
||||||
return await request(`api/${normalized}`, params, me);
|
parameters: P,
|
||||||
};
|
|
||||||
|
|
||||||
export type ApiRequest = {
|
|
||||||
endpoint: string,
|
|
||||||
parameters: object,
|
|
||||||
user: UserToken | undefined,
|
user: UserToken | undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const successfulApiCall = async <T, >(request: ApiRequest, assertion: {
|
export const successfulApiCall = async <E extends keyof misskey.Endpoints, P extends misskey.Endpoints[E]['req']>(request: ApiRequest<E, P>, assertion: {
|
||||||
status?: number,
|
status?: number,
|
||||||
} = {}): Promise<T> => {
|
} = {}): Promise<misskey.api.SwitchCaseResponseType<E, P>> => {
|
||||||
const { endpoint, parameters, user } = request;
|
const { endpoint, parameters, user } = request;
|
||||||
const res = await api(endpoint, parameters, user);
|
const res = await api(endpoint, parameters, user);
|
||||||
const status = assertion.status ?? (res.body == null ? 204 : 200);
|
const status = assertion.status ?? (res.body == null ? 204 : 200);
|
||||||
|
@ -56,7 +50,7 @@ export const successfulApiCall = async <T, >(request: ApiRequest, assertion: {
|
||||||
return res.body;
|
return res.body;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const failedApiCall = async <T, >(request: ApiRequest, assertion: {
|
export const failedApiCall = async <T, E extends keyof misskey.Endpoints, P extends misskey.Endpoints[E]['req']>(request: ApiRequest<E, P>, assertion: {
|
||||||
status: number,
|
status: number,
|
||||||
code: string,
|
code: string,
|
||||||
id: string
|
id: string
|
||||||
|
@ -70,7 +64,7 @@ export const failedApiCall = async <T, >(request: ApiRequest, assertion: {
|
||||||
return res.body;
|
return res.body;
|
||||||
};
|
};
|
||||||
|
|
||||||
const request = async (path: string, params: any, me?: UserToken): Promise<{
|
export const api = async <E extends keyof misskey.Endpoints>(path: E, params: misskey.Endpoints[E]['req'], me?: UserToken): Promise<{
|
||||||
status: number,
|
status: number,
|
||||||
headers: Headers,
|
headers: Headers,
|
||||||
body: any
|
body: any
|
||||||
|
@ -86,7 +80,7 @@ const request = async (path: string, params: any, me?: UserToken): Promise<{
|
||||||
bodyAuth.i = me.token;
|
bodyAuth.i = me.token;
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await relativeFetch(path, {
|
const res = await relativeFetch(`api/${path}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers,
|
headers,
|
||||||
body: JSON.stringify(Object.assign(bodyAuth, params)),
|
body: JSON.stringify(Object.assign(bodyAuth, params)),
|
||||||
|
@ -141,7 +135,7 @@ export const signup = async (params?: Partial<misskey.Endpoints['signup']['req']
|
||||||
return res.body;
|
return res.body;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const post = async (user: UserToken, params?: misskey.Endpoints['notes/create']['req']): Promise<misskey.entities.Note> => {
|
export const post = async (user: UserToken, params: misskey.Endpoints['notes/create']['req']): Promise<misskey.entities.Note> => {
|
||||||
const q = params;
|
const q = params;
|
||||||
|
|
||||||
const res = await api('notes/create', q, user);
|
const res = await api('notes/create', q, user);
|
||||||
|
@ -159,8 +153,8 @@ export const createAppToken = async (user: UserToken, permissions: (typeof missk
|
||||||
};
|
};
|
||||||
|
|
||||||
// 非公開ノートをAPI越しに見たときのノート NoteEntityService.ts
|
// 非公開ノートをAPI越しに見たときのノート NoteEntityService.ts
|
||||||
export const hiddenNote = (note: any): any => {
|
export const hiddenNote = (note: misskey.entities.Note): misskey.entities.Note => {
|
||||||
const temp = {
|
const temp: misskey.entities.Note = {
|
||||||
...note,
|
...note,
|
||||||
fileIds: [],
|
fileIds: [],
|
||||||
files: [],
|
files: [],
|
||||||
|
@ -173,21 +167,22 @@ export const hiddenNote = (note: any): any => {
|
||||||
return temp;
|
return temp;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const react = async (user: UserToken, note: any, reaction: string): Promise<any> => {
|
export const react = async (user: UserToken, note: misskey.entities.Note, reaction: string): Promise<void> => {
|
||||||
await api('notes/reactions/create', {
|
await api('notes/reactions/create', {
|
||||||
noteId: note.id,
|
noteId: note.id,
|
||||||
reaction: reaction,
|
reaction: reaction,
|
||||||
}, user);
|
}, user);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const userList = async (user: UserToken, userList: any = {}): Promise<any> => {
|
export const userList = async (user: UserToken, userList: Partial<misskey.entities.UserList> = {}): Promise<misskey.entities.UserList> => {
|
||||||
const res = await api('users/lists/create', {
|
const res = await api('users/lists/create', {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
|
...userList,
|
||||||
}, user);
|
}, user);
|
||||||
return res.body;
|
return res.body;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const page = async (user: UserToken, page: any = {}): Promise<any> => {
|
export const page = async (user: UserToken, page: Partial<misskey.entities.Page> = {}): Promise<misskey.entities.Page> => {
|
||||||
const res = await api('pages/create', {
|
const res = await api('pages/create', {
|
||||||
alignCenter: false,
|
alignCenter: false,
|
||||||
content: [
|
content: [
|
||||||
|
@ -198,7 +193,7 @@ export const page = async (user: UserToken, page: any = {}): Promise<any> => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
eyeCatchingImageId: null,
|
eyeCatchingImageId: null,
|
||||||
font: 'sans-serif',
|
font: 'sans-serif' as any,
|
||||||
hideTitleWhenPinned: false,
|
hideTitleWhenPinned: false,
|
||||||
name: '1678594845072',
|
name: '1678594845072',
|
||||||
script: '',
|
script: '',
|
||||||
|
@ -210,7 +205,7 @@ export const page = async (user: UserToken, page: any = {}): Promise<any> => {
|
||||||
return res.body;
|
return res.body;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const play = async (user: UserToken, play: any = {}): Promise<any> => {
|
export const play = async (user: UserToken, play: Partial<misskey.entities.Flash> = {}): Promise<misskey.entities.Flash> => {
|
||||||
const res = await api('flash/create', {
|
const res = await api('flash/create', {
|
||||||
permissions: [],
|
permissions: [],
|
||||||
script: 'test',
|
script: 'test',
|
||||||
|
@ -221,7 +216,7 @@ export const play = async (user: UserToken, play: any = {}): Promise<any> => {
|
||||||
return res.body;
|
return res.body;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const clip = async (user: UserToken, clip: any = {}): Promise<any> => {
|
export const clip = async (user: UserToken, clip: Partial<misskey.entities.Clip> = {}): Promise<misskey.entities.Clip> => {
|
||||||
const res = await api('clips/create', {
|
const res = await api('clips/create', {
|
||||||
description: null,
|
description: null,
|
||||||
isPublic: true,
|
isPublic: true,
|
||||||
|
@ -231,18 +226,18 @@ export const clip = async (user: UserToken, clip: any = {}): Promise<any> => {
|
||||||
return res.body;
|
return res.body;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const galleryPost = async (user: UserToken, channel: any = {}): Promise<any> => {
|
export const galleryPost = async (user: UserToken, galleryPost: Partial<misskey.entities.GalleryPost> = {}): Promise<misskey.entities.GalleryPost> => {
|
||||||
const res = await api('gallery/posts/create', {
|
const res = await api('gallery/posts/create', {
|
||||||
description: null,
|
description: null,
|
||||||
fileIds: [],
|
fileIds: [],
|
||||||
isSensitive: false,
|
isSensitive: false,
|
||||||
title: 'test',
|
title: 'test',
|
||||||
...channel,
|
...galleryPost,
|
||||||
}, user);
|
}, user);
|
||||||
return res.body;
|
return res.body;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const channel = async (user: UserToken, channel: any = {}): Promise<any> => {
|
export const channel = async (user: UserToken, channel: Partial<misskey.entities.Channel> = {}): Promise<misskey.entities.Channel> => {
|
||||||
const res = await api('channels/create', {
|
const res = await api('channels/create', {
|
||||||
bannerId: null,
|
bannerId: null,
|
||||||
description: null,
|
description: null,
|
||||||
|
@ -252,7 +247,7 @@ export const channel = async (user: UserToken, channel: any = {}): Promise<any>
|
||||||
return res.body;
|
return res.body;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const role = async (user: UserToken, role: any = {}, policies: any = {}): Promise<any> => {
|
export const role = async (user: UserToken, role: Partial<misskey.entities.Role> = {}, policies: any = {}): Promise<misskey.entities.Role> => {
|
||||||
const res = await api('admin/roles/create', {
|
const res = await api('admin/roles/create', {
|
||||||
asBadge: false,
|
asBadge: false,
|
||||||
canEditMembersByModerator: false,
|
canEditMembersByModerator: false,
|
||||||
|
@ -260,7 +255,7 @@ export const role = async (user: UserToken, role: any = {}, policies: any = {}):
|
||||||
condFormula: {
|
condFormula: {
|
||||||
id: 'ebef1684-672d-49b6-ad82-1b3ec3784f85',
|
id: 'ebef1684-672d-49b6-ad82-1b3ec3784f85',
|
||||||
type: 'isRemote',
|
type: 'isRemote',
|
||||||
},
|
} as any,
|
||||||
description: '',
|
description: '',
|
||||||
displayOrder: 0,
|
displayOrder: 0,
|
||||||
iconUrl: null,
|
iconUrl: null,
|
||||||
|
@ -298,7 +293,7 @@ interface UploadOptions {
|
||||||
export const uploadFile = async (user?: UserToken, { path, name, blob }: UploadOptions = {}): Promise<{
|
export const uploadFile = async (user?: UserToken, { path, name, blob }: UploadOptions = {}): Promise<{
|
||||||
status: number,
|
status: number,
|
||||||
headers: Headers,
|
headers: Headers,
|
||||||
body: misskey.Endpoints['drive/files/create']['res'] | null
|
body: misskey.entities.DriveFile | null
|
||||||
}> => {
|
}> => {
|
||||||
const absPath = path == null
|
const absPath = path == null
|
||||||
? new URL('resources/Lenna.jpg', import.meta.url)
|
? new URL('resources/Lenna.jpg', import.meta.url)
|
||||||
|
@ -335,14 +330,14 @@ export const uploadFile = async (user?: UserToken, { path, name, blob }: UploadO
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const uploadUrl = async (user: UserToken, url: string): Promise<Packed<'DriveFile'>> => {
|
export const uploadUrl = async (user: UserToken, url: string): Promise<misskey.entities.DriveFile> => {
|
||||||
const marker = Math.random().toString();
|
const marker = Math.random().toString();
|
||||||
|
|
||||||
const catcher = makeStreamCatcher(
|
const catcher = makeStreamCatcher(
|
||||||
user,
|
user,
|
||||||
'main',
|
'main',
|
||||||
(msg) => msg.type === 'urlUploadFinished' && msg.body.marker === marker,
|
(msg) => msg.type === 'urlUploadFinished' && msg.body.marker === marker,
|
||||||
(msg) => msg.body.file as Packed<'DriveFile'>,
|
(msg) => msg.body.file,
|
||||||
60 * 1000,
|
60 * 1000,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
"astring": "1.8.6",
|
"astring": "1.8.6",
|
||||||
"broadcast-channel": "7.0.0",
|
"broadcast-channel": "7.0.0",
|
||||||
"buraha": "0.0.1",
|
"buraha": "0.0.1",
|
||||||
"canvas-confetti": "1.6.1",
|
"canvas-confetti": "1.9.2",
|
||||||
"chart.js": "4.4.2",
|
"chart.js": "4.4.2",
|
||||||
"chartjs-adapter-date-fns": "3.0.0",
|
"chartjs-adapter-date-fns": "3.0.0",
|
||||||
"chartjs-chart-matrix": "2.0.1",
|
"chartjs-chart-matrix": "2.0.1",
|
||||||
|
@ -60,17 +60,17 @@
|
||||||
"rollup": "4.12.0",
|
"rollup": "4.12.0",
|
||||||
"sanitize-html": "2.12.1",
|
"sanitize-html": "2.12.1",
|
||||||
"sass": "1.71.1",
|
"sass": "1.71.1",
|
||||||
"shiki": "1.0.0-beta.3",
|
"shiki": "1.1.7",
|
||||||
"strict-event-emitter-types": "2.0.0",
|
"strict-event-emitter-types": "2.0.0",
|
||||||
"textarea-caret": "3.1.0",
|
"textarea-caret": "3.1.0",
|
||||||
"three": "0.161.0",
|
"three": "0.162.0",
|
||||||
"throttle-debounce": "5.0.0",
|
"throttle-debounce": "5.0.0",
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
"tsc-alias": "1.8.8",
|
"tsc-alias": "1.8.8",
|
||||||
"tsconfig-paths": "4.2.0",
|
"tsconfig-paths": "4.2.0",
|
||||||
"typescript": "5.3.3",
|
"typescript": "5.3.3",
|
||||||
"uuid": "9.0.1",
|
"uuid": "9.0.1",
|
||||||
"v-code-diff": "1.7.2",
|
"v-code-diff": "1.9.0",
|
||||||
"vite": "5.1.4",
|
"vite": "5.1.4",
|
||||||
"vue": "3.4.21",
|
"vue": "3.4.21",
|
||||||
"vuedraggable": "next"
|
"vuedraggable": "next"
|
||||||
|
|
|
@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</ol>
|
</ol>
|
||||||
<ol v-else-if="emojis.length > 0" ref="suggests" :class="$style.list">
|
<ol v-else-if="emojis.length > 0" ref="suggests" :class="$style.list">
|
||||||
<li v-for="emoji in emojis" :key="emoji.emoji" :class="$style.item" tabindex="-1" @click="complete(type, emoji.emoji)" @keydown="onKeydown">
|
<li v-for="emoji in emojis" :key="emoji.emoji" :class="$style.item" tabindex="-1" @click="complete(type, emoji.emoji)" @keydown="onKeydown">
|
||||||
<MkCustomEmoji v-if="'isCustomEmoji' in emoji && emoji.isCustomEmoji" :name="emoji.emoji" :class="$style.emoji"/>
|
<MkCustomEmoji v-if="'isCustomEmoji' in emoji && emoji.isCustomEmoji" :name="emoji.emoji" :class="$style.emoji" :fallbackToImage="true"/>
|
||||||
<MkEmoji v-else :emoji="emoji.emoji" :class="$style.emoji"/>
|
<MkEmoji v-else :emoji="emoji.emoji" :class="$style.emoji"/>
|
||||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||||
<span v-if="q" :class="$style.emojiName" v-html="sanitizeHtml(emoji.name.replace(q, `<b>${q}</b>`))"></span>
|
<span v-if="q" :class="$style.emojiName" v-html="sanitizeHtml(emoji.name.replace(q, `<b>${q}</b>`))"></span>
|
||||||
|
@ -77,7 +77,7 @@ const emojiDb = computed(() => {
|
||||||
unicodeEmojiDB.push({
|
unicodeEmojiDB.push({
|
||||||
emoji: emoji,
|
emoji: emoji,
|
||||||
name: k,
|
name: k,
|
||||||
aliasOf: getEmojiName(emoji)!,
|
aliasOf: getEmojiName(emoji),
|
||||||
url: char2path(emoji),
|
url: char2path(emoji),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
@pointerenter="computeButtonTitle"
|
@pointerenter="computeButtonTitle"
|
||||||
@click="emit('chosen', emoji, $event)"
|
@click="emit('chosen', emoji, $event)"
|
||||||
>
|
>
|
||||||
<MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/>
|
<MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true" :fallbackToImage="true"/>
|
||||||
<MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/>
|
<MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -87,7 +87,7 @@ const shown = ref(!!props.initialShown);
|
||||||
function computeButtonTitle(ev: MouseEvent): void {
|
function computeButtonTitle(ev: MouseEvent): void {
|
||||||
const elm = ev.target as HTMLElement;
|
const elm = ev.target as HTMLElement;
|
||||||
const emoji = elm.dataset.emoji as string;
|
const emoji = elm.dataset.emoji as string;
|
||||||
elm.title = getEmojiName(emoji) ?? emoji;
|
elm.title = getEmojiName(emoji);
|
||||||
}
|
}
|
||||||
|
|
||||||
function nestedChosen(emoji: any, ev: MouseEvent) {
|
function nestedChosen(emoji: any, ev: MouseEvent) {
|
||||||
|
|
|
@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@click="chosen(emoji, $event)"
|
@click="chosen(emoji, $event)"
|
||||||
>
|
>
|
||||||
<MkCustomEmoji class="emoji" :name="emoji.name"/>
|
<MkCustomEmoji class="emoji" :name="emoji.name" :fallbackToImage="true"/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="searchResultUnicode.length > 0" class="body">
|
<div v-if="searchResultUnicode.length > 0" class="body">
|
||||||
|
@ -353,7 +353,7 @@ watch(q, () => {
|
||||||
searchResultUnicode.value = Array.from(searchUnicode());
|
searchResultUnicode.value = Array.from(searchUnicode());
|
||||||
});
|
});
|
||||||
|
|
||||||
function canReact(emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef): boolean {
|
function canReact(emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef | string): boolean {
|
||||||
return !props.targetNote || checkReactionPermissions($i!, props.targetNote, emoji);
|
return !props.targetNote || checkReactionPermissions($i!, props.targetNote, emoji);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,11 +378,14 @@ function getKey(emoji: string | Misskey.entities.EmojiSimple | UnicodeEmojiDef):
|
||||||
return typeof emoji === 'string' ? emoji : 'char' in emoji ? emoji.char : `:${emoji.name}:`;
|
return typeof emoji === 'string' ? emoji : 'char' in emoji ? emoji.char : `:${emoji.name}:`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDef(emoji: string) {
|
function getDef(emoji: string): string | Misskey.entities.EmojiSimple | UnicodeEmojiDef {
|
||||||
if (emoji.includes(':')) {
|
if (emoji.includes(':')) {
|
||||||
return customEmojisMap.get(emoji.replace(/:/g, ''))!;
|
// カスタム絵文字が存在する場合はその情報を持つオブジェクトを返し、
|
||||||
|
// サーバの管理画面から削除された等で情報が見つからない場合は名前の文字列をそのまま返しておく(undefinedを返すとエラーになるため)
|
||||||
|
const name = emoji.replaceAll(':', '');
|
||||||
|
return customEmojisMap.get(name) ?? emoji;
|
||||||
} else {
|
} else {
|
||||||
return getUnicodeEmoji(emoji)!;
|
return getUnicodeEmoji(emoji);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,7 +393,7 @@ function getDef(emoji: string) {
|
||||||
function computeButtonTitle(ev: MouseEvent): void {
|
function computeButtonTitle(ev: MouseEvent): void {
|
||||||
const elm = ev.target as HTMLElement;
|
const elm = ev.target as HTMLElement;
|
||||||
const emoji = elm.dataset.emoji as string;
|
const emoji = elm.dataset.emoji as string;
|
||||||
elm.title = getEmojiName(emoji) ?? emoji;
|
elm.title = getEmojiName(emoji);
|
||||||
}
|
}
|
||||||
|
|
||||||
function chosen(emoji: any, ev?: MouseEvent) {
|
function chosen(emoji: any, ev?: MouseEvent) {
|
||||||
|
|
|
@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkCustomEmoji v-if="reaction[0] === ':'" ref="elRef" :name="reaction" :normal="true" :noStyle="noStyle" :url="emojiUrl"/>
|
<MkCustomEmoji v-if="reaction[0] === ':'" ref="elRef" :name="reaction" :normal="true" :noStyle="noStyle" :url="emojiUrl" :fallbackToImage="true"/>
|
||||||
<MkEmoji v-else ref="elRef" :emoji="reaction" :normal="true" :noStyle="noStyle"/>
|
<MkEmoji v-else ref="elRef" :emoji="reaction" :normal="true" :noStyle="noStyle"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ function getReactionName(reaction: string): string {
|
||||||
if (trimLocal.startsWith(':')) {
|
if (trimLocal.startsWith(':')) {
|
||||||
return trimLocal;
|
return trimLocal;
|
||||||
}
|
}
|
||||||
return getEmojiName(reaction) ?? reaction;
|
return getEmojiName(reaction);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -48,3 +48,18 @@ export const Missing = {
|
||||||
name: Default.args.name,
|
name: Default.args.name,
|
||||||
},
|
},
|
||||||
} satisfies StoryObj<typeof MkCustomEmoji>;
|
} satisfies StoryObj<typeof MkCustomEmoji>;
|
||||||
|
export const ErrorToText = {
|
||||||
|
...Default,
|
||||||
|
args: {
|
||||||
|
url: 'https://example.com/404',
|
||||||
|
name: Default.args.name,
|
||||||
|
},
|
||||||
|
} satisfies StoryObj<typeof MkCustomEmoji>;
|
||||||
|
export const ErrorToImage = {
|
||||||
|
...Default,
|
||||||
|
args: {
|
||||||
|
url: 'https://example.com/404',
|
||||||
|
name: Default.args.name,
|
||||||
|
fallbackToImage: true,
|
||||||
|
},
|
||||||
|
} satisfies StoryObj<typeof MkCustomEmoji>;
|
||||||
|
|
|
@ -4,7 +4,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<span v-if="errored">:{{ customEmojiName }}:</span>
|
<img
|
||||||
|
v-if="errored && fallbackToImage"
|
||||||
|
:class="[$style.root, { [$style.normal]: normal, [$style.noStyle]: noStyle }]"
|
||||||
|
src="/client-assets/dummy.png"
|
||||||
|
:title="alt"
|
||||||
|
/>
|
||||||
|
<span v-else-if="errored">:{{ customEmojiName }}:</span>
|
||||||
<img
|
<img
|
||||||
v-else
|
v-else
|
||||||
:class="[$style.root, { [$style.normal]: normal, [$style.noStyle]: noStyle }]"
|
:class="[$style.root, { [$style.normal]: normal, [$style.noStyle]: noStyle }]"
|
||||||
|
@ -39,6 +45,7 @@ const props = defineProps<{
|
||||||
useOriginalSize?: boolean;
|
useOriginalSize?: boolean;
|
||||||
menu?: boolean;
|
menu?: boolean;
|
||||||
menuReaction?: boolean;
|
menuReaction?: boolean;
|
||||||
|
fallbackToImage?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const react = inject<((name: string) => void) | null>('react', null);
|
const react = inject<((name: string) => void) | null>('react', null);
|
||||||
|
|
|
@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, inject } from 'vue';
|
import { computed, inject } from 'vue';
|
||||||
import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@/scripts/emoji-base.js';
|
import { char2fluentEmojiFilePath, char2twemojiFilePath } from '@/scripts/emoji-base.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
import { colorizeEmoji, getEmojiName } from '@/scripts/emojilist.js';
|
import { colorizeEmoji, getEmojiName } from '@/scripts/emojilist.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
|
@ -34,8 +34,7 @@ const colorizedNativeEmoji = computed(() => colorizeEmoji(props.emoji));
|
||||||
|
|
||||||
// Searching from an array with 2000 items for every emoji felt like too energy-consuming, so I decided to do it lazily on pointerenter
|
// Searching from an array with 2000 items for every emoji felt like too energy-consuming, so I decided to do it lazily on pointerenter
|
||||||
function computeTitle(event: PointerEvent): void {
|
function computeTitle(event: PointerEvent): void {
|
||||||
const title = getEmojiName(props.emoji as string) ?? props.emoji as string;
|
(event.target as HTMLElement).title = getEmojiName(props.emoji);
|
||||||
(event.target as HTMLElement).title = title;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClick(ev: MouseEvent) {
|
function onClick(ev: MouseEvent) {
|
||||||
|
|
|
@ -407,6 +407,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
|
||||||
useOriginalSize: scale >= 2.5,
|
useOriginalSize: scale >= 2.5,
|
||||||
menu: props.enableEmojiMenu,
|
menu: props.enableEmojiMenu,
|
||||||
menuReaction: props.enableEmojiMenuReaction,
|
menuReaction: props.enableEmojiMenuReaction,
|
||||||
|
fallbackToImage: false,
|
||||||
})];
|
})];
|
||||||
} else {
|
} else {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
|
|
|
@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div class="misskey">Misskey</div>
|
<div class="misskey">Misskey</div>
|
||||||
<div class="version">v{{ version }}</div>
|
<div class="version">v{{ version }}</div>
|
||||||
<span v-for="emoji in easterEggEmojis" :key="emoji.id" class="emoji" :data-physics-x="emoji.left" :data-physics-y="emoji.top" :class="{ _physics_circle_: !emoji.emoji.startsWith(':') }">
|
<span v-for="emoji in easterEggEmojis" :key="emoji.id" class="emoji" :data-physics-x="emoji.left" :data-physics-y="emoji.top" :class="{ _physics_circle_: !emoji.emoji.startsWith(':') }">
|
||||||
<MkCustomEmoji v-if="emoji.emoji[0] === ':'" class="emoji" :name="emoji.emoji" :normal="true" :noStyle="true"/>
|
<MkCustomEmoji v-if="emoji.emoji[0] === ':'" class="emoji" :name="emoji.emoji" :normal="true" :noStyle="true" :fallbackToImage="true"/>
|
||||||
<MkEmoji v-else class="emoji" :emoji="emoji.emoji" :normal="true" :noStyle="true"/>
|
<MkEmoji v-else class="emoji" :emoji="emoji.emoji" :normal="true" :noStyle="true"/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
>
|
>
|
||||||
<template #item="{element}">
|
<template #item="{element}">
|
||||||
<button class="_button" :class="$style.emojisItem" @click="removeReaction(element, $event)">
|
<button class="_button" :class="$style.emojisItem" @click="removeReaction(element, $event)">
|
||||||
<MkCustomEmoji v-if="element[0] === ':'" :name="element" :normal="true"/>
|
<MkCustomEmoji v-if="element[0] === ':'" :name="element" :normal="true" :fallbackToImage="true"/>
|
||||||
<MkEmoji v-else :emoji="element" :normal="true"/>
|
<MkEmoji v-else :emoji="element" :normal="true"/>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
@ -63,7 +63,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
>
|
>
|
||||||
<template #item="{element}">
|
<template #item="{element}">
|
||||||
<button class="_button" :class="$style.emojisItem" @click="removeEmoji(element, $event)">
|
<button class="_button" :class="$style.emojisItem" @click="removeEmoji(element, $event)">
|
||||||
<MkCustomEmoji v-if="element[0] === ':'" :name="element" :normal="true"/>
|
<MkCustomEmoji v-if="element[0] === ':'" :name="element" :normal="true" :fallbackToImage="true"/>
|
||||||
<MkEmoji v-else :emoji="element" :normal="true"/>
|
<MkEmoji v-else :emoji="element" :normal="true"/>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { UnicodeEmojiDef } from './emojilist.js';
|
import { UnicodeEmojiDef } from './emojilist.js';
|
||||||
|
|
||||||
export function checkReactionPermissions(me: Misskey.entities.MeDetailed, note: Misskey.entities.Note, emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef): boolean {
|
export function checkReactionPermissions(me: Misskey.entities.MeDetailed, note: Misskey.entities.Note, emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef | string): boolean {
|
||||||
if ('char' in emoji) return true; // UnicodeEmojiDefなら常にリアクション可能
|
if (typeof emoji === 'string') return true; // UnicodeEmojiDefにも無い絵文字であれば文字列で来る。Unicode絵文字であることには変わりないので常にリアクション可能とする;
|
||||||
|
if ('char' in emoji) return true; // UnicodeEmojiDefなら常にリアクション可能
|
||||||
|
|
||||||
emoji = emoji as Misskey.entities.EmojiSimple;
|
const roleIdsThatCanBeUsedThisEmojiAsReaction = emoji.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [];
|
||||||
const roleIdsThatCanBeUsedThisEmojiAsReaction = emoji.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [];
|
return !(emoji.localOnly && note.user.host !== me.host)
|
||||||
return !(emoji.localOnly && note.user.host !== me.host)
|
|
||||||
&& !(emoji.isSensitive && (note.reactionAcceptance === 'nonSensitiveOnly' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote'))
|
&& !(emoji.isSensitive && (note.reactionAcceptance === 'nonSensitiveOnly' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote'))
|
||||||
&& (roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0 || me.roles.some(role => roleIdsThatCanBeUsedThisEmojiAsReaction.includes(role.id)));
|
&& (roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0 || me.roles.some(role => roleIdsThatCanBeUsedThisEmojiAsReaction.includes(role.id)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,21 +39,29 @@ for (let i = 0; i < emojilist.length; i++) {
|
||||||
|
|
||||||
export const emojiCharByCategory = _charGroupByCategory;
|
export const emojiCharByCategory = _charGroupByCategory;
|
||||||
|
|
||||||
export function getUnicodeEmoji(char: string): UnicodeEmojiDef | null {
|
export function getUnicodeEmoji(char: string): UnicodeEmojiDef | string {
|
||||||
// Colorize it because emojilist.json assumes that
|
// Colorize it because emojilist.json assumes that
|
||||||
return unicodeEmojisMap.get(colorizeEmoji(char)) ?? null;
|
return unicodeEmojisMap.get(colorizeEmoji(char))
|
||||||
|
// カラースタイル絵文字がjsonに無い場合はテキストスタイル絵文字にフォールバックする
|
||||||
|
?? unicodeEmojisMap.get(char)
|
||||||
|
// それでも見つからない場合はそのまま返す(絵文字情報がjsonに無い場合、このフォールバックが無いとレンダリングに失敗する)
|
||||||
|
?? char;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getEmojiName(char: string): string | null {
|
export function getEmojiName(char: string): string {
|
||||||
// Colorize it because emojilist.json assumes that
|
// Colorize it because emojilist.json assumes that
|
||||||
const idx = _indexByChar.get(colorizeEmoji(char));
|
const idx = _indexByChar.get(colorizeEmoji(char)) ?? _indexByChar.get(char);
|
||||||
if (idx == null) {
|
if (idx === undefined) {
|
||||||
return null;
|
// 絵文字情報がjsonに無い場合は名前の取得が出来ないのでそのまま返すしか無い
|
||||||
|
return char;
|
||||||
} else {
|
} else {
|
||||||
return emojilist[idx].name;
|
return emojilist[idx].name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* テキストスタイル絵文字(U+260Eなどの1文字で表現される絵文字)をカラースタイル絵文字に変換します(VS16:U+FE0Fを付与)。
|
||||||
|
*/
|
||||||
export function colorizeEmoji(char: string) {
|
export function colorizeEmoji(char: string) {
|
||||||
return char.length === 1 ? `${char}\uFE0F` : char;
|
return char.length === 1 ? `${char}\uFE0F` : char;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
export type EmojiDef = {
|
export type EmojiDef = {
|
||||||
emoji: string;
|
emoji: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
@ -7,28 +7,28 @@ import { assert, describe, test } from 'vitest';
|
||||||
import { searchEmoji } from '@/scripts/search-emoji.js';
|
import { searchEmoji } from '@/scripts/search-emoji.js';
|
||||||
|
|
||||||
describe('emoji autocomplete', () => {
|
describe('emoji autocomplete', () => {
|
||||||
test('名前の完全一致は名前の前方一致より優先される', async () => {
|
test('名前の完全一致は名前の前方一致より優先される', async () => {
|
||||||
const result = searchEmoji('foooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':foooobaaar:', name: 'foooobaaar' }]);
|
const result = searchEmoji('foooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':foooobaaar:', name: 'foooobaaar' }]);
|
||||||
assert.equal(result[0].emoji, ':foooo:');
|
assert.equal(result[0].emoji, ':foooo:');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('名前の前方一致は名前の部分一致より優先される', async () => {
|
test('名前の前方一致は名前の部分一致より優先される', async () => {
|
||||||
const result = searchEmoji('baaa', [{ emoji: ':baaar:', name: 'baaar' }, { emoji: ':foooobaaar:', name: 'foooobaaar' }]);
|
const result = searchEmoji('baaa', [{ emoji: ':baaar:', name: 'baaar' }, { emoji: ':foooobaaar:', name: 'foooobaaar' }]);
|
||||||
assert.equal(result[0].emoji, ':baaar:');
|
assert.equal(result[0].emoji, ':baaar:');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('名前の完全一致はタグの完全一致より優先される', async () => {
|
test('名前の完全一致はタグの完全一致より優先される', async () => {
|
||||||
const result = searchEmoji('foooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]);
|
const result = searchEmoji('foooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]);
|
||||||
assert.equal(result[0].emoji, ':foooo:');
|
assert.equal(result[0].emoji, ':foooo:');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('名前の前方一致はタグの前方一致より優先される', async () => {
|
test('名前の前方一致はタグの前方一致より優先される', async () => {
|
||||||
const result = searchEmoji('foo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]);
|
const result = searchEmoji('foo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]);
|
||||||
assert.equal(result[0].emoji, ':foooo:');
|
assert.equal(result[0].emoji, ':foooo:');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('名前の部分一致はタグの部分一致より優先される', async () => {
|
test('名前の部分一致はタグの部分一致より優先される', async () => {
|
||||||
const result = searchEmoji('oooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]);
|
const result = searchEmoji('oooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]);
|
||||||
assert.equal(result[0].emoji, ':foooo:');
|
assert.equal(result[0].emoji, ':foooo:');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"name": "misskey-js",
|
"name": "misskey-js",
|
||||||
"version": "2024.2.0",
|
"version": "2024.3.1",
|
||||||
"description": "Misskey SDK for JavaScript",
|
"description": "Misskey SDK for JavaScript",
|
||||||
"types": "./built/dts/index.d.ts",
|
"types": "./built/dts/index.d.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|
108
pnpm-lock.yaml
108
pnpm-lock.yaml
|
@ -117,14 +117,14 @@ importers:
|
||||||
specifier: 5.0.3
|
specifier: 5.0.3
|
||||||
version: 5.0.3
|
version: 5.0.3
|
||||||
'@nestjs/common':
|
'@nestjs/common':
|
||||||
specifier: 10.2.10
|
specifier: 10.3.3
|
||||||
version: 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
|
version: 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1)
|
||||||
'@nestjs/core':
|
'@nestjs/core':
|
||||||
specifier: 10.2.10
|
specifier: 10.3.3
|
||||||
version: 10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)
|
version: 10.3.3(@nestjs/common@10.3.3)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1)
|
||||||
'@nestjs/testing':
|
'@nestjs/testing':
|
||||||
specifier: 10.2.10
|
specifier: 10.3.3
|
||||||
version: 10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)(@nestjs/platform-express@10.3.3)
|
version: 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(@nestjs/platform-express@10.3.3)
|
||||||
'@peertube/http-signature':
|
'@peertube/http-signature':
|
||||||
specifier: 1.7.0
|
specifier: 1.7.0
|
||||||
version: 1.7.0
|
version: 1.7.0
|
||||||
|
@ -351,8 +351,8 @@ importers:
|
||||||
specifier: 0.1.4
|
specifier: 0.1.4
|
||||||
version: 0.1.4
|
version: 0.1.4
|
||||||
reflect-metadata:
|
reflect-metadata:
|
||||||
specifier: 0.1.14
|
specifier: 0.2.1
|
||||||
version: 0.1.14
|
version: 0.2.1
|
||||||
rename:
|
rename:
|
||||||
specifier: 1.0.4
|
specifier: 1.0.4
|
||||||
version: 1.0.4
|
version: 1.0.4
|
||||||
|
@ -513,7 +513,7 @@ importers:
|
||||||
version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
|
version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
|
||||||
'@nestjs/platform-express':
|
'@nestjs/platform-express':
|
||||||
specifier: 10.3.3
|
specifier: 10.3.3
|
||||||
version: 10.3.3(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)
|
version: 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)
|
||||||
'@simplewebauthn/types':
|
'@simplewebauthn/types':
|
||||||
specifier: 9.0.1
|
specifier: 9.0.1
|
||||||
version: 9.0.1
|
version: 9.0.1
|
||||||
|
@ -725,8 +725,8 @@ importers:
|
||||||
specifier: 0.0.1
|
specifier: 0.0.1
|
||||||
version: 0.0.1
|
version: 0.0.1
|
||||||
canvas-confetti:
|
canvas-confetti:
|
||||||
specifier: 1.6.1
|
specifier: 1.9.2
|
||||||
version: 1.6.1
|
version: 1.9.2
|
||||||
chart.js:
|
chart.js:
|
||||||
specifier: 4.4.2
|
specifier: 4.4.2
|
||||||
version: 4.4.2
|
version: 4.4.2
|
||||||
|
@ -806,8 +806,8 @@ importers:
|
||||||
specifier: 1.71.1
|
specifier: 1.71.1
|
||||||
version: 1.71.1
|
version: 1.71.1
|
||||||
shiki:
|
shiki:
|
||||||
specifier: 1.0.0-beta.3
|
specifier: 1.1.7
|
||||||
version: 1.0.0-beta.3
|
version: 1.1.7
|
||||||
strict-event-emitter-types:
|
strict-event-emitter-types:
|
||||||
specifier: 2.0.0
|
specifier: 2.0.0
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
|
@ -815,8 +815,8 @@ importers:
|
||||||
specifier: 3.1.0
|
specifier: 3.1.0
|
||||||
version: 3.1.0
|
version: 3.1.0
|
||||||
three:
|
three:
|
||||||
specifier: 0.161.0
|
specifier: 0.162.0
|
||||||
version: 0.161.0
|
version: 0.162.0
|
||||||
throttle-debounce:
|
throttle-debounce:
|
||||||
specifier: 5.0.0
|
specifier: 5.0.0
|
||||||
version: 5.0.0
|
version: 5.0.0
|
||||||
|
@ -836,8 +836,8 @@ importers:
|
||||||
specifier: 9.0.1
|
specifier: 9.0.1
|
||||||
version: 9.0.1
|
version: 9.0.1
|
||||||
v-code-diff:
|
v-code-diff:
|
||||||
specifier: 1.7.2
|
specifier: 1.9.0
|
||||||
version: 1.7.2(vue@3.4.21)
|
version: 1.9.0(vue@3.4.21)
|
||||||
vite:
|
vite:
|
||||||
specifier: 5.1.4
|
specifier: 5.1.4
|
||||||
version: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
|
version: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
|
||||||
|
@ -4860,12 +4860,12 @@ packages:
|
||||||
tar-fs: 2.1.1
|
tar-fs: 2.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@nestjs/common@10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1):
|
/@nestjs/common@10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1):
|
||||||
resolution: {integrity: sha512-fwAk931rjW8CNH2Mgwawq/7HWHH1dxkOLdcgs7U52ddLk8CtHXjejm1cbNahewlSbNhvlOl7y1STLHutE6sUqw==}
|
resolution: {integrity: sha512-LAkTe8/CF0uNWM0ecuDwUNTHCi1lVSITmmR4FQ6Ftz1E7ujQCnJ5pMRzd8JRN14vdBkxZZ8VbVF0BDUKoKNxMQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
class-transformer: '*'
|
class-transformer: '*'
|
||||||
class-validator: '*'
|
class-validator: '*'
|
||||||
reflect-metadata: ^0.1.12
|
reflect-metadata: ^0.1.12 || ^0.2.0
|
||||||
rxjs: ^7.1.0
|
rxjs: ^7.1.0
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
class-transformer:
|
class-transformer:
|
||||||
|
@ -4874,20 +4874,20 @@ packages:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
iterare: 1.2.1
|
iterare: 1.2.1
|
||||||
reflect-metadata: 0.1.14
|
reflect-metadata: 0.2.1
|
||||||
rxjs: 7.8.1
|
rxjs: 7.8.1
|
||||||
tslib: 2.6.2
|
tslib: 2.6.2
|
||||||
uid: 2.0.2
|
uid: 2.0.2
|
||||||
|
|
||||||
/@nestjs/core@10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.1.14)(rxjs@7.8.1):
|
/@nestjs/core@10.3.3(@nestjs/common@10.3.3)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1):
|
||||||
resolution: {integrity: sha512-+ckOI6BPi2ZMHikT9MCG4ctHDc4OnjhoIytrn7f2AYMMXI4bnutJhqyQKc30VDka5x3Wq6QAD57pgSP7y+JjJg==}
|
resolution: {integrity: sha512-kxJWggQAPX3RuZx9JVec69eSLaYLNIox2emkZJpfBJ5Qq7cAq7edQIt1r4LGjTKq6kFubNTPsqhWf5y7yFRBPw==}
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@nestjs/common': ^10.0.0
|
'@nestjs/common': ^10.0.0
|
||||||
'@nestjs/microservices': ^10.0.0
|
'@nestjs/microservices': ^10.0.0
|
||||||
'@nestjs/platform-express': ^10.0.0
|
'@nestjs/platform-express': ^10.0.0
|
||||||
'@nestjs/websockets': ^10.0.0
|
'@nestjs/websockets': ^10.0.0
|
||||||
reflect-metadata: ^0.1.12
|
reflect-metadata: ^0.1.12 || ^0.2.0
|
||||||
rxjs: ^7.1.0
|
rxjs: ^7.1.0
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
'@nestjs/microservices':
|
'@nestjs/microservices':
|
||||||
|
@ -4897,27 +4897,27 @@ packages:
|
||||||
'@nestjs/websockets':
|
'@nestjs/websockets':
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
|
'@nestjs/common': 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1)
|
||||||
'@nestjs/platform-express': 10.3.3(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)
|
'@nestjs/platform-express': 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)
|
||||||
'@nuxtjs/opencollective': 0.3.2
|
'@nuxtjs/opencollective': 0.3.2
|
||||||
fast-safe-stringify: 2.1.1
|
fast-safe-stringify: 2.1.1
|
||||||
iterare: 1.2.1
|
iterare: 1.2.1
|
||||||
path-to-regexp: 3.2.0
|
path-to-regexp: 3.2.0
|
||||||
reflect-metadata: 0.1.14
|
reflect-metadata: 0.2.1
|
||||||
rxjs: 7.8.1
|
rxjs: 7.8.1
|
||||||
tslib: 2.6.2
|
tslib: 2.6.2
|
||||||
uid: 2.0.2
|
uid: 2.0.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- encoding
|
- encoding
|
||||||
|
|
||||||
/@nestjs/platform-express@10.3.3(@nestjs/common@10.2.10)(@nestjs/core@10.2.10):
|
/@nestjs/platform-express@10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3):
|
||||||
resolution: {integrity: sha512-GGKSEU48Os7nYFIsUM0nutuFUGn5AbeP8gzFBiBCAtiuJWrXZXpZ58pMBYxAbMf7IrcOZFInHEukjHGAQU0OZw==}
|
resolution: {integrity: sha512-GGKSEU48Os7nYFIsUM0nutuFUGn5AbeP8gzFBiBCAtiuJWrXZXpZ58pMBYxAbMf7IrcOZFInHEukjHGAQU0OZw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@nestjs/common': ^10.0.0
|
'@nestjs/common': ^10.0.0
|
||||||
'@nestjs/core': ^10.0.0
|
'@nestjs/core': ^10.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
|
'@nestjs/common': 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1)
|
||||||
'@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)
|
'@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1)
|
||||||
body-parser: 1.20.2
|
body-parser: 1.20.2
|
||||||
cors: 2.8.5
|
cors: 2.8.5
|
||||||
express: 4.18.2
|
express: 4.18.2
|
||||||
|
@ -4926,8 +4926,8 @@ packages:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
/@nestjs/testing@10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)(@nestjs/platform-express@10.3.3):
|
/@nestjs/testing@10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(@nestjs/platform-express@10.3.3):
|
||||||
resolution: {integrity: sha512-IVLUnPz/+fkBtPATYfqTIP+phN9yjkXejmj+JyhmcfPJZpxBmD1i9VSMqa4u54l37j0xkGPscQ0IXpbhqMYUKw==}
|
resolution: {integrity: sha512-kX20GfjAImL5grd/i69uD/x7sc00BaqGcP2dRG3ilqshQUuy5DOmspLCr3a2C8xmVU7kzK4spT0oTxhe6WcCAA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@nestjs/common': ^10.0.0
|
'@nestjs/common': ^10.0.0
|
||||||
'@nestjs/core': ^10.0.0
|
'@nestjs/core': ^10.0.0
|
||||||
|
@ -4939,9 +4939,9 @@ packages:
|
||||||
'@nestjs/platform-express':
|
'@nestjs/platform-express':
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
|
'@nestjs/common': 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1)
|
||||||
'@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)
|
'@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1)
|
||||||
'@nestjs/platform-express': 10.3.3(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)
|
'@nestjs/platform-express': 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)
|
||||||
tslib: 2.6.2
|
tslib: 2.6.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
@ -5327,8 +5327,8 @@ packages:
|
||||||
string-argv: 0.3.1
|
string-argv: 0.3.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@shikijs/core@1.0.0-beta.3:
|
/@shikijs/core@1.1.7:
|
||||||
resolution: {integrity: sha512-SCwPom2Wn8XxNlEeqdzycU93SKgzYeVsedjqDsgZaz4XiiPpZUzlHt2NAEQTwTnPcHNZapZ6vbkwJ8P11ggL3Q==}
|
resolution: {integrity: sha512-gTYLUIuD1UbZp/11qozD3fWpUTuMqPSf3svDMMrL0UmlGU7D9dPw/V1FonwAorCUJBltaaESxq90jrSjQyGixg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@sideway/address@4.1.4:
|
/@sideway/address@4.1.4:
|
||||||
|
@ -9473,8 +9473,8 @@ packages:
|
||||||
resolution: {integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==}
|
resolution: {integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/canvas-confetti@1.6.1:
|
/canvas-confetti@1.9.2:
|
||||||
resolution: {integrity: sha512-CgGR5DL9+dkne4AwcpvWQc0LIQq43yDIxlwdZcyrq3yklricNfuPHoOSoM6Ya7yCQ+sXmZ2iNV2feiKjVG8C1g==}
|
resolution: {integrity: sha512-6Xi7aHHzKwxZsem4mCKoqP6YwUG3HamaHHAlz1hTNQPCqXhARFpSXnkC9TWlahHY5CG6hSL5XexNjxK8irVErg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/caseless@0.12.0:
|
/caseless@0.12.0:
|
||||||
|
@ -12500,8 +12500,8 @@ packages:
|
||||||
resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==}
|
resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/highlight.js@11.8.0:
|
/highlight.js@11.9.0:
|
||||||
resolution: {integrity: sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==}
|
resolution: {integrity: sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==}
|
||||||
engines: {node: '>=12.0.0'}
|
engines: {node: '>=12.0.0'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
@ -17110,12 +17110,8 @@ packages:
|
||||||
redis-errors: 1.2.0
|
redis-errors: 1.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/reflect-metadata@0.1.14:
|
|
||||||
resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==}
|
|
||||||
|
|
||||||
/reflect-metadata@0.2.1:
|
/reflect-metadata@0.2.1:
|
||||||
resolution: {integrity: sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==}
|
resolution: {integrity: sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==}
|
||||||
dev: false
|
|
||||||
|
|
||||||
/regenerate-unicode-properties@10.1.0:
|
/regenerate-unicode-properties@10.1.0:
|
||||||
resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==}
|
resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==}
|
||||||
|
@ -17669,10 +17665,10 @@ packages:
|
||||||
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
/shiki@1.0.0-beta.3:
|
/shiki@1.1.7:
|
||||||
resolution: {integrity: sha512-z7cHTNSSvwGx2DfeLwjSNLo+HcVxifgNIzLm6Ye52eXcIwNHXT0wHbhy7FDOKSKveuEHBwt9opfj3Hoc8LE1Yg==}
|
resolution: {integrity: sha512-9kUTMjZtcPH3i7vHunA6EraTPpPOITYTdA5uMrvsJRexktqP0s7P3s9HVK80b4pP42FRVe03D7fT3NmJv2yYhw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@shikijs/core': 1.0.0-beta.3
|
'@shikijs/core': 1.1.7
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/side-channel@1.0.4:
|
/side-channel@1.0.4:
|
||||||
|
@ -18507,8 +18503,8 @@ packages:
|
||||||
real-require: 0.2.0
|
real-require: 0.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/three@0.161.0:
|
/three@0.162.0:
|
||||||
resolution: {integrity: sha512-LC28VFtjbOyEu5b93K0bNRLw1rQlMJ85lilKsYj6dgTu+7i17W+JCCEbvrpmNHF1F3NAUqDSWq50UD7w9H2xQw==}
|
resolution: {integrity: sha512-xfCYj4RnlozReCmUd+XQzj6/5OjDNHBy5nT6rVwrOKGENAvpXe2z1jL+DZYaMu4/9pNsjH/4Os/VvS9IrH7IOQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/throttle-debounce@5.0.0:
|
/throttle-debounce@5.0.0:
|
||||||
|
@ -19187,8 +19183,8 @@ packages:
|
||||||
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
|
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
/v-code-diff@1.7.2(vue@3.4.21):
|
/v-code-diff@1.9.0(vue@3.4.21):
|
||||||
resolution: {integrity: sha512-y+q8ZHf8GfphYLhcZbjAKcId/h6vZujS71Ryq5u+dI6Jg4ZLTdLrBNVSzYpHywHSSFFfBMdilm6XvVryEaH4+A==}
|
resolution: {integrity: sha512-alg6krCxFvwTob/rJq+3LzjdIbLb/ni8tS8YmBbI0wckOkbJuN1cShFJ6XEkm82tMgpv5NYEeWLEWhggeV7BDg==}
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@vue/composition-api': ^1.4.9
|
'@vue/composition-api': ^1.4.9
|
||||||
|
@ -19199,9 +19195,9 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
diff: 5.1.0
|
diff: 5.1.0
|
||||||
diff-match-patch: 1.0.5
|
diff-match-patch: 1.0.5
|
||||||
highlight.js: 11.8.0
|
highlight.js: 11.9.0
|
||||||
vue: 3.4.21(typescript@5.3.3)
|
vue: 3.4.21(typescript@5.3.3)
|
||||||
vue-demi: 0.13.11(vue@3.4.21)
|
vue-demi: 0.14.7(vue@3.4.21)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/v8-to-istanbul@9.2.0:
|
/v8-to-istanbul@9.2.0:
|
||||||
|
@ -19456,8 +19452,8 @@ packages:
|
||||||
resolution: {integrity: sha512-6bnLkn8O0JJyiFSIF0EfCogzeqNXpnjJ0vW/SZzNHfe6sPx30lTtTXlE5TFs2qhJlAtDFybStVNpL73cPe3OMQ==}
|
resolution: {integrity: sha512-6bnLkn8O0JJyiFSIF0EfCogzeqNXpnjJ0vW/SZzNHfe6sPx30lTtTXlE5TFs2qhJlAtDFybStVNpL73cPe3OMQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/vue-demi@0.13.11(vue@3.4.21):
|
/vue-demi@0.14.7(vue@3.4.21):
|
||||||
resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==}
|
resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
|
Loading…
Reference in New Issue